{"repo":"zcash/zips","commit":"4699a8d518c4c409db3f486c7917e649369c41c9","commitShort":"4699a8d518","commitUrl":"https://github.com/zcash/zips/commit/4699a8d518c4c409db3f486c7917e649369c41c9","syncedAt":"2026-11-06T16:44:46.926Z","error":null,"prs":[{"number":1446,"title":"Protocol spec: Apply the NU7 ZIPs","author":"daira","createdAt":"2026-11-06T04:04:51Z","updatedAt":"2026-11-06T15:49:26Z","url":"https://github.com/zcash/zips/pull/1446","diffUrl":"https://github.com/zcash/zips/pull/1446.diff","headRef":"nu7-spec","headRepo":"zcash/zips","headSha":"d0e2420ffda6d08d599c15d3e1990454505afbbc","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Applies the NU7 ZIPs to the protocol specification as a new `nu7` variant, on top of #1444, makes it the default, and releases it as version 2026.9.0. Each ZIP is its own commit, in the order the ZIPs depend on each other. Where the specification's wording or structure differs from a ZIP's descripti","zipRefs":[207,218,234,235,237,259,2008],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"protocol/Makefile","status":"modified","additions":30,"deletions":14,"patch":"@@ -55,10 +55,10 @@ protocol: $(PDFDIR)/protocol.pdf\n protocol-dark: $(PDFDIR)/protocol-dark.pdf\n \n all-pdfs: .Makefile.uptodate\n-\t$(MAKE) $(PDFDIR)/protocol.pdf $(PDFDIR)/protocol-dark.pdf $(PDFDIR)/nu6_2.pdf $(PDFDIR)/nu6_1.pdf $(PDFDIR)/nu6.pdf $(PDFDIR)/nu5.pdf $(PDFDIR)/canopy.pdf $(PDFDIR)/heartwood.pdf $(PDFDIR)/blossom.pdf $(PDFDIR)/sapling.pdf\n+\t$(MAKE) $(PDFDIR)/protocol.pdf $(PDFDIR)/protocol-dark.pdf $(PDFDIR)/nu6_3.pdf $(PDFDIR)/nu6_2.pdf $(PDFDIR)/nu6_1.pdf $(PDFDIR)/nu6.pdf $(PDFDIR)/nu5.pdf $(PDFDIR)/canopy.pdf $(PDFDIR)/heartwood.pdf $(PDFDIR)/blossom.pdf $(PDFDIR)/sapling.pdf\n \n all-specs: .Makefile.uptodate\n-\t$(MAKE) nu6_3 nu6_3-dark nu6_2 nu6_1 nu6 nu5 canopy heartwood blossom sapling\n+\t$(MAKE) nu7 nu7-dark nu6_3 nu6_2 nu6_1 nu6 nu5 canopy heartwood blossom sapling\n \n discard:\n \tgit checkout -- \"$(PDFDIR)/*.pdf\"\n@@ -91,14 +91,17 @@ $(PDFDIR)/nu6_1.pdf: protocol.tex zcash.bib jubjub.png key_components_sapling.pn\n $(PDFDIR)/nu6_2.pdf: protocol.tex zcash.bib jubjub.png key_components_sapling.png key_components_orchard.png incremental_merkle.png\n \t$(MAKE) nu6_2\n \n+$(PDFDIR)/nu6_3.pdf: protocol.tex zcash.bib jubjub.png key_components_sapling.png key_components_orchard.png incremental_merkle.png\n+\t$(MAKE) nu6_3\n+\n # protocol.pdf and protocol-dark.pdf are symbolic links to the PDFs for the most recent\n # settled network upgrade, so that links to that upgrade's own PDF remain valid when\n # a later upgrade becomes the default.\n $(PDFDIR)/protocol.pdf: protocol.tex zcash.bib jubjub.png key_components_sapling.png key_components_orchard.png incremental_merkle.png\n-\t$(MAKE) nu6_3\n+\t$(MAKE) nu7\n \n $(PDFDIR)/protocol-dark.pdf: protocol.tex zcash.bib jubjub.png key_components_sapling.png key_components_orchard_dark.png incremental_merkle_dark.png\n-\t$(MAKE) nu6_3-dark\n+\t$(MAKE) nu7-dark\n \n .PHONY: auxsapling sapling\n auxsapling:\n@@ -216,21 +219,34 @@ nu6_3:\n \t$(MAKE) auxnu6_3\n \tmkdir -p $(PDFDIR)\n \tmv -f aux/nu6_3.pdf $(PDFDIR)\n-\tln -sf nu6_3.pdf $(PDFDIR)/protocol.pdf\n \n-.PHONY: auxnu6_3-dark nu6_3-dark\n-auxnu6_3-dark:\n-\tprintf '\\\\toggletrue{darkmode}\\\\toggletrue{isnusixthree}\\n\\\\renewcommand{\\\\docversion}{Version %s [\\\\NUSixThreeSpec]}\\n' \"$$(git describe --tags --abbrev=6 |sed 's/-1-g.*//;s/-auto//')\" |tee protocol.ver\n+.PHONY: auxnu7 nu7\n+auxnu7:\n+\tprintf '\\\\toggletrue{isnuseven}\\n\\\\renewcommand{\\\\docversion}{Version %s [\\\\NUSevenSpec]}\\n' \"$$(git describe --tags --abbrev=6 |sed 's/-1-g.*//;s/-auto//')\" |tee protocol.ver\n+\tmkdir -p aux\n+\trm -f aux/nu7.*\n+\tcp mymakeindex.sh aux\n+\t$(LATEXMK) -jobname=nu7 -auxdir=aux -outdir=aux $(EXTRAOPT) protocol $(NOCRUFT)\n+\n+nu7:\n+\t$(MAKE) auxnu7\n+\tmkdir -p $(PDFDIR)\n+\tmv -f aux/nu7.pdf $(PDFDIR)\n+\tln -sf nu7.pdf $(PDFDIR)/protocol.pdf\n+\n+.PHONY: auxnu7-dark nu7-dark\n+auxnu7-dark:\n+\tprintf '\\\\toggletrue{darkmode}\\\\toggletrue{isnuseven}\\n\\\\renewcommand{\\\\docversion}{Version %s [\\\\NUSevenSpec]}\\n' \"$$(git describe --tags --abbrev=6 |sed 's/-1-g.*//;s/-auto//')\" |tee protocol.ver\n \tmkdir -p aux\n-\trm -f aux/nu6_3-dark.*\n+\trm -f aux/nu7-dark.*\n \tcp mymakeindex.sh aux\n-\t$(LATEXMK) -jobname=nu6_3-dark -auxdir=aux -outdir=aux $(EXTRAOPT) protocol $(NOCRUFT)\n+\t$(LATEXMK) -jobname=nu7-dark -auxdir=aux -outdir=aux $(EXTRAOPT) protocol $(NOCRUFT)\n \n-nu6_3-dark:\n-\t$(MAKE) auxnu6_3-dark\n+nu7-dark:\n+\t$(MAKE) auxnu7-dark\n \tmkdir -p $(PDFDIR)\n-\tmv -f aux/nu6_3-dark.pdf $(PDFDIR)\n-\tln -sf nu6_3-dark.pdf $(PDFDIR)/protocol-dark.pdf\n+\tmv -f aux/nu7-dark.pdf $(PDFDIR)\n+\tln -sf nu7-dark.pdf $(PDFDIR)/protocol-dark.pdf\n \n .PHONY: clean\n clean:"},{"filename":"protocol/README.rst","status":"modified","additions":5,"deletions":5,"patch":"@@ -24,13 +24,13 @@ Building\n \n Use:\n \n-* ``make nu6_3`` to make the specification for NU6.3 (``nu6_3.pdf``), which is\n+* ``make nu7`` to make the specification for NU7 (``nu7.pdf``), which is\n   the default (``protocol.pdf`` is a link to it);\n-* ``make nu6_3-dark`` to make its dark mode version (``nu6_3-dark.pdf``, linked\n+* ``make nu7-dark`` to make its dark mode version (``nu7-dark.pdf``, linked\n   as ``protocol-dark.pdf``);\n-* ``make nu6_2``, ``make nu6_1``, ``make nu6``, ``make nu5``, ``make canopy``,\n-  ``make heartwood``, ``make blossom``, or ``make sapling`` to make the\n-  specification for an earlier network upgrade (``nu6_2.pdf``, etc.);\n+* ``make nu6_3``, ``make nu6_2``, ``make nu6_1``, ``make nu6``, ``make nu5``,\n+  ``make canopy``, ``make heartwood``, ``make blossom``, or ``make sapling`` to\n+  make the specification for an earlier network upgrade (``nu6_3.pdf``, etc.);\n * ``make all-specs`` to make all of the above;\n * ``make linkcheck`` (in the root of the repo) to build everything and also\n   perform link checking. This will access the network."},{"filename":"protocol/protocol.tex","status":"modified","additions":450,"deletions":58,"patch":"@@ -84,6 +84,7 @@\n \\newcommand{\\NUSixOneSpec}{NU6.1}\n \\newcommand{\\NUSixTwoSpec}{NU6.2}\n \\newcommand{\\NUSixThreeSpec}{NU6.3}\n+\\newcommand{\\NUSevenSpec}{NU7}\n \\newtoggle{issapling}\n \\togglefalse{issapling}\n \\newtoggle{isblossom}\n@@ -647,6 +648,7 @@\n   \\colorlet{pink}{magenta!70}\n   \\definecolor{burgundy}{RGB}{210,20,80}\n   \\definecolor{brown}{RGB}{160,50,50}\n+  \\definecolor{teal}{RGB}{40,190,190}\n   \\colorlet{warningred}{BrickRed}\n   \\colorlet{cream}{yellow!20}\n \n@@ -662,6 +664,7 @@\n   \\colorlet{pink}{magenta!75}\n   \\definecolor{burgundy}{RGB}{200,18,75}\n   \\colorlet{brown}{Sepia}\n+  \\definecolor{teal}{RGB}{0,135,135}\n   \\colorlet{warningred}{BrickRed}\n   \\colorlet{cream}{yellow!20}\n \n@@ -691,10 +694,21 @@\n \\newcommand{\\nusixtwocolorname}{\\nusixtwocolor}\n \\newcommand{\\nusixthreecolor}{blue}\n \\newcommand{\\nusixthreecolorname}{bright blue}\n+\\newcommand{\\nusevencolor}{teal}\n+\\newcommand{\\nusevencolorname}{\\nusevencolor}\n \\newcommand{\\labelcolor}{cream}\n \n \\iftoggle{isnuseven}{\n-  \\errmessage{NU7 is not defined yet}\n+  \\iftoggle{darkmode}{\n+    \\providecommand{\\baseurl}{https://zips.z.cash/protocol/nu7-dark.pdf}\n+  }{\n+    \\providecommand{\\baseurl}{https://zips.z.cash/protocol/nu7.pdf}\n+  }\n+  \\toggletrue{isnusixthree}\n+  \\newcommand{\\setnuseven}{\\color{\\nusevencolor}}\n+  \\newcommand{\\nuseven}[1]{\\texorpdfstring{{\\setnuseven{#1}}}{#1}}\n+  \\newcommand{\\notnuseven}[1]{}\n+  \\newcommand{\\notbeforenuseven}[1]{#1}\n } {\n   \\newcommand{\\setnuseven}{}\n   \\newcommand{\\nuseven}[1]{}\n@@ -703,11 +717,7 @@\n }\n \n \\iftoggle{isnusixthree}{\n-  \\iftoggle{darkmode}{\n-    \\providecommand{\\baseurl}{https://zips.z.cash/protocol/nu6_3-dark.pdf}\n-  }{\n-    \\providecommand{\\baseurl}{https://zips.z.cash/protocol/nu6_3.pdf}\n-  }\n+  \\providecommand{\\baseurl}{https://zips.z.cash/protocol/nu6_3.pdf}\n   \\toggletrue{isnusixtwo}\n   \\newcommand{\\setnusixthree}{\\color{\\nusixthreecolor}}\n   \\newcommand{\\nusixthree}[1]{\\texorpdfstring{{\\setnusixthree{#1}}}{#1}}\n@@ -2228,9 +2238,12 @@\n \\newcommand{\\fsRecipientIndex}{\\fs\\mathsf{.RecipientIndex}}\n \\newcommand{\\fsNumRecipients}{\\fs\\mathsf{.NumRecipients}}\n \\newcommand{\\fsRedeemScriptHash}{\\fs\\mathsf{.RedeemScriptHash}}\n+\\newcommand{\\fsPubKeyHash}{\\fs\\mathsf{.PubKeyHash}}\n \\newcommand{\\SlowStartInterval}{\\mathsf{SlowStartInterval}}\n \\newcommand{\\SlowStartShift}{\\mathsf{SlowStartShift}}\n \\newcommand{\\SlowStartRate}{\\mathsf{SlowStartRate}}\n+\\newcommand{\\BlossomAdjustment}{\\mathsf{BlossomAdjustment}}\n+\\newcommand{\\NUSevenAdjustment}{\\mathsf{NU7Adjustment}}\n \\newcommand{\\PreBlossomHalvingInterval}{\\mathsf{\\notbeforeblossom{PreBlossom}HalvingInterval}}\n \\newcommand{\\PostBlossomHalvingInterval}{\\mathsf{PostBlossomHalvingInterval}}\n \\newcommand{\\MaxBlockSubsidy}{\\mathsf{MaxBlockSubsidy}}\n@@ -2239,6 +2252,7 @@\n \\newcommand{\\FoundersFraction}{\\mathsf{FoundersFraction}}\n \\newcommand{\\FSRecipientChangeInterval}{\\mathsf{FSRecipientChangeInterval}}\n \\newcommand{\\FSRecipientPeriod}{\\mathsf{FSRecipientPeriod}}\n+\\newcommand{\\PreNUSevenEquivalentHeight}{\\mathsf{PreNU7EquivalentHeight}}\n \\newcommand{\\BlockHeight}{\\mathsf{height}}\n \\newcommand{\\FounderAddressAdjustedHeight}{\\mathsf{FounderAddressAdjustedHeight}}\n \\newcommand{\\FoundersRewardLastBlockHeight}{\\mathsf{FoundersRewardLastBlockHeight}}\n@@ -2252,6 +2266,18 @@\n \\newcommand{\\heightBytes}{\\mathsf{heightBytes}}\n \\newcommand{\\ChainValuePoolBalance}[1]{\\ensuremath{\\mathsf{ChainValuePoolBalance}^{#1}}}\n \\newcommand{\\IssuedSupply}{\\mathsf{IssuedSupply}}\n+\\newcommand{\\TransactionFees}{\\mathsf{TransactionFees}}\n+\\newcommand{\\NSMFeeContribution}{\\mathsf{NSMFeeContribution}}\n+\\newcommand{\\MinerFees}{\\mathsf{MinerFees}}\n+\\newcommand{\\ScheduledBlockSubsidy}{\\mathsf{ScheduledBlockSubsidy}}\n+\\newcommand{\\AdditionalBlockSubsidy}{\\mathsf{AdditionalBlockSubsidy}}\n+\\newcommand{\\HalvingIntervalFunc}{\\mathsf{HalvingInterval}}\n+\\newcommand{\\BlockSubsidyFraction}{\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}}\n+\\newcommand{\\LNTwoScaled}{\\mathsf{LN2\\_SCALED}}\n+\\newcommand{\\InitialNSMValueBalance}{\\mathsf{INITIAL\\_NSM\\_VALUE\\_BALANCE}}\n+\\newcommand{\\NSMReissuanceHeight}{\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}}\n+\\newcommand{\\NSMValueBalance}{\\mathsf{NSMValueBalance}}\n+\\newcommand{\\NSMValueRemoved}{\\mathsf{NSMValueRemoved}}\n \n \\newcommand{\\blockSubsidy}{\\term{block subsidy}}\n \\newcommand{\\minerSubsidy}{\\term{miner subsidy}}\n@@ -2272,6 +2298,9 @@\n \n \\newcommand{\\BlossomActivationHeight}{\\mathsf{BlossomActivationHeight}}\n \\newcommand{\\IsBlossomActivated}{\\mathsf{IsBlossomActivated}}\n+\\newcommand{\\NUSevenActivationHeight}{\\mathsf{NU7ActivationHeight}}\n+\\newcommand{\\IsNUSevenActivated}{\\mathsf{IsNU7Activated}}\n+\\newcommand{\\PostNUSevenHalvingInterval}{\\mathsf{PostNU7HalvingInterval}}\n \\newcommand{\\CanopyActivationHeight}{\\mathsf{CanopyActivationHeight}}\n \\newcommand{\\ZIPTwoOneTwoGracePeriod}{\\mathsf{ZIP212GracePeriod}}\n \\newcommand{\\ZIPTwoSevenOneActivationHeight}{\\mathsf{ZIP271ActivationHeight}}\n@@ -2281,13 +2310,22 @@\n \n \\newcommand{\\PoWLimit}{\\mathsf{PoWLimit}}\n \\newcommand{\\PoWAveragingWindow}{\\mathsf{PoWAveragingWindow}}\n+\\newcommand{\\PreNUSevenPoWAveragingWindow}{\\mathsf{PreNU7PoWAveragingWindow}}\n+\\newcommand{\\PostNUSevenPoWAveragingWindow}{\\mathsf{PostNU7PoWAveragingWindow}}\n+\\newcommand{\\GlobalShieldedBudget}{\\mathsf{GlobalShieldedBudget}}\n+\\newcommand{\\OrchardProtocolBlockActionLimit}{\\mathsf{OrchardProtocolBlockActionLimit}}\n+\\newcommand{\\SaplingBlockIOLimit}{\\mathsf{SaplingBlockIOLimit}}\n+\\newcommand{\\SproutBlockJoinSplitLimit}{\\mathsf{SproutBlockJoinSplitLimit}}\n \\newcommand{\\PoWMedianBlockSpan}{\\mathsf{PoWMedianBlockSpan}}\n \\newcommand{\\PoWMaxAdjustDown}{\\mathsf{PoWMaxAdjustDown}}\n \\newcommand{\\PoWMaxAdjustUp}{\\mathsf{PoWMaxAdjustUp}}\n \\newcommand{\\PoWDampingFactor}{\\mathsf{PoWDampingFactor}}\n \\newcommand{\\PreBlossomPoWTargetSpacing}{\\mathsf{\\notbeforeblossom{PreBlossom}PoWTargetSpacing}}\n \\newcommand{\\PostBlossomPoWTargetSpacing}{\\mathsf{PostBlossomPoWTargetSpacing}}\n \\newcommand{\\BlossomPoWTargetSpacingRatio}{\\mathsf{BlossomPoWTargetSpacingRatio}}\n+\\newcommand{\\PostNUSevenPoWTargetSpacing}{\\mathsf{PostNU7PoWTargetSpacing}}\n+\\newcommand{\\NUSevenPoWTargetSpacingRatio}{\\mathsf{NU7PoWTargetSpacingRatio}}\n+\\newcommand{\\CombinedPoWTargetSpacingRatio}{\\mathsf{CombinedPoWTargetSpacingRatio}}\n \\newcommand{\\PoWTargetSpacingFunc}{\\mathsf{PoWTargetSpacing}}\n \\newcommand{\\MeanTarget}{\\mathsf{MeanTarget}}\n \\newcommand{\\MedianTime}{\\mathsf{MedianTime}}\n@@ -2523,6 +2561,7 @@\n \\newcommand{\\saplingtype}[1]{\\textcolor{\\saplingcolor}{\\type{#1}}}\n \\newcommand{\\nufivetype}[1]{\\textcolor{\\nufivecolor}{\\type{#1}}}\n \\newcommand{\\nusixthreetype}[1]{\\textcolor{\\nusixthreecolor}{\\type{#1}}}\n+\\newcommand{\\nuseventype}[1]{\\textcolor{\\nusevencolor}{\\type{#1}}}\n \\newcommand{\\typecodeField}{\\mathtt{typecode}}\n \\newcommand{\\lengthField}{\\mathtt{length}}\n \\newcommand{\\addrField}{\\mathtt{addr}}\n@@ -3069,7 +3108,8 @@\n \\notnusixone{\\nusix{\\thisspecdefines{\\NUOverwinter, \\NUSapling, \\NUBlossom, \\NUHeartwood, \\NUCanopy, \\NUFive, and \\NUSix}}}%\n \\notnusixtwo{\\nusixone{\\thisspecdefines{\\NUOverwinter, \\NUSapling, \\NUBlossom, \\NUHeartwood, \\NUCanopy, \\NUFive, \\NUSix, and \\NUSixOne}}}%\n \\notnusixthree{\\nusixtwo{\\thisspecdefines{\\NUOverwinter, \\NUSapling, \\NUBlossom, \\NUHeartwood, \\NUCanopy, \\NUFive, \\NUSix, \\NUSixOne, and \\NUSixTwo}}}%\n-\\nusixthree{\\thisspecdefines{\\NUOverwinter, \\NUSapling, \\NUBlossom, \\NUHeartwood, \\NUCanopy, \\NUFive, \\NUSix, \\NUSixOne, \\NUSixTwo, and \\NUSixThree}}%\n+\\notnuseven{\\nusixthree{\\thisspecdefines{\\NUOverwinter, \\NUSapling, \\NUBlossom, \\NUHeartwood, \\NUCanopy, \\NUFive, \\NUSix, \\NUSixOne, \\NUSixTwo, and \\NUSixThree}}}%\n+\\nuseven{\\thisspecdefines{\\NUOverwinter, \\NUSapling, \\NUBlossom, \\NUHeartwood, \\NUCanopy, \\NUFive, \\NUSix, \\NUSixOne, \\NUSixTwo, \\NUSixThree, and \\NUSeven}}%\n It is a work in progress. Protocol differences from \\Zerocash and \\Bitcoin are also explained.\n \n \\vspace{2ex}\n@@ -3151,6 +3191,9 @@\n \\notbeforenusixthree{Changes specific to the \\NUSixThree upgrade following \\NUSixTwo\n are highlighted in \\nusixthree{\\nusixthreecolorname}.}\n \n+\\notbeforenuseven{Changes specific to the \\NUSeven upgrade following \\NUSixThree\n+are highlighted in \\nuseven{\\nusevencolorname}.}\n+\n All of these are also changes from \\Zerocash.\n \n The name ``Sprout'' refers to the \\Zcash protocol prior to \\NUSapling\n@@ -3537,10 +3580,12 @@\n     $\\PRFOutputLengthNfSapling$,} $\\NoteCommitRandLengthSprout$, $\\RandomSeedLength$, $\\AuthPrivateLength$,\n     $\\NoteUniquePreRandLength$,\\sapling{ $\\SpendingKeyLength$, $\\DiversifierLength$,\\nufive{ $\\DiversifierKeyLength$,}\\nusixthree{ $\\QuantumSpendingKeyLength$, $\\QuantumIntermediateKeyLength$,}\n     $\\InViewingKeyLength{Sapling}$, $\\OutViewingKeyLength$, $\\ScalarLength{Sapling}$,\\nufive{ $\\ScalarLength{Orchard}$, $\\BaseLength{Orchard}$,}}\n-    $\\MAXMONEY$,\\blossom{ $\\BlossomActivationHeight$,}\\strut\\canopy{ $\\CanopyActivationHeight$, $\\ZIPTwoOneTwoGracePeriod$,}\n+    $\\MAXMONEY$,\\blossom{ $\\BlossomActivationHeight$,}\\strut\\canopy{ $\\CanopyActivationHeight$, $\\ZIPTwoOneTwoGracePeriod$,}\\nuseven{ $\\NUSevenActivationHeight$,}\n     $\\SlowStartInterval$, $\\PreBlossomHalvingInterval$, $\\MaxBlockSubsidy$, $\\NumFounderAddresses$,\n-    $\\PoWLimit$, $\\PoWAveragingWindow$, $\\PoWMedianBlockSpan$, $\\PoWDampingFactor$,\n-    \\notblossom{and }$\\PreBlossomPoWTargetSpacing$\\blossom{, and $\\PostBlossomPoWTargetSpacing$}.\n+    $\\PoWLimit$, \\notnuseven{$\\PoWAveragingWindow$}\\nuseven{$\\PreNUSevenPoWAveragingWindow$, $\\PostNUSevenPoWAveragingWindow$}, $\\PoWMedianBlockSpan$, $\\PoWDampingFactor$,\n+    \\notblossom{and }$\\PreBlossomPoWTargetSpacing$\\blossom{, \\notnuseven{and }$\\PostBlossomPoWTargetSpacing$}\\nuseven{, $\\PostNUSevenPoWTargetSpacing$,\n+    $\\GlobalShieldedBudget$, $\\OrchardProtocolBlockActionLimit$, $\\SaplingBlockIOLimit$, $\\SproutBlockJoinSplitLimit$,\n+    $\\LNTwoScaled$, $\\InitialNSMValueBalance$, and $\\NSMReissuanceHeight$}.\n   \\end{flushleft}\n \\end{formulae}\n \n@@ -4111,13 +4156,17 @@\n \\vspace{-2ex}\n \n As in \\Bitcoin, the remaining value in the \\transparentTxValuePool of a non-coinbase \\transaction\n-is available to miners as a \\defining{\\transactionFee}. That is, the sum of those values for non-coinbase \\transactions\n-in each \\block is treated as an implicit input to the \\transparentTxValuePool balance of the\n-\\block's \\coinbaseTransaction (in addition to the implicit input created by issuance).\n+is a \\defining{\\transactionFee}. The sum of those values for non-coinbase \\transactions in each\n+\\block\\nuseven{ (pre-\\NUSeven), or $\\MinerFees(\\BlockHeight)$ for the \\block at \\blockHeight\n+$\\BlockHeight$ (\\NUSeven onward),} is available to miners: it is treated as an implicit input to\n+the \\transparentTxValuePool balance of the \\block's \\coinbaseTransaction (in addition to the\n+implicit input created by issuance).\\nuseven{ $\\MinerFees$ is defined in \\shortcrossref{subsidies}.}\n \n The remaining value in the \\transparentTxValuePool of \\coinbaseTransactions\\nusix{ in \\blocks\n-prior to \\NUSix} is destroyed. \\nusix{From \\NUSix, this remaining value is required to be zero;\n-that is, all of the available balance \\MUST be consumed by outputs of the \\coinbaseTransaction.}\n+prior to \\NUSix} is left unclaimed and is not added to any \\chainValuePool. \\nusix{From \\NUSix, this\n+remaining value is required to be zero; that is, all of the available balance \\MUST be consumed by\n+outputs of the \\coinbaseTransaction.} \\nusevenonward{This unclaimed value is included in\n+$\\InitialNSMValueBalance$, and so is reissued as specified in \\shortcrossref{subsidies}.}\n \n \\vspace{-1ex}\n \\consensusrule{\n@@ -4412,21 +4461,26 @@\n \n \\canopyonward{The \\blockSubsidy is composed of a \\minerSubsidy and a series of \\defining{\\fundingStreams}.}\n \n-As in \\Bitcoin, the miner of a \\block also receives \\defining{\\transactionFees}.\n+As in \\Bitcoin, the miner of a \\block also receives \\defining{\\transactionFees}\\nuseven{\n+(pre-\\NUSeven), or $\\MinerFees(\\BlockHeight)$ (\\NUSeven onward)}.\n \n The calculations of the \\blockSubsidy, \\minerSubsidy, \\notcanopy{and }\\foundersReward\\canopy{, and \\fundingStreams}\n depend on the \\blockHeight, as defined in \\crossref{blockchain}.\n \n \\crossref{subsidies}\\notbeforenusixone{\\\\} describes these calculations.\n \n+\\nusevenonward{From $\\NSMReissuanceHeight$, the \\blockSubsidy also includes value reissued from the\n+NSM Value Balance defined in \\crossref{chainvaluepoolbalances}, and so depends on the NSM Value\n+Balance after the preceding \\block as well as on the \\blockHeight.}\n+\n \n \\lsubsection{Coinbase Transactions}{coinbasetransactions}\n \n A \\transaction that has a single \\transparentInput with a null \\prevoutField{} field, is\n called a \\defining{\\coinbaseTransaction}. Every \\block has a single \\coinbaseTransaction\n as the first \\transaction in the \\block. The purpose of this \\coinbaseTransaction is to\n-collect and spend any \\minerSubsidy, and \\transactionFees paid by other \\transactions included\n-in the \\block.\n+collect and spend any \\minerSubsidy, and the \\transactionFees paid by other \\transactions\n+included in the \\block\\nuseven{ (pre-\\NUSeven) or $\\MinerFees(\\BlockHeight)$ (\\NUSeven onward)}.\n \n \\precanopy{\\crossref{foundersreward} specifies that the \\coinbaseTransaction \\MUST also pay\n the \\foundersReward.}\n@@ -4458,6 +4512,8 @@\n \n \\Mainnet \\NUSixThree \\activationBlock: $\\mathtt{00000000001a8b54bbde4e8996373417a6b33ee2bd984bcf02882e5d8128d761}$\n \n+\\Mainnet \\NUSeven \\activationBlock: $\\mathtt{0000000000246a2b1fecb3f465770894d146f6b7044e46b13ffb65e57e4ccd4a}$\n+\n \\introlist\n There is also a public test \\network called \\defining{\\Testnet}. It supports a \\TAZ token which is\n intended to have no monetary value. By convention, \\Testnet normally activates \\networkUpgrades (as described\n@@ -4469,6 +4525,8 @@\n \n \\Testnet \\NUSixThree \\activationBlock: $\\mathtt{000003943e1a2c60c17b69ed4d448ea9296019a247f1134b507917941957fccd}$\n \n+\\Testnet \\NUSeven \\activationBlock: $\\mathtt{000089ba27100beede16d64b34e3d1b626b428cb7ee9fe6dcfdc217ce24e78af}$\n+\n We call the smallest units of currency (on either \\network) \\zatoshi.\\footnote{\\definingquotedterm{tazoshi}\n may be used for the smallest units of currency on Testnet, but it is usually more convenient to use a\n \\network-independent term.}\n@@ -7132,6 +7190,8 @@\n         as defined in \\cite{ZIP-257}.}\n   \\nusixthreeonlyitem{All \\transactions \\MUST use the \\NUSixThree \\consensusBranchID \\hexint{37A5165B}\n         as defined in \\cite{ZIP-258}.}\n+  \\nusevenonlyitem{All \\transactions \\MUST use the \\NUSeven \\consensusBranchID \\hexint{77190AD9}\n+        as defined in \\cite{ZIP-259}.}\n \\end{consensusrules}\n \n \n@@ -7863,6 +7923,47 @@\n created as a result of accepting a \\block, then all nodes \\MUST reject the block as invalid.}\n } %nusixthree\n \n+\\nuseven{\n+\\vspace{1.5ex}\n+Let $\\InitialNSMValueBalance$ and $\\NUSevenActivationHeight$ be as defined in \\crossref{constants},\n+and let $\\NSMFeeContribution$ and $\\AdditionalBlockSubsidy$ be as defined in \\shortcrossref{subsidies}.\n+\n+\\cite{ZIP-237} specifies a Network Sustainability Mechanism that tracks value removed from\n+circulation and reissues it through the \\blockSubsidy. The value removed from circulation in the\n+\\block at \\blockHeight $\\BlockHeight$ is $\\NSMValueRemoved(\\BlockHeight) := \\NSMFeeContribution(\\BlockHeight)$,\n+the part of the \\transactionFees specified by \\cite{ZIP-235}, which is the only removal mechanism\n+deployed in \\NUSeven.\n+\n+The \\defining{NSM Value Balance} of a \\blockChain up to and including \\blockHeight $\\BlockHeight$\n+is given by:\n+\n+\\begin{formulae}\n+  \\item $\\NSMValueBalance(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n+          0,&\\caseif \\BlockHeight < \\NUSevenActivationHeight - 1 \\\\[0.6ex]\n+          \\InitialNSMValueBalance,&\\caseif \\BlockHeight = \\NUSevenActivationHeight - 1 \\\\[0.6ex]\n+          \\NSMValueBalance(\\BlockHeight - 1) - \\AdditionalBlockSubsidy(\\BlockHeight) \\\\[-0.4ex]\n+          \\hspace{1.5em} +\\, \\NSMValueRemoved(\\BlockHeight),&\\caseotherwise\n+        \\end{cases}$\n+\\end{formulae}\n+\n+\\vspace{-2ex}\n+\\consensusrule{\\nusevenonward{If $\\NSMValueBalance(\\BlockHeight)$ would become negative in the \\blockChain\n+created as a result of accepting a \\block at \\blockHeight $\\BlockHeight$, then all nodes \\MUST reject\n+the block as invalid.}}\n+\n+\\begin{pnotes}\n+  \\item The NSM Value Balance is tracked as consensus state alongside the \\chainValuePools. It is\n+        not a spendable \\chainValuePool and is not included in $\\IssuedSupply(\\BlockHeight)$: it\n+        counts \\ZEC (or \\TAZ) that is not in circulation. Reissuance moves value from the NSM Value\n+        Balance into the \\totalIssuedSupply through the \\blockSubsidy.\n+  \\item As \\cite{ZIP-2003} clarifies, the \\SproutChainValuePool balance at \\NUSeven activation\n+        remains issued, and so is included in $\\IssuedSupply(\\BlockHeight)$ for the purposes of\n+        \\cite{ZIP-237}.\n+\\end{pnotes}\n+} %nuseven\n+\n \\vspace{2ex}\n The \\defining{\\totalIssuedSupply} of a \\blockChain at \\blockHeight $\\BlockHeight$ is given\n by the function:\n@@ -9290,6 +9391,12 @@\n         \\end{cases}$\n   \\item $\\ZIPTwoOneTwoGracePeriod \\typecolon \\Nat := 32256$\n } %canopy\n+\\nuseven{\n+  \\item $\\NUSevenActivationHeight \\typecolon \\Nat := \\begin{cases}\n+          3543405,&\\squash\\text{for \\Mainnet} \\\\\n+          4465026,&\\squash\\text{for \\Testnet}\n+        \\end{cases}$ \\cite{ZIP-259}\n+} %nuseven\n   \\item $\\SlowStartInterval \\typecolon \\Nat := 20000$\n   \\item $\\PreBlossomHalvingInterval \\typecolon \\Nat := 840000$\n   \\item $\\MaxBlockSubsidy \\typecolon \\Nat := 1.25 \\smult 10^9$ (\\zatoshi)\n@@ -9299,7 +9406,13 @@\n           2^{243} - 1,&\\squash\\text{for \\Mainnet} \\\\\n           2^{251} - 1,&\\squash\\text{for \\Testnet}\n         \\end{cases}$\n+\\notnuseven{\n   \\item $\\PoWAveragingWindow \\typecolon \\Nat := 17$\n+} %notnuseven\n+\\nuseven{\n+  \\item $\\PreNUSevenPoWAveragingWindow \\typecolon \\Nat := 17$\n+  \\item $\\PostNUSevenPoWAveragingWindow \\typecolon \\Nat := 102$\n+} %nuseven\n   \\item $\\PoWMedianBlockSpan \\typecolon \\Nat := 11$\n   \\item $\\PoWMaxAdjustDown \\typecolon \\Rat := \\frac{32}{100}$\n   \\item $\\PoWMaxAdjustUp \\typecolon \\Rat := \\frac{16}{100}$\n@@ -9308,8 +9421,34 @@\n \\blossom{\n   \\item $\\PostBlossomPoWTargetSpacing \\typecolon \\Nat := 75$ (seconds).\n } %blossom\n+\\nuseven{\n+  \\item $\\PostNUSevenPoWTargetSpacing \\typecolon \\Nat := 25$ (seconds).\n+  \\item $\\GlobalShieldedBudget \\typecolon \\Nat := 330$\n+  \\item $\\OrchardProtocolBlockActionLimit \\typecolon \\Nat := 330$\n+  \\item $\\SaplingBlockIOLimit \\typecolon \\Nat := 300$\n+  \\item $\\SproutBlockJoinSplitLimit \\typecolon \\Nat := 0$\n+  \\item $\\LNTwoScaled \\typecolon \\Nat := 6931680000$\n+  \\item $\\InitialNSMValueBalance \\typecolon \\range{0}{\\MAXMONEY} := \\begin{cases}\n+          36858445520,&\\squash\\text{for \\Mainnet} \\\\\n+          55768414957,&\\squash\\text{for \\Testnet}\n+        \\end{cases}$ (\\zatoshi)\n+  \\item $\\NSMReissuanceHeight \\typecolon \\Nat := \\begin{cases}\n+          8939664,&\\squash\\text{for \\Mainnet} \\\\\n+          7305222,&\\squash\\text{for \\Testnet}\n+        \\end{cases}$ \\cite{ZIP-259}\n+} %nuseven\n \\end{formulae}\n \n+\\nuseven{\n+\\vspace{-1.5ex}\n+\\pnote{$\\InitialNSMValueBalance$ is the \\blockSubsidy and \\transactionFees left unclaimed by\n+\\coinbaseTransactions before \\NUSix, which \\cite{ZIP-237} derives for each \\network from the\n+scheduled issuance and the \\totalIssuedSupply at the last pre-\\NUSix \\blockHeight.\n+$\\NSMReissuanceHeight$ is the height at which reissuance begins. \\cite{ZIP-237} specifies how\n+it is calculated from the scheduled issuance curve, and \\cite{ZIP-259} gives the resulting value\n+for each \\network.}\n+} %nuseven\n+\n \n \\introsection\n \\lsubsection{Concrete Cryptographic Schemes}{concreteschemes}\n@@ -13548,11 +13687,16 @@\n and rationale is given in \\cite{ZODL-Ironwood}. Specifications relating to wallet support and\n migration to the \\IronwoodPool are in \\cite{ZIP-318} and \\cite{ZIP-326}.}\n \n+\\nuseven{An eleventh upgrade, called \\defining{\\NUSeven}, activated on \\Mainnet on 6~November, 2026\n+at \\blockHeight $3543405$ \\cite{ZIP-259}. Its specifications are described in this document,\n+\\cite{ZIP-259}, \\cite{ZIP-218}, \\cite{ZIP-235}, \\cite{ZIP-237}, \\cite{ZIP-2003}, and \\cite{ZIP-2008},\n+with updates to \\cite{ZIP-207} and \\cite{ZIP-214}.}\n+\n \\introlist\n \\vspace{1.5ex}\n This section summarizes the strategy for upgrading from the Sprout epoch to subsequent epochs\n of the protocol (\\NUOverwinter, \\NUSapling, \\NUBlossom, \\NUHeartwood, \\NUCanopy, \\NUFive, \\NUSix,\n-\\NUSixOne, \\NUSixTwo, and \\NUSixThree), and for future upgrades.\n+\\NUSixOne, \\NUSixTwo, \\NUSixThree, and \\NUSeven), and for future upgrades.\n \n \\defining{The \\networkUpgrade mechanism is described in \\cite{ZIP-200}.}\n \n@@ -13932,7 +14076,8 @@\n         be $\\hexint{03C48270}$.}\n   \\saplingprenufiveitem{The \\transactionVersionNumber{} \\MUST be $4$.}\n   \\nufiveprenusixthreeitem{The \\transactionVersionNumber \\MUST be $4$ or $5$.}\n-  \\nusixthreeonwarditem{The \\transactionVersionNumber \\MUST be $4$ or $5$ or $6$.}\n+  \\nusixthreeonlyitem{The \\transactionVersionNumber \\MUST be $4$ or $5$ or $6$.}\n+  \\nusevenonwarditem{The \\transactionVersionNumber \\MUST be $5$ or $6$.}\n   \\saplingonwarditem{If the \\transactionVersionNumber{} is $4$ then the \\versionGroupID \\MUST be $\\hexint{892F2085}$.}\n   \\nufiveonwarditem{If the \\transactionVersionNumber{} is $5$ then the \\versionGroupID \\MUST be $\\hexint{26A7270A}$.}\n   \\nusixthreeonwarditem{If the \\transactionVersionNumber{} is $6$ then the \\versionGroupID \\MUST be $\\hexint{D884B698}$.}\n@@ -14015,14 +14160,16 @@\n         \\end{itemize}}\n         \\vspace{-1ex}\n   \\item \\nusix{Let $\\totalDeferredOutput$\\nusixone{ and $\\totalDeferredInput$} be as defined in \\shortcrossref{subsidies}.}\n+        \\nuseven{Let $\\MinerFees$ be as defined in \\shortcrossref{subsidies}.}\n         For the \\block at \\blockHeight $\\mathsf{height}$:\n         \\begin{itemize}\n           \\item define the \\totalOutputValue of its \\coinbaseTransaction to be the total value\n                 in \\zatoshi of its \\transparentOutputs\\heartwood{, minus $\\vBalance{\\SaplingPoolId}$}\\nufive{,\n                 minus $\\vBalance{\\OrchardPoolId}$}\\nusix{, plus $\\totalDeferredOutput(\\mathsf{height})$};\n           \\item define the \\totalInputValue of its \\coinbaseTransaction to be the value in \\zatoshi\n-                of the \\blockSubsidy, plus the \\transactionFees paid by \\transactions in the\n-                \\block\\nusixone{, plus $\\totalDeferredInput(\\mathsf{height})$}.\n+                of the \\blockSubsidy, plus \\prenuseven{the \\transactionFees paid by \\transactions in the\n+                \\block}\\nuseven{ or }\\nusevenonward{$\\MinerFees(\\mathsf{height})$}\\nusixone{, plus\n+                $\\totalDeferredInput(\\mathsf{height})$}.\n         \\end{itemize}\n         \\vspace{-1ex}\n         \\prenusix{The \\totalOutputValue of a \\coinbaseTransaction \\MUSTNOT be greater than its \\totalInputValue.}\n@@ -14558,6 +14705,18 @@\n         $653606$ or greater on \\Testnet, $\\nTimeField$ \\MUST be less than or equal to\n         the \\medianTimePast of that \\block plus $90 \\mult 60$ seconds.\n   \\item The size of a \\block \\MUST be less than or equal to $2000000$ bytes.\n+  \\nusevenonwarditem{Let $\\nActionsField{Orchard}$, $\\nActionsField{Ironwood}$, $\\nSpendsSapling$,\n+        $\\nOutputsSapling$, and $\\nJoinSplit$ be the fields of a \\transaction specified in\n+        \\crossref{txnencoding}, each taken as $0$ for a \\transaction version that does not have\n+        the field. Summed over the \\transactions in the \\block:\n+        \\begin{itemize}\n+          \\item $\\nActionsField{Orchard}$ \\MUSTNOT exceed $\\OrchardProtocolBlockActionLimit$;\n+          \\item $\\nActionsField{Ironwood}$ \\MUSTNOT exceed $\\OrchardProtocolBlockActionLimit$;\n+          \\item $\\nSpendsSapling + \\nOutputsSapling$ \\MUSTNOT exceed $\\SaplingBlockIOLimit$;\n+          \\item $\\nJoinSplit$ \\MUSTNOT exceed $\\SproutBlockJoinSplitLimit$; and\n+          \\item $\\nActionsField{Orchard} + \\nActionsField{Ironwood} + \\nSpendsSapling + \\nOutputsSapling + 2 \\mult \\nJoinSplit$\n+                \\MUSTNOT exceed $\\GlobalShieldedBudget$.\n+        \\end{itemize}}\n \\notheartwood{\n   \\saplingonwarditem{$\\hashFinalSaplingRoot$ \\MUST be $\\LEBStoOSPOf{256}{\\rt{\\SaplingPoolId}}$ where\n         $\\rt{\\SaplingPoolId}$ is the \\merkleRoot of the \\SaplingPoolAdj \\noteCommitmentTree for the final\n@@ -14847,10 +15006,14 @@\n the desired \\blockTargetSpacing. Unlike \\Bitcoin, the difficulty adjustment occurs\n after every \\block.\n \n-\\notbeforeblossom{The constants }$\\PoWLimit$, $\\PreBlossomHalvingInterval$, $\\PoWAveragingWindow$,\n+\\notbeforenuseven{\\begingroup\\raggedright}\n+\\notbeforeblossom{The constants }$\\PoWLimit$, $\\PreBlossomHalvingInterval$, \\notnuseven{$\\PoWAveragingWindow$}\\nuseven{$\\PreNUSevenPoWAveragingWindow$,\n+$\\PostNUSevenPoWAveragingWindow$},\n $\\PoWMaxAdjustDown$, $\\PoWMaxAdjustUp$, $\\PoWDampingFactor$,\\notblossom{ and}\n-$\\PreBlossomPoWTargetSpacing$\\blossom{, and $\\PostBlossomPoWTargetSpacing$}\n+$\\PreBlossomPoWTargetSpacing$\\blossom{, \\notnuseven{and }$\\PostBlossomPoWTargetSpacing$}\\nuseven{, $\\PostNUSevenPoWTargetSpacing$,\n+and $\\NUSevenActivationHeight$}\n are specified in section \\crossref{constants}.\n+\\notbeforenuseven{\\par\\endgroup}\n \n Let $\\ToCompact$ and $\\ToTarget$ be as defined in \\crossref{nbits}.\n \n@@ -14879,25 +15042,53 @@\n   \\item $\\IsBlossomActivated(\\BlockHeight \\typecolon \\Nat) := (\\BlockHeight \\geq \\BlossomActivationHeight)$\n   \\item $\\BlossomPoWTargetSpacingRatio := \\hfrac{\\PreBlossomPoWTargetSpacing}{\\PostBlossomPoWTargetSpacing}$\n   \\item $\\PostBlossomHalvingInterval := \\floor{\\PreBlossomHalvingInterval \\mult \\BlossomPoWTargetSpacingRatio}$\n-  \\item $\\PoWTargetSpacingFunc(\\BlockHeight \\typecolon \\Nat) := \\begin{cases}\n+\\nuseven{\n+  \\item $\\IsNUSevenActivated(\\BlockHeight \\typecolon \\Nat) := (\\BlockHeight \\geq \\NUSevenActivationHeight)$\n+  \\item $\\NUSevenPoWTargetSpacingRatio := \\hfrac{\\PostBlossomPoWTargetSpacing}{\\PostNUSevenPoWTargetSpacing}$\n+  \\item $\\CombinedPoWTargetSpacingRatio := \\BlossomPoWTargetSpacingRatio \\mult \\NUSevenPoWTargetSpacingRatio$\n+  \\item $\\PostNUSevenHalvingInterval := \\floor{\\PostBlossomHalvingInterval \\mult \\NUSevenPoWTargetSpacingRatio}$\n+} %nuseven\n+  \\item $\\PoWTargetSpacingFunc(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n           \\PreBlossomPoWTargetSpacing,&\\caseif \\text{not } \\IsBlossomActivated(\\BlockHeight) \\\\\n-          \\PostBlossomPoWTargetSpacing,&\\caseotherwise\n+          \\PostBlossomPoWTargetSpacing,&\\notnuseven{\\caseotherwise}\\notbeforenuseven{\\nuseven{\\caseif \\IsBlossomActivated(\\BlockHeight)} \\\\[-0.8ex]\n+          &\\nuseven{\\text{ and not } \\IsNUSevenActivated(\\BlockHeight)}\\! \\\\\n+          \\nuseven{\\PostNUSevenPoWTargetSpacing,}&\\nuseven{\\caseotherwise}}\n+        \\end{cases}$\n+\\nuseven{\n+  \\item $\\PoWAveragingWindow(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n+          \\PreNUSevenPoWAveragingWindow,&\\caseif \\text{not } \\IsNUSevenActivated(\\BlockHeight) \\\\\n+          \\PostNUSevenPoWAveragingWindow,&\\caseotherwise\n         \\end{cases}$\n+} %nuseven\n } %blossom\n \n-  \\item $\\AveragingWindowTimespan\\blossom{(\\BlockHeight \\typecolon \\Nat)} := \\PoWAveragingWindow \\mult \\PoWTargetSpacingFunc\\blossom{(\\BlockHeight)}$\n+  \\item $\\AveragingWindowTimespan\\blossom{(\\BlockHeight \\typecolon \\Nat)} :=\\notnuseven{ \\PoWAveragingWindow \\mult \\PoWTargetSpacingFunc\\blossom{(\\BlockHeight)}}$\n+\\notbeforenuseven{\n+        \\vspace{-0.8ex}\n+  \\item \\tab $\\PoWAveragingWindow(\\BlockHeight) \\mult \\PoWTargetSpacingFunc(\\BlockHeight)$\n+} %notbeforenuseven\n   \\item $\\MinActualTimespan\\blossom{(\\BlockHeight \\typecolon \\Nat)} := \\floor{\\AveragingWindowTimespan\\blossom{(\\BlockHeight)} \\mult (1 - \\PoWMaxAdjustUp)}$\n   \\item $\\MaxActualTimespan\\blossom{(\\BlockHeight \\typecolon \\Nat)} := \\floor{\\AveragingWindowTimespan\\blossom{(\\BlockHeight)} \\mult (1 + \\PoWMaxAdjustDown)}$\n   \\item $\\MedianTime(\\BlockHeight \\typecolon \\Nat) :=\n           \\median(\\listcomp{\\nTime(i) \\for i \\from \\maximum(0, \\BlockHeight - \\PoWMedianBlockSpan) \\upto \\BlockHeight - 1})$\n-  \\item $\\ActualTimespan(\\BlockHeight \\typecolon \\Nat) := \\MedianTime(\\BlockHeight) - \\MedianTime(\\BlockHeight - \\PoWAveragingWindow)$\n+  \\item $\\ActualTimespan(\\BlockHeight \\typecolon \\Nat) :=\\notnuseven{ \\MedianTime(\\BlockHeight) - \\MedianTime(\\BlockHeight - \\PoWAveragingWindow)}$\n+\\notbeforenuseven{\n+        \\vspace{-0.8ex}\n+  \\item \\tab $\\MedianTime(\\BlockHeight) - \\MedianTime(\\BlockHeight - \\PoWAveragingWindow(\\BlockHeight))$\n+} %notbeforenuseven\n   \\item $\\ActualTimespanDamped(\\BlockHeight \\typecolon \\Nat) :=$\n         \\vspace{-0.8ex}\n   \\item \\tab $\\AveragingWindowTimespan\\blossom{(\\BlockHeight)} + \\trunc{\\hfrac{\\ActualTimespan(\\BlockHeight) - \\AveragingWindowTimespan\\blossom{(\\BlockHeight)}}{\\PoWDampingFactor}}$\n   \\item $\\ActualTimespanBounded(\\BlockHeight \\typecolon \\Nat) := \\bound{\\MinActualTimespan\\blossom{(\\BlockHeight)}}{\\MaxActualTimespan\\blossom{(\\BlockHeight)}}(\\ActualTimespanDamped(\\BlockHeight))$\n-  \\item $\\MeanTarget(\\BlockHeight \\typecolon \\Nat) := \\!\\begin{cases}\n-          \\PoWLimit, \\hspace{16em}\\text{if } \\BlockHeight \\leq \\PoWAveragingWindow \\\\\n-          \\mean(\\listcomp{\\!\\ToTarget(\\nBits(i)\\kern-0.1em) \\for i \\from \\BlockHeight\\!-\\!\\PoWAveragingWindow \\upto \\BlockHeight\\!-\\!1\\!}),\\\\\n+  \\item $\\MeanTarget(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n+          \\PoWLimit, \\hspace{16em}\\text{if } \\BlockHeight \\leq \\PoWAveragingWindow\\nuseven{(\\BlockHeight)} \\\\\n+          \\mean(\\listcomp{\\!\\ToTarget(\\nBits(i)\\kern-0.1em) \\for i \\from \\BlockHeight\\!-\\!\\PoWAveragingWindow\\nuseven{(\\BlockHeight)} \\upto \\BlockHeight\\!-\\!1\\!}),\\\\\n                      \\hspace{20.7em}\\text{otherwise.}\n         \\end{cases}$\n \\end{formulae}\n@@ -14906,9 +15097,11 @@\n The \\targetThreshold for a given \\blockHeight $\\BlockHeight$ is then calculated as:\n \n \\begin{formulae}\n-  \\item $\\Threshold(\\BlockHeight \\typecolon \\Nat) \\hspace{0.43em} := \\hspace{0.3em} \\begin{cases}\n+  \\item $\\Threshold(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n                \\PoWLimit, \\hspace{16em}\\text{if } \\BlockHeight = 0 \\\\\n-               \\minimum(\\PoWLimit, \\floor{\\hfrac{\\MeanTarget(\\BlockHeight)}{\\AveragingWindowTimespan}}\n+               \\minimum(\\PoWLimit, \\floor{\\hfrac{\\MeanTarget(\\BlockHeight)}{\\AveragingWindowTimespan\\blossom{(\\BlockHeight)}}}\n                                    \\mult \\ActualTimespanBounded(\\BlockHeight)),\\\\\n                           \\hspace{20.7em}\\text{otherwise}\n              \\end{cases}$\n@@ -14922,7 +15115,7 @@\n         is that these functions use only information from \\blocks \\emph{preceding} the given \\blockHeight.\n         \\introlist\n   \\item When the $\\median$ function is applied to a sequence of even length (which only happens\n-        in the definition of $\\MedianTime$ during the first $\\PoWAveragingWindow - 1$ \\blocks of\n+        in the definition of $\\MedianTime$ during the first $\\PoWAveragingWindow\\nuseven{(\\BlockHeight)} - 1$ \\blocks of\n         the \\blockChain), the element that begins the second half of the sequence is taken.\n         This corresponds to the \\zcashd implementation, but was not specified correctly in versions\n         of this specification prior to \\historyref{2019.0.0}.\n@@ -14932,6 +15125,8 @@\n is changed to allow minimum-difficulty \\blocks, as described in \\cite{ZIP-205}.\n \\blossom{The \\NUBlossom \\networkUpgrade changes the minimum-difficulty time threshold to $6$ times\n the \\blockTargetSpacing, as described in \\cite{ZIP-208}.}\n+\\nuseven{The \\NUSeven \\networkUpgrade changes it to $18$ times the \\blockTargetSpacing, as described\n+in \\cite{ZIP-218}, so that it remains $450$ seconds at the $25$-second \\blockTargetSpacing.}\n \\notbeforeblossom{These changes do}\\notblossom{This change does} not apply to \\Mainnet.\n \n \n@@ -14958,10 +15153,10 @@\n \\end{formulae}\n \n \\introlist\n-\\vspace{-2ex}\n+\\vspace{0.5ex}\n \\lsubsubsection{Definition of Work}{workdef}\n \n-\\vspace{-0.5ex}\n+\\vspace{0.5ex}\n As explained in \\crossref{blockchain}, a node chooses the ``best'' \\blockChain\n visible to it by finding the chain of valid \\blocks with the greatest total work.\n \n@@ -14972,58 +15167,102 @@\n \n \n \\introlist\n-\\vspace{-1.5ex}\n+\\vspace{0.5ex}\n \\lsubsection{Calculating Block Subsidy\\notbeforecanopy{, Funding Streams,}\\notbeforenusixone{ Lockbox Disbursement,}\n and Founders' Reward}{subsidies}\n \n-\\vspace{-0.5ex}\n+\\vspace{0.5ex}\n In \\crossref{subsidyconcepts} the \\blockSubsidy, \\minerSubsidy,\\notcanopy{ and} \\foundersReward\\canopy{,\n and \\fundingStreams} are defined. Their amounts in \\zatoshi are calculated from the \\blockHeight using\n the formulae below.\n \n+\\notbeforenuseven{\\begingroup\\raggedright}\n Let\\notbeforeblossom{ the constants} $\\SlowStartInterval$,\\, $\\PreBlossomHalvingInterval$,\\,\n-\\blossom{$\\PostBlossomHalvingInterval$,\\, $\\BlossomActivationHeight$,\\, }$\\MaxBlockSubsidy$,\n+\\blossom{$\\PostBlossomHalvingInterval$,\\, $\\BlossomActivationHeight$,\\, }\\nuseven{$\\PostNUSevenHalvingInterval$,\\,\n+$\\NUSevenActivationHeight$,\\, $\\LNTwoScaled$,\\, $\\NSMReissuanceHeight$,\\, }$\\MaxBlockSubsidy$,\n and $\\FoundersFraction$ be as defined in \\crossref{constants}.\n+\\notbeforenuseven{\\par\\endgroup}\n \n \\canopy{Let $\\FundingStreams$ be as specified in \\crossref{zip214fundingstreams}.}\n \n+\\nuseven{Let $\\NSMValueBalance$ be as defined in \\crossref{chainvaluepoolbalances}.}\n+\n \\nusixone{Let $\\ZIPTwoSevenOneActivationHeight$ and $\\ZIPTwoSevenOneDisbursementAmount$ be as defined in \\cite{ZIP-271}.}\n \n \\vspace{1ex}\n \\begin{formulae}\n   \\item $\\SlowStartShift \\typecolon \\Nat := \\hfrac{\\SlowStartInterval}{2}$\n   \\item $\\SlowStartRate \\typecolon \\Nat := \\hfrac{\\MaxBlockSubsidy}{\\SlowStartInterval}$\n+\\blossom{\n+        \\vspace{0.5ex}\n+  \\item $\\BlossomAdjustment \\typecolon \\Rat := \\hfrac{\\BlossomActivationHeight - \\SlowStartShift}{\\PreBlossomHalvingInterval}$\n+} %blossom\n+\\nuseven{\n         \\vspace{0.5ex}\n-  \\item $\\Halving(\\BlockHeight \\typecolon \\Nat) := \\begin{cases}\n+  \\item $\\NUSevenAdjustment \\typecolon \\Rat := \\BlossomAdjustment + \\hfrac{\\NUSevenActivationHeight - \\BlossomActivationHeight}{\\PostBlossomHalvingInterval}$\n+} %nuseven\n+        \\vspace{0.5ex}\n+        \\needspace{26ex}\n+  \\item $\\Halving(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n           0,&\\caseif \\BlockHeight < \\SlowStartShift \\\\\n         \\notblossom{\n           \\floor{\\hfrac{\\BlockHeight - \\SlowStartShift}{\\PreBlossomHalvingInterval}}\\kern-0.25em,&\\caseotherwise\n         } %notblossom\n         \\notbeforeblossom{\n-          \\floor{\\hfrac{\\BlockHeight - \\SlowStartShift}{\\PreBlossomHalvingInterval}}\\kern-0.25em,&\\blossom{\\caseif \\text{not } \\IsBlossomActivated(\\BlockHeight)} \\\\[1.6ex]\n-          \\multicolumn{2}{@{}l}{\\blossom{\\floor{\\hfrac{\\BlossomActivationHeight - \\SlowStartShift}{\\PreBlossomHalvingInterval} +\n-                                                \\hfrac{\\BlockHeight - \\BlossomActivationHeight}{\\PostBlossomHalvingInterval}}\\kern-0.3em,\\hspace{0.85em}\\caseotherwise}}\n+          \\floor{\\hfrac{\\BlockHeight - \\SlowStartShift}{\\PreBlossomHalvingInterval}}\\kern-0.25em,&\\blossom{\\caseif \\text{not } \\IsBlossomActivated(\\BlockHeight)} \\\\[1.2ex]\n+          \\blossom{\\floor{\\BlossomAdjustment + \\hfrac{\\BlockHeight - \\BlossomActivationHeight}{\\PostBlossomHalvingInterval}}\\kern-0.3em,}&\\blossom{\\notnuseven{\\caseotherwise}\\nuseven{\\caseif \\IsBlossomActivated(\\BlockHeight)}}\\notbeforenuseven{ \\\\[-1.8ex]\n+          &\\nuseven{\\text{ and not } \\IsNUSevenActivated(\\BlockHeight)}\\! \\\\[1.2ex]\n+          \\nuseven{\\floor{\\NUSevenAdjustment + \\hfrac{\\BlockHeight - \\NUSevenActivationHeight}{\\PostNUSevenHalvingInterval}}\\kern-0.3em,}&\\nuseven{\\caseotherwise}}\n         } %notbeforeblossom\n         \\end{cases}$\n         \\vspace{1ex}\n-  \\item $\\BlockSubsidy(\\BlockHeight \\typecolon \\Nat) := \\begin{cases}\n+        \\needspace{32ex}\n+  \\item $\\notnuseven{\\BlockSubsidy}\\nuseven{\\ScheduledBlockSubsidy}(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n           \\SlowStartRate \\mult \\BlockHeight,&\\caseif \\BlockHeight < \\SlowStartShift \\\\[1.2ex]\n           \\SlowStartRate \\mult (\\BlockHeight + 1),&\\caseif \\SlowStartShift \\leq \\BlockHeight \\\\[-0.8ex]\n                                                   &\\text{ and } \\BlockHeight < \\SlowStartInterval \\\\[1.2ex]\n           \\floor{\\hfrac{\\MaxBlockSubsidy}{2^{\\Halving(\\BlockHeight)}}}\\kern-0.25em,&\\notblossom{\\caseotherwise}\\notbeforeblossom{\\caseif \\SlowStartInterval \\leq \\BlockHeight} \\\\[-1.4ex]\n \\notbeforeblossom {\n                                                                                    &\\blossom{\\text{ and not } \\IsBlossomActivated(\\BlockHeight)}\\! \\\\[1.2ex]\n-          \\blossom{\\floor{\\hfrac{\\MaxBlockSubsidy}{\\BlossomPoWTargetSpacingRatio \\mult 2^{\\Halving(\\BlockHeight)}}}\\kern-0.25em,}&\\blossom{\\caseotherwise}\n+          \\blossom{\\floor{\\hfrac{\\MaxBlockSubsidy}{\\BlossomPoWTargetSpacingRatio \\mult 2^{\\Halving(\\BlockHeight)}}}\\kern-0.25em,}&\\blossom{\\notnuseven{\\caseotherwise}\\nuseven{\\caseif \\IsBlossomActivated(\\BlockHeight)}}\\notbeforenuseven{ \\\\[-1.8ex]\n+                                                                                   &\\nuseven{\\text{ and not } \\IsNUSevenActivated(\\BlockHeight)}\\! \\\\[1.2ex]\n+          \\nuseven{\\floor{\\hfrac{\\MaxBlockSubsidy}{\\CombinedPoWTargetSpacingRatio \\mult 2^{\\Halving(\\BlockHeight)}}}\\kern-0.25em,}&\\nuseven{\\caseotherwise}}\n } %notbeforeblossom\n         \\end{cases}$\n+\\nuseven{\n+        \\vspace{1ex}\n+  \\item $\\HalvingIntervalFunc(\\BlockHeight \\typecolon \\Nat) := \\begin{cases}\n+          \\PostBlossomHalvingInterval,&\\caseif \\text{not } \\IsNUSevenActivated(\\BlockHeight) \\\\\n+          \\PostNUSevenHalvingInterval,&\\caseotherwise\n+        \\end{cases}$\n+        \\vspace{0.5ex}\n+  \\item $\\BlockSubsidyFraction(\\BlockHeight \\typecolon \\Nat) \\typecolon \\Rat :=\n+           \\hfrac{\\floor{\\LNTwoScaled / \\HalvingIntervalFunc(\\BlockHeight)}}{10^{10}}$\n         \\vspace{0.5ex}\n+  \\item $\\AdditionalBlockSubsidy(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n+          0,&\\caseif \\BlockHeight < \\NSMReissuanceHeight \\\\[1ex]\n+          \\mathsf{ceiling}\\big(\\BlockSubsidyFraction(\\BlockHeight) \\mult \\\\[-0.4ex]\n+          \\hspace{4em} \\NSMValueBalance(\\BlockHeight - 1)\\big),&\\caseotherwise\n+        \\end{cases}$\n+        \\vspace{0.5ex}\n+  \\item $\\BlockSubsidy(\\BlockHeight \\typecolon \\Nat) := \\ScheduledBlockSubsidy(\\BlockHeight) + \\AdditionalBlockSubsidy(\\BlockHeight)$\n+} %nuseven\n+        \\vspace{1ex}\n   \\item $\\FoundersReward(\\BlockHeight \\typecolon \\Nat) := \\begin{cases}\n           \\BlockSubsidy(\\BlockHeight) \\mult \\FoundersFraction,&\\caseif \\Halving(\\BlockHeight) < 1 \\\\\n           0,&\\caseotherwise\n         \\end{cases}$\n+        \\vspace{0.5ex}\n \n \\canopy {\n         \\vspace{0.5ex}\n+        \\needspace{14ex}\n   \\item for $\\fs \\in \\FundingStreams$,\\, $\\fsValue(\\BlockHeight) :=$\n         \\vspace{-0.5ex}\n   \\item \\tab $\\begin{cases}\n@@ -15049,8 +15288,27 @@\n \n   \\item $\\MinerSubsidy(\\BlockHeight) := \\BlockSubsidy(\\BlockHeight) - \\FoundersReward(\\BlockHeight)\n            \\canopy{\\,- \\ssum{\\fs\\, \\in\\, \\FundingStreams}{} \\fsValue(\\BlockHeight)}$.\n+\\nuseven{\n+        \\vspace{1ex}\n+  \\item $\\TransactionFees(\\BlockHeight \\typecolon \\Nat) \\typecolon \\range{0}{\\MAXMONEY} :=$ the total\n+        value in \\zatoshi of the \\transactionFees paid by the non-coinbase \\transactions in the\n+        \\block at \\blockHeight $\\BlockHeight$\n+        \\vspace{0.5ex}\n+  \\item $\\NSMFeeContribution(\\BlockHeight \\typecolon \\Nat) := \\floor{\\hfrac{6 \\mult \\TransactionFees(\\BlockHeight)}{10}}$\n+        \\vspace{0.5ex}\n+  \\item $\\MinerFees(\\BlockHeight \\typecolon \\Nat) := \\TransactionFees(\\BlockHeight) - \\NSMFeeContribution(\\BlockHeight)$.\n+} %nuseven\n \\end{formulae}\n \n+\\nuseven{\n+\\vspace{-3ex}\n+\\nnote{\\cite{ZIP-235} writes $\\NSMFeeContribution(\\BlockHeight)$ as $6 \\mult q + \\floor{6 \\mult r / 10}$,\n+with $q$ and $r$ the quotient and remainder of $\\TransactionFees(\\BlockHeight)$ divided by $10$.\n+That is equal to the definition above, and avoids forming $6 \\mult \\TransactionFees(\\BlockHeight)$,\n+which can exceed $\\MAXMONEY$ in an implementation whose amounts are bounded by it. $\\NSMFeeContribution(\\BlockHeight)$\n+is removed from circulation into the NSM Value Balance defined in \\crossref{chainvaluepoolbalances}.}\n+} %nuseven\n+\n \n \\vspace{-2ex}\n \\lsubsection{Payment of Founders' Reward}{foundersreward}\n@@ -15249,10 +15507,38 @@\n   \\item $\\HeightForHalving(\\HalvingNum \\typecolon \\PosInt) :=\n           \\minimum\\Of{\\setof{\\BlockHeight \\typecolon \\Nat \\suchthat \\Halving(\\BlockHeight) = \\HalvingNum}}$\n   \\item $\\FSRecipientChangeInterval := \\PostBlossomHalvingInterval / 48$\n+\\nuseven{\n+  \\item $\\PreNUSevenEquivalentHeight(\\BlockHeight \\typecolon \\Nat) :=$\n+        \\vspace{-0.5ex}\n+  \\item \\tab $\\begin{cases}\n+          \\BlockHeight,&\\caseif \\text{not } \\IsNUSevenActivated(\\BlockHeight) \\\\[1ex]\n+          \\NUSevenActivationHeight + \\hfrac{\\BlockHeight - \\NUSevenActivationHeight}{\\NUSevenPoWTargetSpacingRatio},&\\caseotherwise\n+        \\end{cases}$\n+} %nuseven\n+\\notnuseven{\n   \\item $\\FSRecipientPeriod(\\BlockHeight) :=\n           \\floor{\\hfrac{\\BlockHeight - (\\HeightForHalving(1) - \\PostBlossomHalvingInterval)}{\\FSRecipientChangeInterval}}$.\n+} %notnuseven\n+\\notbeforenuseven{\n+  \\item \\nuseven{$\\FSRecipientPeriod(\\BlockHeight) :=$}\n+        \\vspace{-0.5ex}\n+  \\item \\tab \\nuseven{$\\floor{\\hfrac{\\PreNUSevenEquivalentHeight(\\BlockHeight) - (\\HeightForHalving(1) - \\PostBlossomHalvingInterval)}{\\FSRecipientChangeInterval}}$.}\n+} %notbeforenuseven\n \\end{formulae}\n \n+\\nuseven{\n+\\pnote{$\\PreNUSevenEquivalentHeight(\\BlockHeight)$ is a rational number: the \\blockHeight that the\n+\\block at \\blockHeight $\\BlockHeight$ would have if the \\blocks from \\NUSeven activation onward had\n+been mined at the pre-\\NUSeven \\blockTargetSpacing. Revision 2 of \\cite{ZIP-207} writes the\n+\\NUSeven case of $\\FSRecipientPeriod$ as $\\floor{\\hfrac{R \\mult (A + I - H_1) + \\BlockHeight - A}{R \\mult C}}$,\n+where $A$ is $\\NUSevenActivationHeight$, $R$ is $\\NUSevenPoWTargetSpacingRatio$, $C$ is\n+$\\FSRecipientChangeInterval$, $I$ is $\\PostBlossomHalvingInterval$, and $H_1$ is $\\HeightForHalving(1)$.\n+Dividing its numerator and denominator by $R$ gives the definition above. This preserves the\n+recipient index at \\NUSeven activation. The remaining \\blocks of the period in which \\NUSeven\n+activates are tripled in number, and each later period lasts $3 \\mult \\FSRecipientChangeInterval$\n+\\blocks, so that each period keeps approximately its previous duration and total value.}\n+} %nuseven\n+\n \\introlist\n For each \\fundingStream $\\fs$, define:\n \n@@ -15263,9 +15549,12 @@\n \\end{formulae}\n \n $\\fsRecipients$ \\MUST be of length $\\fsNumRecipients$. Each element of $\\fsRecipients$\n-\\MUST represent either a \\transparentPtoSHAddress as specified in \\crossref{transparentaddrencoding},\n-or a \\Sapling \\shieldedPaymentAddress as specified in \\crossref{saplingpaymentaddrencoding}\\nusix{,\n-or the identifier $\\DeferredPoolRecipient$}.\n+\\MUST represent either a \\transparentPtoSHAddress\\nuseven{ or \\transparentPtoPKHAddress} as specified\n+in \\crossref{transparentaddrencoding}, or a \\Sapling \\shieldedPaymentAddress as specified in\n+\\crossref{saplingpaymentaddrencoding}\\nuseven{, or a Unified Address \\cite{ZIP-316} containing only\n+an \\Orchard Receiver and no other Items}\\nusix{, or the identifier $\\DeferredPoolRecipient$}.\n+\\nuseven{A Unified Address recipient identifier represents its \\Orchard Receiver, which is paid by\n+an \\IronwoodPoolAdj output as specified below.}\n \n A \\fundingStream $\\fs$ is ``active'' at \\blockHeight $\\BlockHeight$ when $\\fsValue(\\BlockHeight) > 0$,\n where $\\fsValue$ is defined in \\crossref{subsidies}.\n@@ -15299,6 +15588,15 @@\n \n         \\defining{\\xStandardRedeemScript} hashes are defined in \\cite{ZIP-48} for\n         \\PtoSHMultisigAddresses, or \\cite{Bitcoin-P2SH} for other \\PtoSHAddresses.\n+\\nuseven{\n+        \\vspace{0.5ex}\n+  \\item The \\defining{\\prescribedWay} to pay a \\transparentPtoPKHAddress is to use a script of\n+        the form $\\ScriptOP{DUP}$ $\\ScriptOP{HASH160}$ $\\fsPubKeyHash(\\BlockHeight)\\;\n+        \\ScriptOP{EQUALVERIFY}$ $\\ScriptOP{CHECKSIG}$ as the $\\scriptPubKey$, where\n+        $\\fsPubKeyHash(\\BlockHeight)$ is the direct push of the $20$-byte public key hash in the\n+        raw encoding (\\crossref{transparentaddrencoding}) of the recipient address for\n+        $\\fsRecipient(\\BlockHeight)$. No other script encoding satisfies this rule.\n+} %nuseven\n         \\vspace{0.5ex}\n   \\item The \\defining{\\prescribedWay} to pay a \\SaplingOrOrchard \\paymentAddress is\n         defined in \\cite{ZIP-213}, using the post-\\NUHeartwood consensus rules in\n@@ -15329,8 +15627,9 @@\n         \\SaplingAndOrchard \\shieldedPaymentAddresses as recipients. \\cite{ZIP-213}\n         permits \\shieldedOutputs of \\coinbaseTransactions to such addresses.\\nufive{ The\n         recipient identifiers permitted above do not include \\Orchard\n-        \\shieldedPaymentAddresses before Revision 2 of \\cite{ZIP-207}. The \\prescribedWay\n-        to pay one is defined in anticipation of that revision.}\n+        \\shieldedPaymentAddresses before Revision 2 of \\cite{ZIP-207}\\nuseven{, which applies\n+        from \\NUSeven}.\\notnuseven{ The \\prescribedWay to pay one was already defined, in\n+        anticipation of that revision.}}\n \\end{pnotes}\n \n \\vspace{-1ex}\n@@ -15420,8 +15719,8 @@\n \\item \\begin{tabular}{|l|c|c|c|c|}\n \\hline\n Stream                   & Numerator & Denominator & Start height & End height \\\\\\hline\\raisedstrut\n-\\texttt{FS\\_FPF\\_ZCG\\_H3}&    $8$    &    $100$    &  $3146400$   &  $4406400$ \\\\\n-\\texttt{FS\\_CCF\\_H3}     &   $12$    &    $100$    &  $3146400$   &  $4406400$ \\\\\\hline\n+\\texttt{FS\\_FPF\\_ZCG\\_H3}&    $8$    &    $100$    &  $3146400$   &  $\\notnuseven{4406400}\\nuseven{6132390}$ \\\\\n+\\texttt{FS\\_CCF\\_H3}     &   $12$    &    $100$    &  $3146400$   &  $\\notnuseven{4406400}\\nuseven{6132390}$ \\\\\\hline\n \\end{tabular}\n \\end{formulae}\n \n@@ -15431,10 +15730,20 @@\n \\item \\begin{tabular}{|l|c|c|c|c|}\n \\hline\n Stream                   & Numerator & Denominator & Start height & End height \\\\\\hline\\raisedstrut\n-\\texttt{FS\\_FPF\\_ZCG\\_H3}&    $8$    &    $100$    &  $3536500$   &  $4476000$ \\\\\n-\\texttt{FS\\_CCF\\_H3}     &   $12$    &    $100$    &  $3536500$   &  $4476000$ \\\\\\hline\n+\\texttt{FS\\_FPF\\_ZCG\\_H3}&    $8$    &    $100$    &  $3536500$   &  $\\notnuseven{4476000}\\nuseven{4497948}$ \\\\\n+\\texttt{FS\\_CCF\\_H3}     &   $12$    &    $100$    &  $3536500$   &  $\\notnuseven{4476000}\\nuseven{4497948}$ \\\\\\hline\n \\end{tabular}\n \\end{formulae}\n+\n+\\nuseven{\n+\\vspace{1ex}\n+\\cite{ZIP-214} Revision 3, which applies from \\NUSeven activation, changes the end height of each\n+Revision 2 \\fundingStream to $\\HeightForHalving(3)$ under the \\NUSeven definition of $\\Halving$,\n+as shown above; before \\NUSeven the end heights were $4406400$ on \\Mainnet and $4476000$ on\n+\\Testnet. It also changes the recipients of \\texttt{FS\\_FPF\\_ZCG\\_H3} on \\Mainnet for the\n+recipient periods after the one that contains the last pre-\\NUSeven \\block, as specified in\n+\\cite{ZIP-2008}.\n+} %nuseven\n } %nusixone\n \n \n@@ -16167,8 +16476,12 @@\n David Campbell, Elena Giralt, Francisco Gindre, Joseph Van~Geffen, Josh Swihart,\n Kevin Gorham, Larry Ruane, Marshall Gaucher, Ryan Taylor, Sasha Meyer,\n Conrado Gouvêa, Aditya Bharadwaj, Andrew Arnott, Arya, Andrea Kobrlova,\n-Lukáš Korba, Honza Rychnovský, Schell Scivally, Julian Abraham, and no doubt\n-others.\n+Lukáš Korba, Honza Rychnovský, Schell Scivally, @Peacemonger, Nicolas Gailly, Danny Willems,\n+Jason McGee, Mark Henderson, Tomek Piotrowski, Mariusz Pilarek, Paul Dann, Dev Ojha, Evan Forbes,\n+Anna Rose, Jens Groth, Judah Caruso, Pablo Kogan, Vivek Arte, Daniel Benarroch, Aurelien Nicolas,\n+Alexey Koren, Antoine Rondelet, Constance Beguier, Georgios Konstantopoulos, Matt Luongo,\n+Alex Bornstein, @dontbeevil, @GGuy, @mistfpga, Gordon Mohr, Dimitris Apostolou, Gregory Maxwell,\n+Pieter Wuille, Julian Abraham, and no doubt others.\n We would also like to thank the designers and developers of \\Bitcoin and\n \\BitcoinCore.\n \n@@ -16196,6 +16509,8 @@\n The 2019 Soundness vulnerability in \\BCTV \\cite{Gabizon2019}\n was found by Ariel Gabizon.\n \n+The 2026 \\Orchard Action circuit soundness vulnerability was found by Taylor Hornby.\n+\n The design of \\Sapling is primarily due to Matthew Green, Ian Miers,\n \\nh{Daira-Emma} Hopwood, Sean Bowe, Jack Grigg, and Jack Gavigan. A potential attack\n linking \\diversifiedPaymentAddresses, avoided in the adopted design, was\n@@ -16222,7 +16537,7 @@\n Amit Sahai.\n \n We thank the organizers of the ZKProof standardization effort and workshops;\n-and also Anna Rose, Fredrik Harrysson, Terun Chitra, James Prestwich,\n+and also Anna Rose, Fredrik Harrysson, Tarun Chitra, James Prestwich,\n Josh Cincinnati, Tanya Karsou, Henrik Jose, Chris Ward, and others for their\n work on the Zero Knowledge Podcast, ZK Summits, and ZK Study Club. These\n efforts have enriched the zero knowledge community immeasurably.\n@@ -16242,7 +16557,7 @@\n \\xShieldedPool names are set in Raleway Italic, by Matt McInerney, Pablo Impallari,\n and Rodrigo Fuenzalida.\n The {\\schoolbook New Century Schoolbook} font by URW Type Foundry, based on\n-Century Schoolbook designed by Morris Fuller, is used for \\emph{italics}\\!.\n+Century Schoolbook designed by Morris Fuller Benton, is used for \\emph{italics}\\!.\n \n Finally, we would like to thank the Internet Archive for their scan of\n Peter Newell's illustration of the Jubjub bird, from \\cite{Carroll1902}.\n@@ -16251,6 +16566,83 @@\n \\intropart\n \\lsection{Change History}{changehistory}\n \n+\\historyentry{2026.9.0}{2026-11-06}\n+\n+\\begin{itemize}\n+\\nuseven{\n+    \\item Describe \\NUSeven as having activated on \\Mainnet at \\blockHeight $3543405$ in\n+          \\shortcrossref{networkupgrades}, give its \\activationBlock hashes in\n+          \\shortcrossref{networks}, and add the consensus rule that \\transactions \\MUST use the\n+          \\NUSeven \\consensusBranchID.\n+    \\item Apply the change described in \\cite{ZIP-2003}: from \\NUSeven, the\n+          \\transactionVersionNumber \\MUST be $5$ or $6$ (\\shortcrossref{txnconsensus}).\n+    \\item Apply the changes described in \\cite{ZIP-218}:\n+          \\vspace{0.5ex}\n+          \\begin{itemize}\n+              \\item Add $\\NUSevenActivationHeight$, $\\PostNUSevenPoWTargetSpacing$, the averaging\n+                    window constants, and the shielded limit constants to \\shortcrossref{constants}\n+                    and \\shortcrossref{notation}.\n+              \\item In \\shortcrossref{diffadjustment}, make $\\PoWTargetSpacingFunc$ return $25$\n+                    seconds from \\NUSeven, make $\\PoWAveragingWindow$ a function of \\blockHeight that\n+                    returns $102$ from \\NUSeven, define $\\CombinedPoWTargetSpacingRatio$ and\n+                    $\\PostNUSevenHalvingInterval$, and change the \\Testnet minimum-difficulty\n+                    threshold to $18$ times the \\blockTargetSpacing.\n+              \\item In \\shortcrossref{subsidies}, define $\\BlossomAdjustment$ and $\\NUSevenAdjustment$,\n+                    and add the \\NUSeven cases of $\\Halving$ and $\\BlockSubsidy$.\n+              \\item In \\shortcrossref{blockheader}, add the per-pool limits and the global shielded\n+                    budget on the shielded components of the \\transactions in a \\block.\n+          \\end{itemize}\n+          \\vspace{-1.5ex}\n+    \\item Apply Revision 2 of \\cite{ZIP-207}, \\cite{ZIP-2008}, and Revision 3 of \\cite{ZIP-214}:\n+          \\vspace{0.5ex}\n+          \\begin{itemize}\n+              \\item In \\shortcrossref{fundingstreams}, add the \\NUSeven case of $\\FSRecipientPeriod$,\n+                    expressed through $\\PreNUSevenEquivalentHeight$.\n+              \\item Permit transparent \\PtoPKHAddresses and Unified Addresses containing only an\n+                    \\Orchard Receiver as \\fundingStream recipients, and define the \\prescribedWay to\n+                    pay a \\transparentPtoPKHAddress.\n+              \\item In \\shortcrossref{zip214fundingstreams}, give the \\NUSeven end heights of the\n+                    Revision 2 \\fundingStreams, and note the \\NUSeven change of recipients of\n+                    \\texttt{FS\\_FPF\\_ZCG\\_H3} on \\Mainnet.\n+          \\end{itemize}\n+          \\vspace{-1.5ex}\n+    \\item Apply the changes described in \\cite{ZIP-235}: define $\\TransactionFees$,\n+          $\\NSMFeeContribution$, and $\\MinerFees$ in \\shortcrossref{subsidies}; from \\NUSeven, the\n+          \\totalInputValue of a \\coinbaseTransaction in \\shortcrossref{txnconsensus} includes\n+          $\\MinerFees(\\BlockHeight)$ in place of the \\transactionFees, and \\shortcrossref{transactions},\n+          \\shortcrossref{subsidyconcepts}, and \\shortcrossref{coinbasetransactions} say so.\n+    \\item Apply the changes described in \\cite{ZIP-237}:\n+          \\vspace{0.5ex}\n+          \\begin{itemize}\n+              \\item Add $\\LNTwoScaled$, $\\InitialNSMValueBalance$, and $\\NSMReissuanceHeight$ to\n+                    \\shortcrossref{constants} and \\shortcrossref{notation}.\n+              \\item In \\shortcrossref{subsidies}, rename $\\BlockSubsidy$ to $\\ScheduledBlockSubsidy$.\n+              \\item In \\shortcrossref{subsidies}, define $\\HalvingIntervalFunc$,\n+                    $\\BlockSubsidyFraction$, and $\\AdditionalBlockSubsidy$, and redefine\n+                    $\\BlockSubsidy$ as the sum of $\\ScheduledBlockSubsidy$ and $\\AdditionalBlockSubsidy$.\n+              \\item In \\shortcrossref{chainvaluepoolbalances}, define $\\NSMValueRemoved$ and the\n+                    NSM Value Balance, with the consensus rule that it \\MUSTNOT become negative, and\n+                    note that the \\SproutChainValuePool balance remains issued (\\cite{ZIP-2003}).\n+              \\item In \\shortcrossref{transactions}, say that the value left unclaimed by\n+                    \\coinbaseTransactions before \\NUSix is reissued, and in\n+                    \\shortcrossref{subsidyconcepts} that the \\blockSubsidy includes reissued value\n+                    from $\\NSMReissuanceHeight$.\n+          \\end{itemize}\n+          \\vspace{-1.5ex}\n+} %nuseven\n+    \\item Add an acknowledgement to Taylor Hornby for finding the 2026 \\Orchard Action\n+          circuit soundness vulnerability.\n+    \\item Add acknowledgements to @Peacemonger, Nicolas Gailly, Danny Willems, Jason McGee,\n+          Mark Henderson, Tomek Piotrowski, Mariusz Pilarek, Paul Dann, Dev Ojha, Evan Forbes,\n+          Anna Rose, Jens Groth, Judah Caruso, Pablo Kogan, Vivek Arte, Daniel Benarroch,\n+          Aurelien Nicolas, Alexey Koren, Antoine Rondelet, Constance Beguier,\n+          Georgios Konstantopoulos, Matt Luongo, Alex Bornstein, @dontbeevil, @GGuy, @mistfpga,\n+          Gordon Mohr, Dimitris Apostolou, Gregory Maxwell, and Pieter Wuille for discussions on\n+          the Zcash protocol.\n+    \\item Correct the names of Tarun Chitra and Morris Fuller Benton in the Acknowledgements.\n+\\end{itemize}\n+\n+\n \\historyentry{2026.8.0}{2026-11-06}\n \n \\begin{itemize}"},{"filename":"protocol/zcash.bib","status":"modified","additions":54,"deletions":0,"patch":"@@ -1390,6 +1390,15 @@ @misc{ZIP-216\n    urldate={2021-02-25}\n }\n \n+@misc{ZIP-218,\n+   presort={ZIP-0218},\n+   author={Dev Ojha and Evan Forbes},\n+   title={25-second Block Target Spacing},\n+   howpublished={Zcash Improvement Proposal 218. Created March~13, 2026.},\n+   url={https://zips.z.cash/zip-0218},\n+   urldate={2026-11-06}\n+}\n+\n @misc{ZIP-221,\n    presort={ZIP-0221},\n    author={{Ying~Tong} Lai and James Prestwich and Georgios Konstantopoulos and Jack Grigg},\n@@ -1435,6 +1444,15 @@ @misc{ZIP-229\n    urldate={2026-07-03}\n }\n \n+@misc{ZIP-235,\n+   presort={ZIP-0235},\n+   author={Jason McGee and Zooko Wilcox and Mark Henderson and Tomek Piotrowski and Mariusz Pilarek and Paul Dann},\n+   title={Remove 60\\% of Transaction Fees From Circulation},\n+   howpublished={Zcash Improvement Proposal 235. Created September~21, 2023.},\n+   url={https://zips.z.cash/zip-0235},\n+   urldate={2026-11-06}\n+}\n+\n @misc{ZIP-236,\n    presort={ZIP-0236},\n    author={{Daira\\nbh{}Emma} Hopwood},\n@@ -1444,6 +1462,15 @@ @misc{ZIP-236\n    urldate={2024-09-24}\n }\n \n+@misc{ZIP-237,\n+   presort={ZIP-0237},\n+   author={Judah Caruso and Jason McGee and Zooko Wilcox and Mark Henderson and Tomek Piotrowski and Mariusz Pilarek and Paul Dann},\n+   title={Network Sustainability Mechanism: Halving-Preserving Issuance},\n+   howpublished={Zcash Improvement Proposal 237. Created August~19, 2026.},\n+   url={https://zips.z.cash/zip-0237},\n+   urldate={2026-11-06}\n+}\n+\n @misc{ZIP-239,\n    presort={ZIP-0239},\n    author={{Daira\\nbh{}Emma} Hopwood and Jack Grigg},\n@@ -1552,6 +1579,15 @@ @misc{ZIP-258\n    urldate={2026-06-16}\n }\n \n+@misc{ZIP-259,\n+   presort={ZIP-0259},\n+   author={Dev Ojha},\n+   title={Deployment of the {NU7} Network Upgrade},\n+   howpublished={Zcash Improvement Proposal 259. Created September~15, 2026.},\n+   url={https://zips.z.cash/zip-0259},\n+   urldate={2026-11-06}\n+}\n+\n @misc{ZIP-271,\n    presort={ZIP-0271},\n    author={Daira\\nbh{}Emma Hopwood and Kris Nuttycombe and Jack Grigg},\n@@ -1660,6 +1696,15 @@ @misc{ZIP-312\n    urldate={2026-09-06}\n }\n \n+@misc{ZIP-2003,\n+   presort={ZIP-2003},\n+   author={Daira\\nbh{}Emma Hopwood},\n+   title={Disallow version 4 transactions},\n+   howpublished={Zcash Improvement Proposal 2003. Created October~31, 2024.},\n+   url={https://zips.z.cash/zip-2003},\n+   urldate={2026-11-06}\n+}\n+\n @misc{ZIP-2005,\n    presort={ZIP-2005},\n    author={Daira\\nbh{}Emma Hopwood and Jack Grigg},\n@@ -1678,6 +1723,15 @@ @misc{ZIP-2006\n    urldate={2026-07-03}\n }\n \n+@misc{ZIP-2008,\n+   presort={ZIP-2008},\n+   author={Jack Grigg},\n+   title={Update to {FS\\_FPF\\_ZCG\\_H3} address list},\n+   howpublished={Zcash Improvement Proposal 2008. Created September~24, 2026.},\n+   url={https://zips.z.cash/zip-2008},\n+   urldate={2026-11-06}\n+}\n+\n @misc{DigiByte-PoW,\n    presort={DigiByte-PoW},\n    author={DigiByte Core Developers},"},{"filename":"zips/zip-0207.rst","status":"modified","additions":9,"deletions":0,"patch":"@@ -237,6 +237,15 @@ period, its remaining block count is tripled; each subsequent period lasts\n retains approximately its previous wall-clock duration and funding amount,\n subject to per-block rounding.\n \n+(The protocol specification [#protocol-fundingstreams]_ expresses this through\n+$\\mathsf{PreNU7EquivalentHeight}(\\mathsf{height})$, the height that the block at\n+$\\mathsf{height}$ would have if the blocks from NU7 activation onward had been\n+mined at the pre-NU7 target spacing: $\\mathsf{height}$ if not\n+$\\mathsf{IsNU7Activated}(\\mathsf{height})$, and $A + (\\mathsf{height} - A)/R$\n+otherwise. Its $\\mathsf{FSRecipientPeriod}$ applies the pre-NU7 formula to this\n+rational number in place of $\\mathsf{height}$, which is equal to the definition\n+above after dividing the numerator and denominator of the second case by $R$.)\n+\n Below is a visual representation of how stream addresses align with funding\n periods:\n "},{"filename":"zips/zip-0218.md","status":"modified","additions":19,"deletions":8,"patch":"@@ -379,23 +379,31 @@ consideration.\n \n ### Halving interval and block subsidy\n \n-Define:\n+In § 7.7.3 'Difficulty adjustment', define:\n \n+$$\\mathsf{CombinedPoWTargetSpacingRatio} : \\mathbb{N} := \\mathsf{BlossomPoWTargetSpacingRatio} \\cdot \\mathsf{NU7PoWTargetSpacingRatio} = 6$$\n $$\\mathsf{PostNU7HalvingInterval} : \\mathbb{N} := \\lfloor \\mathsf{PostBlossomHalvingInterval} \\cdot \\mathsf{NU7PoWTargetSpacingRatio} \\rfloor = 5{,}040{,}000$$\n \n-Redefine $\\mathsf{Halving}$ as a function of type $\\mathbb{N} \\to \\mathbb{N}$:\n+In § 7.8 'Calculating Block Subsidy, Funding Streams, Lockbox Disbursement, and\n+Founders' Reward' [^protocol-subsidies], define:\n+\n+$$\\mathsf{BlossomAdjustment} : \\mathbb{Q} := \\dfrac{\\mathsf{BlossomActivationHeight} - \\mathsf{SlowStartShift}}{\\mathsf{PreBlossomHalvingInterval}}$$\n+$$\\mathsf{NU7Adjustment} : \\mathbb{Q} := \\mathsf{BlossomAdjustment} + \\dfrac{\\mathsf{NU7ActivationHeight} - \\mathsf{BlossomActivationHeight}}{\\mathsf{PostBlossomHalvingInterval}}$$\n+\n+$\\mathsf{BlossomAdjustment}$ is the first term of the existing Blossom case of\n+$\\mathsf{Halving}$, which is rewritten to use it. Redefine $\\mathsf{Halving}$ as\n+a function of type $\\mathbb{N} \\to \\mathbb{N}$:\n \n $$\n \\mathsf{Halving}(\\mathsf{height}) :=\n   \\begin{cases}\n     0, &\\textsf{if } \\mathsf{height} < \\mathsf{SlowStartShift} \\\\\\\\[1.5ex]\n     \\left\\lfloor \\dfrac{\\mathsf{height} - \\mathsf{SlowStartShift}}{\\mathsf{PreBlossomHalvingInterval}} \\right\\rfloor,\n       &\\textsf{if not } \\mathsf{IsBlossomActivated}(\\mathsf{height}) \\\\\\\\[1.5ex]\n-    \\left\\lfloor \\dfrac{\\mathsf{BlossomActivationHeight} - \\mathsf{SlowStartShift}}{\\mathsf{PreBlossomHalvingInterval}}\n+    \\left\\lfloor \\mathsf{BlossomAdjustment}\n       + \\dfrac{\\mathsf{height} - \\mathsf{BlossomActivationHeight}}{\\mathsf{PostBlossomHalvingInterval}} \\right\\rfloor,\n       &\\textsf{if } \\mathsf{IsBlossomActivated}(\\mathsf{height}) \\textsf{ and not } \\mathsf{IsNU7Activated}(\\mathsf{height}) \\\\\\\\[1.5ex]\n-    \\left\\lfloor \\dfrac{\\mathsf{BlossomActivationHeight} - \\mathsf{SlowStartShift}}{\\mathsf{PreBlossomHalvingInterval}}\n-      + \\dfrac{\\mathsf{NU7ActivationHeight} - \\mathsf{BlossomActivationHeight}}{\\mathsf{PostBlossomHalvingInterval}}\n+    \\left\\lfloor \\mathsf{NU7Adjustment}\n       + \\dfrac{\\mathsf{height} - \\mathsf{NU7ActivationHeight}}{\\mathsf{PostNU7HalvingInterval}} \\right\\rfloor,\n       &\\textsf{otherwise}\n   \\end{cases}\n@@ -412,8 +420,8 @@ $$\n \\mathsf{BlockSubsidy}(\\mathsf{height}) :=\n   \\begin{cases}\n     \\ldots &\\text{(prior cases, with the Blossom case amended as above)} \\\\\\\\[1ex]\n-    \\mathsf{floor}\\left(\\dfrac{\\mathsf{MaxBlockSubsidy}}{\\mathsf{BlossomPoWTargetSpacingRatio} \\cdot \\mathsf{NU7PoWTargetSpacingRatio} \\cdot 2^{\\mathsf{Halving}(\\mathsf{height})}}\\right),\n-      &\\textsf{if } \\mathsf{IsNU7Activated}(\\mathsf{height})\n+    \\mathsf{floor}\\left(\\dfrac{\\mathsf{MaxBlockSubsidy}}{\\mathsf{CombinedPoWTargetSpacingRatio} \\cdot 2^{\\mathsf{Halving}(\\mathsf{height})}}\\right),\n+      &\\textsf{otherwise}\n   \\end{cases}\n $$\n \n@@ -512,7 +520,8 @@ fields specified in § 7.1 'Transaction Encoding and Consensus'\n by ZIP 229 [^zip-0229]. Each is taken as 0 for a transaction version that\n does not have the field.\n \n-For each block at height $\\mathsf{height}$ where\n+In § 7.6 'Block Header Encoding and Consensus' [^protocol-blockheader], add a\n+consensus rule that for each block at height $\\mathsf{height}$ where\n $\\mathsf{IsNU7Activated}(\\mathsf{height})$, the following limits MUST\n be satisfied:\n \n@@ -993,6 +1002,8 @@ consensus branch ID.\n \n [^protocol-txnconsensus]: [Zcash Protocol Specification, Version 2025.6.3. Section 7.1.2: Transaction Consensus Rules](protocol/protocol.pdf#txnconsensus)\n \n+[^protocol-blockheader]: [Zcash Protocol Specification, Version 2025.6.3. Section 7.6: Block Header Encoding and Consensus](protocol/protocol.pdf#blockheader)\n+\n [^protocol-subsidies]: [Zcash Protocol Specification, Version 2025.6.3. Section 7.8: Calculating Block Subsidy, Funding Streams, Lockbox Disbursement, and Founders' Reward](protocol/protocol.pdf#subsidies)\n \n [^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)"},{"filename":"zips/zip-0234.md","status":"modified","additions":6,"deletions":0,"patch":"@@ -16,6 +16,12 @@ License: BSD-2-Clause\n Discussions-To: <https://github.com/zcash/zips/issues/923>\n ```\n \n+<div class=\"note\"></div>\n+\n+This ZIP was replaced by ZIP 237 (\"Network Sustainability Mechanism:\n+Halving-Preserving Issuance\" [^zip-0237]), which Network Upgrade 7 deploys\n+instead. [^zip-0259]\n+\n \n # Terminology\n "},{"filename":"zips/zip-0235.md","status":"modified","additions":10,"deletions":0,"patch":"@@ -143,6 +143,14 @@ $\\mathsf{floor}(6 \\cdot \\mathsf{TransactionFees}(\\mathsf{height}) / 10)$, but\n avoids multiplying the aggregate fee value by 6. It uses the aggregate fees of\n the block, not the fees of each transaction.\n \n+In § 7.8 ‘Calculating Block Subsidy, Funding Streams, Lockbox Disbursement, and\n+Founders' Reward’ [^protocol-subsidies], add the definitions of\n+$\\mathsf{TransactionFees}$, $\\mathsf{NSMFeeContribution}$, and\n+$\\mathsf{MinerFees}$. The protocol specification defines\n+$\\mathsf{NSMFeeContribution}(\\mathsf{height})$ directly as\n+$\\mathsf{floor}(6 \\cdot \\mathsf{TransactionFees}(\\mathsf{height}) / 10)$, and\n+gives the form above in a non-normative note.\n+\n In § 7.1.2 ‘Transaction Consensus Rules’ [^protocol-txnconsensus], change the\n definition of the total input value of a coinbase transaction to:\n \n@@ -291,6 +299,8 @@ require the activation of ZIP 233 (\"NSM: Removing Funds From Circulation\" [^zip-\n \n [^protocol-txnconsensus]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.1.2: Transaction Consensus Rules](protocol/protocol.pdf#txnconsensus)\n \n+[^protocol-subsidies]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.8: Calculating Block Subsidy, Funding Streams, Lockbox Disbursement, and Founders' Reward](protocol/protocol.pdf#subsidies)\n+\n [^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)\n \n [^zip-0233]: [ZIP 233: Network Sustainability Mechanism: Removing Funds From Circulation](zip-0233.md)"},{"filename":"zips/zip-0237.md","status":"modified","additions":26,"deletions":22,"patch":"@@ -146,7 +146,7 @@ satisfies the following requirements:\n 1. The issuance can be summarized into a reasonably simple explanation.\n 2. If the NSM Value Balance is zero, block subsidies are identical to the\n    existing halving schedule.\n-3. From $\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ onward, if the NSM Value Balance\n+3. From $\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$ onward, if the NSM Value Balance\n    is greater than 0, then the additional block\n    subsidy must be non-zero, so that funds removed from circulation are\n    eventually fully reissued.\n@@ -191,7 +191,7 @@ This is $4126 / 10\\_000\\_000\\_000 = 0.0000004126$ with\n $\\mathsf{PostBlossomHalvingInterval} = 1\\_680\\_000$, the value specified by\n ZIP 234 [^zip-0234], and $1375 / 10\\_000\\_000\\_000$ with\n $\\mathsf{PostNU7HalvingInterval} = 5\\_040\\_000$. Since\n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ is not before NU7 activation, only the latter\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$ is not before NU7 activation, only the latter\n value is used by $\\mathsf{AdditionalBlockSubsidy}$. See the BLOCK_SUBSIDY_FRACTION\n section below.\n \n@@ -222,10 +222,9 @@ value pool balances that a full node reports at that height.\n | Mainnet | 1,575,000,000,000,000 | 1,574,963,141,554,480 |\n | Testnet | 1,603,125,000,000,000 | 1,603,069,231,585,043 |\n \n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT} ⦂ \\mathbb{N} :=$ the height at which reissuance begins\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT} ⦂ \\mathbb{N} :=$ the height at which reissuance begins\n on the relevant network, calculated in [NSM reissuance height](#nsmreissuanceheight)\n-below. It is named $\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$ in the NU7 deployment\n-ZIP [^zip-0259].\n+below.\n \n ## NSM reissuance height\n \n@@ -241,7 +240,7 @@ through height $h$, excluding additional NSM subsidies and amounts removed\n from circulation. Let $H_3$ and $H_4$ be the first heights at which the\n ZIP 218 $\\mathsf{Halving}$ function equals 3 and 4, respectively.\n \n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ SHALL be the first height $h$ with\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$ SHALL be the first height $h$ with\n $\\max(A, H_3 + 1) \\leq h < H_4$ for which\n \n $$\\mathsf{ceiling}\\left(\\frac{1375\\,(\\mathsf{MAX\\_MONEY} - S_A(h - 1))}{10^{10}}\\right) < B_A(h).$$\n@@ -273,7 +272,7 @@ $R_0 = 262{,}499{,}975{,}638{,}334 - (A - H_2^{(75)})$ zatoshi.\n Consequently $\\mathsf{ceiling}((R_0 - R_{\\max})/b) = 2{,}807{,}273$\n throughout the range. Including the one-block offset from $H_3$ to\n $h_0$ gives\n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT} = H_3 + 2{,}807{,}274$:\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT} = H_3 + 2{,}807{,}274$:\n \n | Network | Second halving | Third halving after NU7 | Reissuance height |\n |---------|---------------:|-------------------------:|------------------:|\n@@ -299,17 +298,19 @@ Add the following definition, where $\\mathsf{NSMValueBalance}$ is as defined\n in § 4.17 ‘Chain Value Pool Balances’ [^protocol-chainvaluepoolbalances] below:\n \n $$\\mathsf{AdditionalBlockSubsidy}(\\mathsf{height} ⦂ \\mathbb{N}) := \\begin{cases}\n-0, & \\text{if } \\mathsf{height} < \\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT} \\\\\n+0, & \\text{if } \\mathsf{height} < \\mathsf{NSM\\_REISSUANCE\\_HEIGHT} \\\\\n \\mathsf{ceiling}(\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}(\\mathsf{height}) \\cdot \\mathsf{NSMValueBalance}(\\mathsf{height} - 1)), & \\text{otherwise}\n \\end{cases}$$\n \n Define $\\mathsf{BlockSubsidy}(\\mathsf{height})$ as:\n \n $\\mathsf{BlockSubsidy}(\\mathsf{height}) := \\mathsf{ScheduledBlockSubsidy}(\\mathsf{height}) + \\mathsf{AdditionalBlockSubsidy}(\\mathsf{height})$\n \n-Add $\\mathsf{LN2\\_SCALED}$ and $\\mathsf{INITIAL\\_NSM\\_VALUE\\_BALANCE}$ to § 5.3\n-‘Constants’ [^protocol-constants], and $\\mathsf{HalvingInterval}$ and\n-$\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}$, as given under Parameters above, to § 7.8.\n+Add $\\mathsf{LN2\\_SCALED}$, $\\mathsf{INITIAL\\_NSM\\_VALUE\\_BALANCE}$, and\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$ to § 5.3 ‘Constants’ [^protocol-constants]\n+and to the list of integer constants in § 2 ‘Notation’ [^protocol-notation],\n+and $\\mathsf{HalvingInterval}$ and $\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}$, as given\n+under Parameters above, to § 7.8.\n \n All other uses of $\\mathsf{BlockSubsidy}(\\mathsf{height})$ in the protocol\n specification ($\\mathsf{FoundersReward}$, $\\mathsf{fsValue}$ and hence\n@@ -322,6 +323,9 @@ NSM Value Balance after block $\\mathsf{height} - 1$, and not only on\n $\\mathsf{height}$.\n \n In § 4.17 ‘Chain Value Pool Balances’ [^protocol-chainvaluepoolbalances], add\n+the definition of $\\mathsf{NSMValueRemoved}(\\mathsf{height})$ from\n+[Mechanisms for removal from circulation](#mechanismsforremovalfromcirculation)\n+above, which as of NU7 is $\\mathsf{NSMFeeContribution}(\\mathsf{height})$, and\n the following definition. Let $\\mathsf{NU7ActivationHeight}$ be the NU7\n activation height on the relevant network. [^zip-0259]\n \n@@ -347,17 +351,17 @@ Add the consensus rule:\n In § 3.10 ‘Block Subsidy, Funding Streams, and Founders' Reward’\n [^protocol-subsidyconcepts], add:\n \n-> [NU7 onward] From $\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$, the block subsidy\n-> also includes value reissued from the NSM Value Balance (§ 4.17), and so\n-> depends on the NSM Value Balance after the preceding block as well as on the\n-> block height.\n+> [NU7 onward] From $\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$, the block subsidy\n+> also includes value reissued from the NSM Value Balance defined in § 4.17\n+> ‘Chain Value Pool Balances’, and so depends on the NSM Value Balance after\n+> the preceding block as well as on the block height.\n \n In § 3.4 ‘Transactions and Treestates’ [^protocol-transactions], where the\n remaining value in the transparent transaction value pool of coinbase\n transactions in blocks prior to NU6 is said to be destroyed, say instead that\n it is left unclaimed and is not added to any chain value pool, and add:\n \n-> [NU7 onward] This value is included in\n+> [NU7 onward] This unclaimed value is included in\n > $\\mathsf{INITIAL\\_NSM\\_VALUE\\_BALANCE}$, and so is reissued as specified in\n > § 7.8.\n \n@@ -372,11 +376,11 @@ All of these changes apply identically to Mainnet and Testnet.\n   function only to the NSM Value Balance satisfies **Requirements 1**, **2**\n   and **4** above.\n * No reissuance occurs between NU7 activation and\n-  $\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$, so the NSM Value Balance accumulates\n+  $\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$, so the NSM Value Balance accumulates\n   during that interval. The reissuance height is chosen as described in\n   [NSM reissuance height](#nsmreissuanceheight) and ZIP 259 [^zip-0259].\n * We round up to the next zatoshi to satisfy **Requirement 3** above from\n-  $\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ onward. Since\n+  $\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$ onward. Since\n   $\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION} < 1$ and the NSM Value Balance is a\n   non-negative integer number of zatoshi, the additional subsidy never exceeds\n   the balance it is calculated from, so from NU7 activation the sum of the\n@@ -402,8 +406,8 @@ Balance at that point ($\\mathsf{INITIAL\\_NSM\\_VALUE\\_BALANCE}$ plus anything\n removed from circulation since NU7 activation) at\n $\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}$ per block. For example, if a total of\n 100,000 ZEC were removed from circulation between NU7 activation and\n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$, then at\n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ the issuance would be larger than the\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$, then at\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$ the issuance would be larger than the\n Scheduled Block Subsidy by\n $100\\_000\\textsf{ ZEC} \\cdot \\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}$, which equals\n $0.01375$ ZEC under the post-NU7 schedule. This example is chosen to demonstrate\n@@ -437,7 +441,7 @@ approximation within $\\pm 0.003\\%$. ZIP 218 [^zip-0218] does not change\n $\\mathsf{PostBlossomHalvingInterval}$; it defines a separate\n $\\mathsf{PostNU7HalvingInterval} = 5\\_040\\_000$ for its 25-second blocks, and\n with that interval the numerator is 1375, which is the value applied from\n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$: $(1 - 1375/10^{10})^{5\\_040\\_000} \\approx 0.50007$,\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$: $(1 - 1375/10^{10})^{5\\_040\\_000} \\approx 0.50007$,\n within $+0.015\\%$. A numerator fixed at 4126 would\n instead halve the balance about every 1.33 years under ZIP 218, because it\n would apply three times as often.\n@@ -476,7 +480,7 @@ constraint.\n \n This ZIP is proposed to activate with Network Upgrade 7 [^zip-0259], with\n reissuance beginning at\n-$\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$. It MUST NOT be deployed together with\n+$\\mathsf{NSM\\_REISSUANCE\\_HEIGHT}$. It MUST NOT be deployed together with\n ZIP 234. [^zip-0234] It does not depend on ZIP 233 (\"NSM: Removing Funds From\n Circulation\" [^zip-0233]): funds removed from circulation by ZIP 233, or by any\n other mechanism, are credited to the NSM Value Balance if and when that"},{"filename":"zips/zip-0259.md","status":"modified","additions":2,"deletions":2,"patch":"@@ -119,8 +119,8 @@ NSM_REISSUANCE_HEIGHT\n : Testnet: 7305222\n : Mainnet: 8939664\n \n-Each NSM_REISSUANCE_HEIGHT is $\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ for the\n-corresponding network, as calculated in ZIP 237 [^zip-0237]. The Testnet\n+Each NSM_REISSUANCE_HEIGHT is as calculated in ZIP 237 [^zip-0237] for the\n+corresponding network. The Testnet\n ACTIVATION_HEIGHT (NU7) $A = 4{,}465{,}026$ lies between the second and third\n pre-NU7 Testnet halvings (2,796,000 and 4,476,000). The ZIP 218 third halving\n is therefore at $H_3 = 13{,}428{,}000 - 2A = 4{,}497{,}948$, and the Testnet"},{"filename":"zips/zip-2008.md","status":"modified","additions":15,"deletions":5,"patch":"@@ -67,11 +67,19 @@ In \"References\" [^zip-0207], add:\n \n ## Changes to the Zcash Protocol Specification\n \n-In § 7.10 'Payment of Funding Streams, Deferred Lockbox, and Lockbox Disbursement',\n-from NU7 activation, extend the permitted funding-stream recipient types to\n-include transparent P2PKH addresses. Add the P2PKH prescribed payment rule above\n-alongside the existing P2SH and shielded payment rules. The amount and distinct\n-output requirements are unchanged.\n+In § 7.10 'Payment of Funding Streams, Deferred Lockbox, and Lockbox Disbursement'\n+[^protocol-fundingstreams], from NU7 activation, extend the permitted\n+funding-stream recipient types to include transparent P2PKH addresses, and add\n+the following alongside the existing P2SH and shielded prescribed ways:\n+\n+> The prescribed way to pay a transparent P2PKH address is to use a script of\n+> the form `OP_DUP OP_HASH160 fs.PubKeyHash(height) OP_EQUALVERIFY OP_CHECKSIG`\n+> as the `scriptPubKey`, where $\\mathsf{fs.PubKeyHash}(\\mathsf{height})$ is the\n+> direct push of the 20-byte public key hash in the raw encoding of the\n+> recipient address for $\\mathsf{fs.Recipient}(\\mathsf{height})$. No other\n+> script encoding satisfies this rule.\n+\n+The amount and distinct output requirements are unchanged.\n \n ## Changes to ZIP 214\n \n@@ -166,6 +174,8 @@ This ZIP is proposed to be deployed with NU7 [^zip-0259].\n \n [^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n \n+[^protocol-fundingstreams]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.10: Payment of Funding Streams, Deferred Lockbox, and Lockbox Disbursement](protocol/protocol.pdf#fundingstreams)\n+\n [^zip-0207]: [ZIP 207: Funding Streams](zip-0207.rst)\n \n [^zip-0214]: [ZIP 214: Consensus rules for a Zcash Development Fund](zip-0214.rst)"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0207.rst","title":"Funding Streams","status":"[Revision 0: Canopy, Revision 1: NU6] Final, [Revision 2: NU7] Proposed","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"revisions","text":"Revisions"},{"level":2,"id":"definitions","text":"Definitions"},{"level":2,"id":"funding-streams","text":"Funding streams"},{"level":2,"id":"deferred-development-fund-chain-value-pool-balance","text":"Deferred Development Fund Chain Value Pool Balance"},{"level":2,"id":"consensus-rules","text":"Consensus rules"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"backward-compatibility","text":"Backward compatibility"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"change-history","text":"Change History"},{"level":2,"id":"section-1","text":"2026-10-13"},{"level":2,"id":"section-2","text":"2026-09-30"},{"level":2,"id":"section-3","text":"2026-09-24"},{"level":2,"id":"section-4","text":"2025-08-19"},{"level":2,"id":"section-5","text":"2024-10-31"},{"level":2,"id":"section-6","text":"2024-09-26"},{"level":2,"id":"section-7","text":"2020-05-30"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1445,"title":"ZIP 207: Permit Orchard addresses as Revision 2 recipient identifiers","author":"daira","createdAt":"2026-11-06T01:05:08Z","updatedAt":"2026-11-06T01:05:10Z","url":"https://github.com/zcash/zips/pull/1445","diffUrl":"https://github.com/zcash/zips/pull/1445.diff","headRef":"zip-207-orchard-recipients","headRepo":"daira/zips","headSha":"2706050695f098eb2de4ae9a36c0b481c9bf18ff","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Revision 2 of ZIP 207 was intended to permit Orchard addresses as funding-stream recipient identifiers, given as Unified Addresses (ZIP 316) containing only an Orchard Receiver. Their omission from the Revision 2 recipient rule was an oversight, which this corrects. The Revision 2 rule is now a list","zipRefs":[207],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0207.rst","status":"modified","additions":29,"deletions":10,"patch":"@@ -7,7 +7,7 @@\n   Status: [Revision 0: Canopy, Revision 1: NU6] Final, [Revision 2: NU7] Proposed\n   Category: Consensus\n   Created: 2019-01-04\n-  Last-Updated: 2026-10-13\n+  Last-Updated: 2026-11-06\n   License: MIT\n \n \n@@ -108,8 +108,9 @@ Revisions\n \n * Revision 2: Extends the remaining address period at NU7 activation and\n   subsequent address periods in proportion to the change in target block\n-  spacing specified in ZIP 218 [#zip-0218]_, and permits transparent P2PKH\n-  addresses per ZIP 2008 [#zip-2008]_.\n+  spacing specified in ZIP 218 [#zip-0218]_, permits transparent P2PKH\n+  addresses per ZIP 2008 [#zip-2008]_, and permits Orchard addresses, given\n+  as Unified Addresses [#zip-0316]_ containing only an Orchard Receiver.\n \n Definitions\n -----------\n@@ -168,10 +169,20 @@ network upgrade [#zip-0253]_, indicates that the value is to be paid to a\n reserve to be used for development funding, the distribution of which is to be\n determined via a future ZIP.\n \n-As of `Revision 2`_, each recipient identifier MUST be either the string encoding\n-of a transparent P2PKH or P2SH address or of a Sapling address (as specified in\n-[#protocol-transparentaddrencoding]_ or [#protocol-saplingpaymentaddrencoding]_)\n-or the identifier $\\mathsf{DEFERRED\\_POOL}$.\n+As of `Revision 2`_, each recipient identifier MUST be one of:\n+\n+- the string encoding of a transparent P2PKH or P2SH address (as specified in\n+  [#protocol-transparentaddrencoding]_);\n+- the string encoding of a Sapling address (as specified in\n+  [#protocol-saplingpaymentaddrencoding]_);\n+- the string encoding of a Unified Address containing only an Orchard Receiver\n+  and no other Items (as specified in ZIP 316 [#zip-0316]_); or\n+- the identifier $\\mathsf{DEFERRED\\_POOL}$.\n+\n+A Unified Address recipient identifier represents its Orchard Receiver, which is\n+an Orchard-protocol address. Since `Revision 2`_ applies only from NU7, which is\n+after NU6.3, such a recipient is paid by an Ironwood-pool output, as rule 5 below\n+specifies.\n \n Each address is used for at most 1/48th of a halving interval, creating a\n roughly-monthly sequence of funding periods. The address to be used for a\n@@ -378,9 +389,9 @@ Rule 5 has subsequently been clarified as follows:\n   from NU6.3 activation onward, since coinbase transactions can then contain\n   no Orchard-pool component [#zip-0213]_.\n \n-Note: The recipient identifiers permitted above do not include Orchard\n-addresses; the \"prescribed way\" to pay an Orchard address is defined in\n-anticipation of a future change.\n+Note: Before `Revision 2`_, the recipient identifiers permitted above did not\n+include Orchard addresses; the \"prescribed way\" to pay an Orchard address was\n+defined in anticipation of their addition.\n Up to and including NU7 there have been no funding streams defined\n with a shielded payment address as a recipient. That might change in future,\n so implementations are encouraged to support Sapling and Orchard addresses as\n@@ -435,6 +446,13 @@ Reference Implementation\n Change History\n ==============\n \n+2026-11-06\n+----------\n+\n+- Permitted Orchard addresses, given as Unified Addresses containing only an\n+  Orchard Receiver, as recipient identifiers in Revision 2. Their omission from\n+  Revision 2 was an oversight.\n+\n 2026-10-13\n ----------\n \n@@ -510,6 +528,7 @@ References\n .. [#zip-0253] `ZIP 253: Deployment of the NU6 Network Upgrade <zip-0253.md>`_\n .. [#zip-0259] `ZIP 259: Deployment of the NU7 Network Upgrade <zip-0259.md>`_\n .. [#zip-0271] `ZIP 271: Dev Fund Extension and One-Time Disbursement <zip-0271.md>`_\n+.. [#zip-0316] `ZIP 316: Unified Addresses and Unified Viewing Keys <zip-0316.rst>`_\n .. [#zip-1014] `ZIP 1014: Establishing a Dev Fund for ECC, ZF, and Major Grants <zip-1014.rst>`_\n .. [#zip-1015] `ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding <zip-1015.rst>`_\n .. [#zip-2001] `ZIP 2001: Lockbox Funding Streams <zip-2001.rst>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0207.rst","title":"Funding Streams","status":"[Revision 0: Canopy, Revision 1: NU6] Final, [Revision 2: NU7] Proposed","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"revisions","text":"Revisions"},{"level":2,"id":"definitions","text":"Definitions"},{"level":2,"id":"funding-streams","text":"Funding streams"},{"level":2,"id":"deferred-development-fund-chain-value-pool-balance","text":"Deferred Development Fund Chain Value Pool Balance"},{"level":2,"id":"consensus-rules","text":"Consensus rules"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"backward-compatibility","text":"Backward compatibility"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"change-history","text":"Change History"},{"level":2,"id":"section-1","text":"2026-11-06"},{"level":2,"id":"section-2","text":"2026-10-13"},{"level":2,"id":"section-3","text":"2026-09-30"},{"level":2,"id":"section-4","text":"2026-09-24"},{"level":2,"id":"section-5","text":"2025-08-19"},{"level":2,"id":"section-6","text":"2024-10-31"},{"level":2,"id":"section-7","text":"2024-09-26"},{"level":2,"id":"section-8","text":"2020-05-30"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1443,"title":"ZIP 248 fixes","author":"str4d","createdAt":"2026-11-05T20:35:47Z","updatedAt":"2026-11-05T21:35:16Z","url":"https://github.com/zcash/zips/pull/1443","diffUrl":"https://github.com/zcash/zips/pull/1443.diff","headRef":"zip248-fixes","headRepo":"zcash/zips","headSha":"d878b28d6d55a92a9d3a0252083ccca65abb52da","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[233,248,2002],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0233.md","status":"modified","additions":7,"deletions":5,"patch":"@@ -162,11 +162,13 @@ In § 7.1 'Transaction Encoding and Consensus' [^protocol-txnconsensus], add:\n \n ## Modifications to Digest Algorithms\n \n-The $\\mathsf{zip233\\_amount}$ is committed to the transaction identifier and\n-signature digest via the `value_pool_deltas_digest` defined in ZIP 248 [^zip-0248].\n-Since the NSM bundle (bundle type 6) has no effecting data and no authorizing\n-data, its only contribution to the transaction digest is through its entry\n-in `mValuePoolDeltas`.\n+The $\\mathsf{zip233\\_amount}$ is committed to the transaction identifier via\n+`value_pool_deltas_digest` and to the v0 signature digest via\n+`value_pool_deltas_sig_digest`, as defined in ZIP 248 [^zip-0248]. The NSM\n+bundle's entry is included in the latter digest regardless of the transparent\n+signature's `hash_type`. Since the NSM bundle (bundle type 6) has no effecting\n+data and no authorizing data, its only contribution to these digests is through\n+its entry in `mValuePoolDeltas`.\n \n ## Applicability\n "},{"filename":"zips/zip-0248.rst","status":"modified","additions":406,"deletions":169,"patch":"@@ -58,13 +58,28 @@ transparent transaction value pool\n Abstract\n ========\n \n-This ZIP proposes an encoding for V7 Zcash transactions that is intended to\n-reduce the impact of future changes to the Zcash transaction format on the\n-Zcash ecosystem. It defines a new typecode-length-value encoding for a sequence\n-of protocol bundles, and a \"value balance\" map that describes the effect of\n-each bundle on the transparent transaction value pool for each asset. The\n-entries of this map serve the same purpose as the Sapling and Orchard value\n-balance fields have done in the past.\n+This ZIP proposes an encoding for V7 Zcash transactions that reduces the\n+impact of future transaction format changes on wallets and other clients.\n+Previous format changes required clients to update their parsers even when\n+they did not use the newly introduced protocol features.\n+\n+The format defines typecode-length-value encodings for protocol bundles and\n+a value pool delta map describing each bundle's effect on the transparent\n+transaction value pool for each asset. The entries of this map serve the\n+same purpose as the Sapling and Orchard value balance fields have done in\n+the past. Effecting data and authorizing data are encoded separately, allowing\n+authorizing data to be pruned without removing the transaction's effects.\n+\n+Clients can parse the common transaction envelope and enumerate public value\n+flows without understanding every bundle type or variant. Computing the\n+transaction identifier additionally requires a supplied digest for each\n+unsupported effecting bundle. This does not eliminate the need to update\n+wallets for consensus changes that alter how they must construct or interpret\n+transactions.\n+\n+Future additive bundle introductions can avoid making wallets' support for\n+unused features distinguishable on-chain, reducing the need for further\n+transaction-format changes and their associated privacy leakage.\n \n \n Motivation\n@@ -151,10 +166,6 @@ Requirements\n   transactions in that version group.\n \n \n-Non-requirements\n-================\n-\n-\n Specification\n =============\n \n@@ -220,9 +231,11 @@ The separation of effecting data from authorizing data serves several purposes:\n    authorizing data appears at the end of the encoded transaction, pruning\n    is simply truncation.\n \n-3. **Partial validation**: A wallet that does not understand a particular\n-   bundle type can still compute the transaction identifier by hashing the\n-   effecting data opaquely, without needing to parse its internal structure.\n+3. **Partial processing**: A wallet can parse a transaction's envelope without\n+   understanding every bundle type or variant. To compute the transaction\n+   identifier, it needs the corresponding ``bundle_effects_digest`` for each\n+   unsupported effecting bundle, as described in `Implications for Wallets`_;\n+   hashing the opaque payload does not in general produce that digest.\n \n .. raw:: html\n \n@@ -288,6 +301,11 @@ A bundle type MUST NOT permit entries in ``mAuthBundles`` unless it also permits\n entries in ``mEffectBundles``. That is, authorizing data cannot exist without\n corresponding effecting data for a given bundle type.\n \n+If a bundle's encoding defines a per-element sequence in its authorizing data\n+as corresponding to a per-element sequence in its effecting data, it MUST\n+specify that the two sequences have the same length and that elements at the\n+same index correspond.\n+\n Once a ``(bundleType, bundleVariant)`` pair is assigned for a given transaction\n version, its semantics are fixed for the lifetime of that transaction version.\n A subsequent network upgrade may define a new transaction version that\n@@ -353,11 +371,12 @@ Potential Future Bundle Types\n    <details>\n    <summary>Click to show/hide</summary>\n \n-The following bundle kinds have no allocated ``(bundleType, bundleVariant)``\n-pair. Where a bundle type is given, the kind is a new variant of that existing\n-type and only its variant identifier is unassigned. Those proposed by a ZIP\n-name it; the rest are provided to illustrate how potential future upgrades\n-might affect the bundle registry:\n+This subsection is non-normative. The following bundle kinds illustrate\n+possible future extensions; they do not register or activate any\n+``(bundleType, bundleVariant)`` pair. Where a bundle type is given, the kind\n+is a new variant of that existing type and only its variant identifier is\n+unassigned. Those proposed by a ZIP name it; the rest illustrate how potential\n+future upgrades might affect the bundle registry:\n \n +------------+-----------------+----------------------+--------------------+------------------+--------------+-------------------------------------------------------------+\n | BundleType | BundleVariant   | ``mValuePoolDeltas`` | ``mEffectBundles`` | ``mAuthBundles`` | Defining ZIP | Bundle kind                                                 |\n@@ -550,27 +569,12 @@ wallet does not itself implement.\n * For each ``bundleType`` that appears in ``mAuthBundles``, a corresponding\n   entry with the same ``bundleVariant`` MUST exist in ``mEffectBundles``.\n \n-* Within the encoding of any bundle's effecting and authorizing data, every\n-  per-element sequence in the authorizing data that is described as\n-  corresponding to a per-element sequence in the effecting data MUST have\n-  exactly the same length as that effecting-data sequence, and the element at\n-  each index in the authorizing-data sequence corresponds to the element at\n-  the same index in the effecting-data sequence. For the bundle types\n-  defined by this ZIP this requires:\n-\n-  * ``vSpendProofsSapling`` and ``vSpendAuthSigsSapling`` each have\n-    ``nSpendsSapling`` elements;\n-  * ``vOutputProofsSapling`` has ``nOutputsSapling`` elements;\n-  * in each Orchard protocol bundle, ``vSpendAuthSigsOrchard`` has\n-    ``nActionsOrchard`` elements, and ``proofsOrchard`` aggregates exactly\n-    ``nActionsOrchard`` per-action proofs.\n-\n A wallet that successfully parses a V7 transaction under these rules is\n-guaranteed to be able to compute the transaction identifier (given, for\n-each bundle type it does not understand, the corresponding 32-byte\n-``bundle_effects_digest`` value as described in `Implications for Wallets`_),\n-and to enumerate the transparent value flows of the transaction at the\n-granularity of bundle types.\n+guaranteed to be able to compute the transaction identifier (given, for each\n+``(bundleType, bundleVariant)`` pair in ``mEffectBundles`` it does not\n+understand, the corresponding 32-byte ``bundle_effects_digest`` value as\n+described in `Implications for Wallets`_), and to enumerate the transparent\n+value flows of the transaction at the granularity of bundle types.\n \n \n Consensus Rules\n@@ -586,12 +590,44 @@ Let ``CoinbaseBundleId`` be the identifier of the coinbase bundle, and\n $\\mathsf{CoinbaseBundleId} = 1$ and $\\mathsf{FeeBundleId} = 5$ as defined in\n the table above.\n \n+Shielded balance field substitutions\n+````````````````````````````````````\n+\n+For V7 transactions, references in existing consensus rules and chain value\n+pool accounting to ``valueBalanceSapling``, ``valueBalanceOrchard``, and\n+``valueBalanceIronwood`` MUST use the ZEC map lookups for bundle types 2, 3,\n+and 4, respectively. These values replace the old encoded fields; they are\n+not additional contributions. A missing map entry yields zero. The binding\n+signature checks use the same values, as specified in `Sapling Bundle`_ and\n+`Orchard Protocol Bundles`_.\n+\n+The existing per-pool value ranges, the nonnegative *Orchard-pool* balancing\n+value restriction, and chain value pool nonnegativity rules continue to\n+apply. In particular, a V7 transaction contributes the negation of each\n+pool's ZEC map lookup to that chain value pool's balance, alongside the\n+contributions from pre-V7 transactions. [#protocol-chainvaluepoolbalances]_\n+All balance sums MUST be evaluated without integer overflow.\n+\n+Bundle availability\n+```````````````````\n+\n+For every ``(bundleType, bundleVariant)`` pair appearing in any of\n+``mValuePoolDeltas``, ``mEffectBundles``, or ``mAuthBundles``, a full validator\n+MUST verify that the pair is registered for V7 and activated for the consensus\n+epoch of the block containing the transaction. A full validator MUST reject a\n+transaction containing an unregistered or unactivated pair. Registration alone\n+does not activate a bundle type or variant.\n+\n+These consensus checks do not prevent a wallet from parsing an unsupported\n+bundle type or variant opaquely under `Parsing Rules`_.\n+\n Bundle-local rules\n ``````````````````\n \n-The following rules constrain the contents of individual bundles. A wallet\n-or full validator only needs to enforce a given rule in this subsection if\n-it understands the bundle type that the rule applies to.\n+The following rules constrain the contents of individual bundles. Full\n+validators MUST enforce every applicable rule. A wallet only needs to enforce\n+a given rule in this subsection if it understands the bundle type and variant\n+that the rule applies to.\n \n * A bundle type MUST NOT appear in any map for which the corresponding column\n   of the `V7 Transaction Bundle Type Registry`_ is marked ❌, and MUST NOT\n@@ -637,9 +673,20 @@ full validators.\n   transaction (expressed as a negative value, since it is removed from the\n   transparent transaction value pool).\n \n-* Within the scope of a block, the sum of the fee bundle values MUST equal 0.\n-  That is, the fees collected by the coinbase transaction MUST equal the sum of\n-  fees paid by all other transactions in the block.\n+* The fees collected by the coinbase transaction MUST equal the sum of the\n+  fees paid by all other transactions in the block, regardless of their\n+  transaction formats. For each non-coinbase transaction, its fee is:\n+\n+  * for a V7 transaction, the negation of\n+    $\\mathsf{mValuePoolDeltas}[(\\mathsf{FeeBundleId}, \\mathsf{Zec})]$;\n+  * for a pre-V7 transaction, the implicit fee defined in § 3.4\n+    'Transactions and Treestates'. [#protocol-transactions]_\n+\n+  For a V7 coinbase transaction, its fee bundle value MUST equal this sum.\n+  For a pre-V7 coinbase transaction, this sum is the transaction-fee input\n+  used by its existing coinbase value rules. In a block containing only V7\n+  transactions, this is equivalent to requiring the sum of fee bundle values\n+  to equal zero.\n \n * Exactly one transaction in a block MUST be a coinbase transaction, and it\n   MUST be the first transaction in the block.\n@@ -671,6 +718,83 @@ full validators.\n \n       \\forall \\mathsf{a}. \\sum_{\\mathsf{d} \\in \\mathsf{mValuePoolDeltas} | \\mathsf{AssetUuid}(\\mathsf{d}) = \\mathsf{a}} \\mathsf{d.value} = 0\n \n+  For V7 transactions, this rule replaces the remaining-value treatment of\n+  the transparent transaction value pool in § 3.4 'Transactions and\n+  Treestates', including the implicit transaction fee. [#protocol-transactions]_\n+  For a V7 coinbase transaction, it also replaces the ``totalInputValue`` and\n+  ``totalOutputValue`` definitions and their comparison in § 7.1.2\n+  'Transaction Consensus Rules'. [#protocol-txnconsensus]_ The explicit\n+  coinbase and fee bundle rules above determine the subsidy and fee\n+  contributions instead. These replacements do not change the balance or\n+  implicit-fee rules for pre-V7 transactions.\n+\n+Changes to the Protocol Specification\n+-------------------------------------\n+\n+Transaction input and output requirements\n+`````````````````````````````````````````\n+\n+In § 7.1.2 'Transaction Consensus Rules', [#protocol-txnconsensus]_ replace\n+$\\mathsf{effectiveVersion} \\geq 6$ with $\\mathsf{effectiveVersion} = 6$ in\n+each of the following rules:\n+\n+  [NU6.3 onward] If $\\mathsf{effectiveVersion} \\geq 6$ then this rule MUST hold:\n+  $\\mathtt{tx\\_in\\_count} > 0$ or $\\mathtt{nSpendsSapling} > 0$ or\n+  ($\\mathtt{nActionsOrchard} > 0$ and $\\mathtt{enableSpendsOrchard} = 1$) or\n+  ($\\mathtt{nActionsIronwood} > 0$ and $\\mathtt{enableSpendsIronwood} = 1$).\n+\n+  [NU6.3 onward] If $\\mathsf{effectiveVersion} \\geq 6$ then this rule MUST hold:\n+  $\\mathtt{tx\\_out\\_count} > 0$ or $\\mathtt{nOutputsSapling} > 0$ or\n+  ($\\mathtt{nActionsOrchard} > 0$ and $\\mathtt{enableOutputsOrchard} = 1$) or\n+  ($\\mathtt{nActionsIronwood} > 0$ and $\\mathtt{enableOutputsIronwood} = 1$).\n+\n+Add the following rules for V7 transactions. They apply to variant 0 of the\n+bundle types listed below; a ZIP introducing another type or variant can\n+amend these conditions as needed.\n+\n+A V7 transaction MUST satisfy at least one of these input-side conditions:\n+\n+* Its ``mEffectBundles`` contains the coinbase bundle (type 1).\n+* Its transparent bundle (type 0) has ``tx_in_count > 0``.\n+* Its Sapling bundle (type 2) has ``nSpendsSapling > 0``.\n+* Its ``mEffectBundles`` contains an Orchard or Ironwood bundle (type 3 or 4)\n+  with ``enableSpends = 1``.\n+\n+A V7 transaction MUST also satisfy at least one of these output-side\n+conditions:\n+\n+* Its transparent bundle (type 0) has ``tx_out_count > 0``.\n+* Its Sapling bundle (type 2) has ``nOutputsSapling > 0``.\n+* Its ``mEffectBundles`` contains an Orchard or Ironwood bundle (type 3 or 4)\n+  with ``enableOutputs = 1``.\n+* Its ZEC fee delta (type 5) is negative, representing a fee payment.\n+* Its ZEC NSM delta (type 6) is negative, representing removal of funds from\n+  circulation as specified in ZIP 233. [#zip-0233]_\n+\n+An absent effecting bundle does not satisfy a condition on that bundle's\n+contents. Orchard and Ironwood bundle presence implies a nonzero action\n+count, as required in `Orchard Protocol Bundles`_. A missing value pool delta\n+entry has value zero and does not satisfy either negative-delta condition.\n+\n+Rationale for Transaction Input and Output Requirements\n+'''''''''''''''''''''''''''''''''''''''''''''''''''''''\n+\n+.. raw:: html\n+\n+   <details>\n+   <summary>Click to show/hide</summary>\n+\n+These conditions exclude metadata-only transactions while allowing value to\n+flow to fees or removal from circulation without creating a chain value pool\n+output. They also allow fee-free transfers within a single shielded pool:\n+such a transaction can satisfy both sides through its spends and outputs\n+even when it has no value pool delta entries. A zero net delta does not imply\n+that no value moves within a pool.\n+\n+.. raw:: html\n+\n+   </details>\n+\n Sighash Versioning\n -------------------\n \n@@ -693,10 +817,13 @@ signers, the ``associatedData`` that each version requires, and the digest that\n a signature using it commits to. Those definitions are given for the bundle\n types registered by this ZIP in `Bundle Definitions`_.\n \n-Version 0 is by convention the \"commit to all effecting data\" sighash algorithm.\n-Other versions can commit to whatever makes sense for desired functionality\n-within a given transaction version. Consensus rules choose the digest algorithm\n-for each signer based on ``sighashVersion``.\n+Version 0 is by convention the \"commit to all effecting data\" sighash algorithm,\n+subject to the transparent bundle's legacy ``hash_type`` rules specified below.\n+Those rules affect only commitments to the transparent bundle; they do not\n+relax commitments to any other bundle's effecting data or value pool deltas.\n+Other versions can define different commitments, including partial commitments\n+across bundles, within a given transaction version. Consensus rules choose the\n+digest algorithm for each signer based on ``sighashVersion``.\n \n Sighash version information is present alongside each signature in the\n authorizing data of each bundle.\n@@ -829,6 +956,27 @@ is::\n \n   BLAKE2b-256(\"ZTxIdEffBnd_Hash\", [])\n \n+Rationale for Per-Bundle Effects Digests\n+''''''''''''''''''''''''''''''''''''''''\n+\n+.. raw:: html\n+\n+   <details>\n+   <summary>Click to show/hide</summary>\n+\n+Each effecting bundle contributes a fixed-size digest to this hash. A wallet\n+can therefore use a supplied digest for an unsupported bundle without parsing\n+its internal structure, while computing the other bundle digests itself.\n+The ``bundleType`` and ``bundleVariant`` tags remain committed to alongside\n+each digest, and the header and complete value pool delta map remain separate\n+commitments in the transaction identifier. This permits bundle-specific hash\n+trees, including the subtrees used in compact representations, without\n+requiring every receiving wallet to understand every bundle's hash tree.\n+\n+.. raw:: html\n+\n+   </details>\n+\n v0 Signature Digest\n ```````````````````\n \n@@ -847,22 +995,24 @@ For transactions that have no transparent inputs, the v0 signature digest is\n identical to the transaction identifier digest.\n \n For transactions with transparent inputs, the signature digest replaces\n-``effects_bundles_digest`` with a ``signature_bundles_digest`` that incorporates\n-``hash_type``-dependent transparent signing data::\n+``value_pool_deltas_digest`` and ``effects_bundles_digest`` with\n+``value_pool_deltas_sig_digest`` and ``signature_bundles_digest``, respectively.\n+These incorporate ``hash_type``-dependent commitments to the transparent bundle\n+while preserving full commitments to all other bundles::\n \n     signature_digest\n     ├── header_digest\n-    ├── value_pool_deltas_digest\n+    ├── value_pool_deltas_sig_digest\n     └── signature_bundles_digest\n \n signature_digest\n ''''''''''''''''\n \n A BLAKE2b-256 hash of the following values::\n \n-   S.1: header_digest             (32-byte hash output)\n-   S.2: value_pool_deltas_digest  (32-byte hash output)\n-   S.3: signature_bundles_digest  (32-byte hash output)\n+   S.1: header_digest                (32-byte hash output)\n+   S.2: value_pool_deltas_sig_digest  (32-byte hash output)\n+   S.3: signature_bundles_digest      (32-byte hash output)\n \n The personalization field of this hash is set to::\n \n@@ -872,6 +1022,26 @@ This value has the same personalization as the transaction identifier digest,\n so that what is being signed in the case that there are no transparent inputs\n is exactly the transaction id.\n \n+S.2: value_pool_deltas_sig_digest\n+'''''''''''''''''''''''''''''''''\n+\n+For a signature over a transparent input with ``SIGHASH_ANYONECANPAY`` set,\n+this is constructed as specified for ``value_pool_deltas_digest`` (T.2), except\n+that all entries with ``bundleType = 0`` (the transparent bundle) are omitted,\n+regardless of ``bundleVariant`` or ``assetClass``. The complete encodings of\n+the remaining entries are concatenated in their original transaction order,\n+without a count prefix. No other entry is omitted; in particular, the\n+transaction fee and all other bundles' value pool deltas remain committed to.\n+\n+The personalization remains ``ZTxIdVPDeltaHash``. If no entries remain, the\n+digest is::\n+\n+  BLAKE2b-256(\"ZTxIdVPDeltaHash\", [])\n+\n+For every other case, including signatures over shielded bundles and\n+transactions with no transparent inputs, ``value_pool_deltas_sig_digest`` is\n+identical to ``value_pool_deltas_digest`` (T.2).\n+\n S.3: signature_bundles_digest\n '''''''''''''''''''''''''''''\n \n@@ -882,6 +1052,33 @@ Otherwise, ``signature_bundles_digest`` is constructed the same as\n ``effects_bundles_digest``, except that ``transparent_effects_digest`` is\n replaced with ``transparent_sig_digest``.\n \n+Rationale for v0 Transparent Commitments\n+''''''''''''''''''''''''''''''''''''''''\n+\n+.. raw:: html\n+\n+   <details>\n+   <summary>Click to show/hide</summary>\n+\n+The legacy ``hash_type`` rules select which transparent inputs and outputs a\n+signature commits to. They do not relax commitments to other bundles. All\n+transparent bundle value pool delta entries are also excluded when\n+``SIGHASH_ANYONECANPAY`` is set, so that they do not independently commit to\n+the aggregate value of transparent inputs and outputs for any asset.\n+Omitting whole entries accommodates the canonical absence of zero-valued\n+entries.\n+\n+The transaction fee remains committed to for all v0 signatures, as specified\n+in ZIP 2002 [#zip-2002]_. Together with the other bundles' fixed value pool\n+deltas and the per-asset balance rule, this constrains the net transparent\n+value in any valid completion. Thus ``SIGHASH_NONE`` and ``SIGHASH_SINGLE``\n+can permit changes to unsigned transparent outputs, but those changes remain\n+subject to the fixed fee and other bundles' value pool deltas.\n+\n+.. raw:: html\n+\n+   </details>\n+\n Authorizing Data Commitment\n ```````````````````````````\n \n@@ -907,9 +1104,6 @@ The personalization field of this hash is set to::\n \n   \"ZTxAuthHash_\" || CONSENSUS_BRANCH_ID\n \n-For transaction versions before V7, a placeholder value consisting of 32 bytes\n-of ``0xFF`` is used in place of the authorizing data commitment.\n-\n A.1: auth_bundles_digest\n ''''''''''''''''''''''''\n \n@@ -973,11 +1167,29 @@ encounters a bundle with a recognized ``bundleType`` but unrecognized\n ``bundleVariant`` SHOULD alert the user that the transaction affects a pool the\n wallet is aware of, but in a way the wallet does not fully understand.\n \n-For bundle types not understood by a wallet, the wallet can compute the\n-transaction identifier so long as it has been provided with the 32-byte\n-``bundle_effects_digest`` value for each bundle that it does not understand.\n-This enables partial verification of transactions containing unknown bundle\n-types.\n+For each unsupported ``(bundleType, bundleVariant)`` pair in\n+``mEffectBundles``, a wallet needs the corresponding 32-byte\n+``bundle_effects_digest`` value in order to compute the transaction identifier.\n+This includes an unsupported variant of a recognized bundle type. For each\n+such bundle, the wallet uses the supplied digest as T.3c, together with the\n+``bundleType`` and ``bundleVariant`` encoded in that transaction's\n+``mEffectBundles`` entry, in `T.3: effects_bundles_digest`_. It computes the\n+digests of supported bundles normally and includes all bundles in transaction\n+order. The header and complete value pool delta map are hashed as specified\n+in T.1 and T.2. Thus access to the unsupported bundles' digests enables the\n+wallet to compute the transaction identifier without interpreting those\n+bundles' internal payloads. How those digests are made available to the\n+wallet is outside the scope of this ZIP.\n+\n+The supplied digests act as commitments to subtrees that the wallet does not\n+need to compute. If the resulting transaction identifier matches a trusted\n+transaction identifier, this authenticates the effecting data the wallet\n+understands, the header, and the value pool delta map. Without a trusted\n+transaction identifier, computing the correct identifier instead depends on\n+the supplied bundle digests being correct.\n+\n+Recognizing a bundle type identifies the affected pool, but does not imply\n+that the wallet can detect or spend notes created by an unsupported variant.\n \n A wallet MUST NOT construct or sign a transaction containing a bundle type or\n variant that it does not fully understand.\n@@ -991,14 +1203,44 @@ defines the bundle types that this ZIP registers: the encoding of their\n effecting and authorizing data, the sighash algorithm versions available to\n their signers, and their contributions to the digests defined in\n `Digest Algorithms`_. A ZIP that registers a further bundle type defines the\n-same things for it, as required by `Bundle Type Registration`_.\n+same things for it, as required by `Bundle Type Registration`_. In particular,\n+each bundle type and variant defines when its entries are present or omitted;\n+the canonical omission rules below apply to the bundle kinds that specify them.\n+\n+For the bundle types defined by this ZIP, the corresponding authorizing\n+sequences take their lengths from the effecting data:\n+\n+* ``tx_in_auth`` has ``tx_in_count`` elements;\n+* ``vSpendProofsSapling`` and ``vSpendAuthSigsSapling`` each have\n+  ``nSpendsSapling`` elements;\n+* ``vOutputProofsSapling`` has ``nOutputsSapling`` elements;\n+* in each Orchard protocol bundle, ``vSpendAuthSigsOrchard`` has\n+  ``nActionsOrchard`` elements.\n+\n+Decoding a recognized ``vBundleData`` payload MUST consume exactly\n+``nBundleDataLen`` bytes; missing fields, truncated fields, or trailing bytes\n+MUST cause rejection. Unsupported payloads are treated as opaque byte arrays\n+of their declared lengths.\n \n Transparent Bundle\n ``````````````````\n \n The transparent bundle's value pool delta in ``mValuePoolDeltas`` represents\n the net value flowing from the transparent inputs to the transparent outputs.\n \n+An entry for this bundle in ``mEffectBundles`` MUST contain at least one\n+transparent input or output. If both ``tx_in_count`` and ``tx_out_count``\n+would be zero, the effecting bundle MUST be omitted instead. An entry in\n+``mAuthBundles`` MUST NOT be present when ``tx_in_count = 0``; the empty\n+authorizing bundle is omitted.\n+\n+For ``bundleVariant = 0``, all value pool delta entries for this bundle MUST\n+have ``assetClass = 0`` (ZEC). If there is no transparent effecting bundle,\n+its ZEC value pool delta MUST be zero. If ``tx_in_count > 0``, a corresponding\n+entry in ``mAuthBundles`` MUST be present, and the scripts MUST authorize each\n+input under the existing transparent script validation rules, using the\n+signature digest specified in `Transparent Sighash Algorithms`_.\n+\n A full validator MUST verify that the ZEC value pool delta for\n ``bundleType = 0`` equals the total value of the transparent inputs minus the\n total value of the transparent outputs. (The input values are not encoded in\n@@ -1100,8 +1342,7 @@ Transparent Sighash Algorithms\n \n For the transparent bundle at ``bundleVariant = 0``, sighash version 0 is the\n only version defined, and its ``associatedData`` is the empty byte string. A\n-signature made with it commits to the digest defined in\n-`S.3.0: transparent_sig_digest`_.\n+signature made with it commits to the digest defined in `v0 Signature Digest`_.\n \n Transparent Digests\n '''''''''''''''''''\n@@ -1120,10 +1361,8 @@ The personalization field of this hash is set to::\n \n   \"ZTxIdTranspaHash\"\n \n-In the case that the transaction has no transparent components,\n-``transparent_effects_digest`` is::\n-\n-  BLAKE2b-256(\"ZTxIdTranspaHash\", [])\n+If there are no transparent inputs or outputs, there is no transparent\n+effecting bundle and no entry for it in ``effects_bundles_digest``.\n \n T.3.0a: prevouts_digest\n ~~~~~~~~~~~~~~~~~~~~~~~\n@@ -1247,7 +1486,15 @@ If the ``SIGHASH_ANYONECANPAY`` flag is set::\n S.3.0e: sequence_sig_digest\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-Identical to ``sequence_digest`` (T.3.0b) regardless of ``hash_type``.\n+If the ``SIGHASH_ANYONECANPAY`` flag is not set, identical to\n+``sequence_digest`` (T.3.0b).\n+\n+Otherwise::\n+\n+  BLAKE2b-256(\"ZTxIdSequencHash\", [])\n+\n+The signing input's own ``nSequence`` is committed to by ``txin_sig_digest``\n+(S.3.0g), regardless of ``hash_type``.\n \n S.3.0f: outputs_sig_digest\n ~~~~~~~~~~~~~~~~~~~~~~~~~~\n@@ -1256,7 +1503,11 @@ If the sighash type is neither ``SIGHASH_SINGLE`` nor ``SIGHASH_NONE``, identica\n to ``outputs_digest`` (T.3.0c).\n \n If the sighash type is ``SIGHASH_SINGLE`` and a transparent output exists at the\n-same index as the input being signed, a hash of that output's encoding.\n+same index as the input being signed, a BLAKE2b-256 hash of that output's encoding.\n+\n+The personalization field of this hash is set to::\n+\n+  \"ZTxIdOutputsHash\"\n \n Otherwise::\n \n@@ -1297,15 +1548,14 @@ The personalization field of this hash is set to::\n \n   \"ZTxAuthTransHash\"\n \n-In the case that the transaction has no transparent inputs::\n-\n-  BLAKE2b-256(\"ZTxAuthTransHash\", [])\n+If there are no transparent inputs, there is no transparent authorizing\n+bundle and no entry for it in ``auth_bundles_digest``.\n \n \n Coinbase Bundle\n ```````````````\n \n-A transaction is a *coinbase transaction* if and only if ``mEffectBundles``\n+A V7 transaction is a *coinbase transaction* if and only if ``mEffectBundles``\n contains an entry with ``bundleType = 1``. The coinbase bundle replaces the\n otherwise-unspendable transparent input that identified a coinbase transaction\n in previous transaction versions, and carries the block height that that input\n@@ -1373,6 +1623,23 @@ no coinbase bundle contributes no entry for it to ``effects_bundles_digest``.\n Sapling Bundle\n ``````````````\n \n+An entry for this bundle in ``mEffectBundles`` MUST contain at least one\n+Sapling Spend or Output description. If both ``nSpendsSapling`` and\n+``nOutputsSapling`` would be zero, the bundle MUST be omitted instead.\n+\n+For ``bundleVariant = 0``, all value pool delta entries for this bundle MUST\n+have ``assetClass = 0`` (ZEC). The Sapling balancing value is\n+$\\mathsf{mValuePoolDeltas}[(2, \\mathsf{Zec})]$, with the sign convention of\n+the 'Balance and Binding Signature (Sapling)' section. [#protocol-saplingbalance]_\n+If there is no Sapling effecting bundle, this value MUST be zero.\n+\n+Every Sapling effecting bundle MUST have a corresponding entry in\n+``mAuthBundles``. A full validator MUST verify the Spend and Output proofs,\n+Spend authorizing signatures, and binding signature under the existing\n+Sapling consensus rules. In particular, the binding validating key MUST be\n+computed using the balancing value above, and the signature messages are\n+those specified in `Sapling Sighash Algorithms`_.\n+\n Sapling Effecting Data\n ''''''''''''''''''''''\n \n@@ -1505,15 +1772,8 @@ The personalization field of this hash is set to::\n \n   \"ZTxIdSaplingH_v7\"\n \n-Note that unlike ZIP 244, neither the value balance nor the anchor is included\n-here. The value balance is committed via ``value_pool_deltas_digest``, and the\n-anchor via ``sapling_auth_digest``; the personalization differs from ZIP 244's\n-``ZTxIdSaplingHash`` because what is directly hashed has changed.\n-\n-In the case that the transaction has no Sapling spends or outputs,\n-``sapling_effects_digest`` is::\n-\n-  BLAKE2b-256(\"ZTxIdSaplingH_v7\", [])\n+If there are no Sapling spends or outputs, there is no Sapling effecting\n+bundle and no entry for it in ``effects_bundles_digest``.\n \n T.3.2a: sapling_spends_digest\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n@@ -1550,15 +1810,12 @@ A BLAKE2b-256 hash of the non-nullifier information for all Sapling spends\n belonging to the transaction. For each spend, the following elements are\n included in the hash::\n \n-   T.3.2a.ii.1: cv     (32 bytes)\n-   T.3.2a.ii.2: anchor (32 bytes)\n-   T.3.2a.ii.3: rk     (32 bytes)\n-\n-The anchor is hashed for *each* spend (even though it is shared in the encoding).\n+   T.3.2a.ii.1: cv (32 bytes)\n+   T.3.2a.ii.2: rk (32 bytes)\n \n The personalization field of this hash is set to::\n \n-  \"ZTxIdSSpendNHash\"\n+  \"ZTxIdSSpendNH_v6\"\n \n T.3.2b: sapling_outputs_digest\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n@@ -1639,12 +1896,8 @@ The personalization field of this hash is set to::\n \n   \"ZTxAuthSapliH_v7\"\n \n-The personalization differs from ZIP 244's ``ZTxAuthSapliHash`` because this\n-digest now also commits to the anchor.\n-\n-In the case that the transaction has no Sapling spends or outputs::\n-\n-  BLAKE2b-256(\"ZTxAuthSapliH_v7\", [])\n+If there are no Sapling spends or outputs, there is no Sapling authorizing\n+bundle and no entry for it in ``auth_bundles_digest``.\n \n \n Orchard Protocol Bundles\n@@ -1656,6 +1909,24 @@ upon by its own bundle type: the Orchard bundle, ``bundleType = 3``, acts on\n the *Orchard pool*, and the Ironwood bundle, ``bundleType = 4``, acts on the\n *Ironwood pool*.\n \n+An entry for either bundle type in ``mEffectBundles`` MUST contain at least\n+one Action description (``nActionsOrchard > 0``). If there would be no Actions,\n+the bundle MUST be omitted instead.\n+\n+For ``bundleVariant = 0``, all value pool delta entries for either bundle type\n+MUST have ``assetClass = 0`` (ZEC). For a bundle with type identifier\n+$\\mathsf{b}$, its pool's balancing value is\n+$\\mathsf{mValuePoolDeltas}[(\\mathsf{b}, \\mathsf{Zec})]$, with the sign convention\n+of the 'Balance and Binding Signature (Orchard)' section. [#protocol-orchardbalance]_\n+If there is no effecting bundle for a pool, its balancing value MUST be zero.\n+\n+Every Orchard or Ironwood effecting bundle MUST have a corresponding entry in\n+``mAuthBundles``. A full validator MUST verify the Action proof, Spend\n+authorizing signatures, and binding signature under the existing consensus\n+rules for that pool. In particular, the binding validating key MUST be\n+computed using that pool's balancing value above, and the signature messages\n+are those specified in `Orchard Protocol Sighash Algorithms`_.\n+\n Both bundle types use the encoding defined in this section. Where a field is\n described below as belonging to the bundle's pool, that pool is the\n *Orchard pool* for an Orchard bundle and the *Ironwood pool* for an Ironwood\n@@ -1799,15 +2070,8 @@ The personalization field of this hash is set to::\n \n   \"ZTxIdOrchardH_v7\"\n \n-Note that unlike ZIP 244, neither the value balance nor the anchor is included\n-here. The value balance is committed via ``value_pool_deltas_digest``, and the\n-anchor via ``orchard_auth_digest``; the personalization differs from ZIP 244's\n-``ZTxIdOrchardHash`` because what is directly hashed has changed.\n-\n-In the case that the transaction has no Orchard actions, ``orchard_effects_digest``\n-is::\n-\n-  BLAKE2b-256(\"ZTxIdOrchardH_v7\", [])\n+If there are no Orchard actions, there is no Orchard effecting bundle and\n+no entry for it in ``effects_bundles_digest``.\n \n T.3.3a: orchard_actions_compact_digest\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n@@ -1864,20 +2128,15 @@ each personalization string is replaced as follows:\n +=====================================+======================+======================+\n | effects digest                      |``ZTxIdOrchardH_v7``  |``ZTxIdIronwd_Hash``  |\n +-------------------------------------+----------------------+----------------------+\n-| actions compact digest              |``ZTxIdOrcActCHash``  |``ZTxIdIrnActCHash``  |\n+| actions compact digest              |``ZTxIdOrcActCHash``  |``ZTxIdIrnActCH_v6``  |\n +-------------------------------------+----------------------+----------------------+\n-| actions memos digest                |``ZTxIdOrcActMHash``  |``ZTxIdIrnActMHash``  |\n+| actions memos digest                |``ZTxIdOrcActMHash``  |``ZTxIdIrnActMH_v6``  |\n +-------------------------------------+----------------------+----------------------+\n-| actions noncompact digest           |``ZTxIdOrcActNHash``  |``ZTxIdIrnActNHash``  |\n+| actions noncompact digest           |``ZTxIdOrcActNHash``  |``ZTxIdIrnActNH_v6``  |\n +-------------------------------------+----------------------+----------------------+\n \n-The personalization strings differ so that the digest of a bundle acting on one\n-pool cannot be reused as the digest of a bundle acting on the other.\n-\n-In the case that the transaction has no Ironwood actions,\n-``ironwood_effects_digest`` is::\n-\n-  BLAKE2b-256(\"ZTxIdIronwd_Hash\", [])\n+If there are no Ironwood actions, there is no Ironwood effecting bundle and\n+no entry for it in ``effects_bundles_digest``.\n \n A.1.3: orchard_auth_digest\n ..........................\n@@ -1897,12 +2156,8 @@ The personalization field of this hash is set to::\n \n   \"ZTxAuthOrchaH_v7\"\n \n-The personalization differs from ZIP 244's ``ZTxAuthOrchaHash`` because this\n-digest now also commits to the anchor.\n-\n-In the case that the transaction has no Orchard actions::\n-\n-  BLAKE2b-256(\"ZTxAuthOrchaH_v7\", [])\n+If there are no Orchard actions, there is no Orchard authorizing bundle and\n+no entry for it in ``auth_bundles_digest``.\n \n A.1.4: ironwood_auth_digest\n ...........................\n@@ -1914,9 +2169,8 @@ that the personalization field of the hash is set to::\n \n   \"ZTxAuthIrnwdHash\"\n \n-In the case that the transaction has no Ironwood actions::\n-\n-  BLAKE2b-256(\"ZTxAuthIrnwdHash\", [])\n+If there are no Ironwood actions, there is no Ironwood authorizing bundle and\n+no entry for it in ``auth_bundles_digest``.\n \n \n Rationale\n@@ -1958,12 +2212,20 @@ separate types also keep the two value pool deltas separable, which a client\n needs in order to report the two chain value pool balances independently.\n \n The Sapling and Orchard protocol anchors are authorizing data rather than\n-effecting data, following ZIP 229 [#zip-0229]_. An anchor selects the note\n-commitment tree state that the bundle's proofs are verified against, which is a\n-property of how the spends are authorized rather than of what the transaction\n-does. Placing it there lets a bundle be re-anchored to a more recent root\n-without changing the transaction identifier, while the proofs still bind to the\n-anchor actually used.\n+effecting data. An anchor selects the note commitment tree state that the\n+bundle's proofs are verified against, which is a property of how the spends\n+are authorized rather than of what the transaction does. Placing it there\n+lets a bundle be re-anchored to a more recent root without changing the\n+transaction identifier, while the proofs still bind to the anchor actually\n+used.\n+\n+Bundle digests retain the input formats and personalizations of ZIP 229\n+[#zip-0229]_ wherever the committed data is unchanged. Effects roots have\n+different personalizations because value balances are committed separately\n+in the value pool delta map. Authorizing digests have different\n+personalizations because signatures include ``sighashInfo`` and anchors\n+precede the proofs and signatures. Orchard and Ironwood use distinct\n+personalizations to separate commitments for their respective pools.\n \n The coinbase bundle also gives the coinbase metadata a place of its own. In\n previous transaction versions that metadata was encoded as the ``scriptSig`` of\n@@ -1992,9 +2254,6 @@ Reference implementation\n ========================\n \n \n-Open issues\n-===========\n-\n Notes from design sessions\n ==========================\n \n@@ -2178,35 +2437,6 @@ Questions\n     * When sending, you need to understand all bundles you are including,\n       and then you can calculate the fee.\n \n-* Can wallets still compute the txid and wtxid of an arbitrary transaction?\n-\n-  * Yes, provided that effecting and authorizing data is separated. Then they\n-    can hash the effecting data even without understanding it to compute the\n-    txid, and they can hash the authorizing data even without understanding\n-    it to compute the authorizing data commitment part of the wtxid (ZIP\n-    239 [#zip-0239]_).\n-  * *However*, this reintroduces a more direct linkage between the [w]txid\n-    computation and the transaction encoding. It's arguably fine, and\n-    potentially simpler -- since the [w]txid computation need not change at\n-    all for most protocol changes.\n-  * If the hashing uses flat hashes over the effectingData and\n-    authorizingData of each bundle (which it has to because the internal\n-    structure is not known), then it might be more difficult to do Merkle\n-    proofs over subsets of the data within a bundle. We haven't used that so\n-    far; is it really needed?\n-\n-  * Should the authorizing data be entirely separate from the effecting data,\n-    and encoded at the end of the transaction in a batch, so that pruning is\n-    simply truncation?\n-\n-* The light wallet protocol will be updated to allow the client to specify the\n-  set of bundle types that the client understands. In the case that this\n-  information is provided, the light client server will then send the root\n-  hashes for each bundle type that the client **does not** understand when\n-  returning raw transaction data, so that the light client can correctly\n-  recompute and validate the txid; also, the compact transactions can be pruned\n-  to exclude bundles of bundle types. that the client will not understand.\n-\n TODO\n ====\n \n@@ -2219,16 +2449,23 @@ References\n .. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words <https://www.rfc-editor.org/info/bcp14>`_\n .. [#protocol] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later <protocol/protocol.pdf>`_\n .. [#protocol-blockchain] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.3: The Block Chain <protocol/protocol.pdf#blockchain>`_\n+.. [#protocol-transactions] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.4: Transactions and Treestates <protocol/protocol.pdf#transactions>`_\n .. [#protocol-networks] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#protocol-saplingbalance] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Balance and Binding Signature (Sapling) <protocol/protocol.pdf#saplingbalance>`_\n+.. [#protocol-orchardbalance] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later. Balance and Binding Signature (Orchard) <protocol/protocol.pdf#orchardbalance>`_\n+.. [#protocol-chainvaluepoolbalances] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later. Chain Value Pool Balances <protocol/protocol.pdf#chainvaluepoolbalances>`_\n+.. [#protocol-txnconsensus] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.1.2: Transaction Consensus Rules <protocol/protocol.pdf#txnconsensus>`_\n .. [#protocol-subsidies] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.8: Block Subsidy and Founders' Reward <protocol/protocol.pdf#subsidies>`_\n .. [#protocol-fundingstreams] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.10: Payment of Funding Streams, Deferred Lockbox, and Lockbox Disbursement <protocol/protocol.pdf#fundingstreams>`_\n-.. [#zip-0203] `ZIP 203: Transaction Expiry <https://zips.z.cash/zip-0203>`_\n-.. [#zip-0212] `ZIP 212: Allow Recipient to Derive Ephemeral Secret from Note Plaintext <https://zips.z.cash/zip-0212>`_\n-.. [#zip-0225] `ZIP 225: Version 5 Transaction Format <https://zips.z.cash/zip-0225>`_\n+.. [#zip-0203] `ZIP 203: Transaction Expiry <zip-0203.rst>`_\n+.. [#zip-0212] `ZIP 212: Allow Recipient to Derive Ephemeral Secret from Note Plaintext <zip-0212.rst>`_\n+.. [#zip-0225] `ZIP 225: Version 5 Transaction Format <zip-0225.rst>`_\n .. [#zip-0229] `ZIP 229: Version 6 Transaction Format <zip-0229.md>`_\n-.. [#zip-0239] `ZIP 239: Relay of Version 5 Transactions <https://zips.z.cash/zip-0239>`_\n-.. [#zip-0244] `ZIP 244: Transaction Identifier Non-Malleability <https://zips.z.cash/zip-0244>`_\n-.. [#zip-0307] `ZIP 307: Light Client Protocol for Payment Detection <https://zips.z.cash/zip-0307>`_\n+.. [#zip-0233] `ZIP 233: Network Sustainability Mechanism: Removing Funds From Circulation <zip-0233.md>`_\n+.. [#zip-0239] `ZIP 239: Relay of Version 5 Transactions <zip-0239.rst>`_\n+.. [#zip-0244] `ZIP 244: Transaction Identifier Non-Malleability <zip-0244.rst>`_\n+.. [#zip-0307] `ZIP 307: Light Client Protocol for Payment Detection <zip-0307.rst>`_\n .. [#zip-2001] `ZIP 2001: Lockbox Funding Streams <zip-2001.rst>`_\n+.. [#zip-2002] `ZIP 2002: Explicit Fees <zip-2002.rst>`_\n .. [#zip-2006] `ZIP 2006: Restricting Transfers into the Orchard Pool <zip-2006.md>`_\n "},{"filename":"zips/zip-2002.rst","status":"modified","additions":36,"deletions":5,"patch":"@@ -127,11 +127,42 @@ In § 7.1 'Transaction Encoding and Consensus' [#protocol-txnconsensus]_, add:\n Modifications to Digest Algorithms\n ----------------------------------\n \n-The fee amount is committed to the transaction identifier and signature\n-digest via the ``value_pool_deltas_digest`` defined in ZIP 248 [#zip-0248]_.\n-Since the fee bundle (bundle type 5) has no effecting data and no authorizing\n-data, its only contribution to the transaction digest is through its entry\n-in ``mValuePoolDeltas``.\n+The fee amount is committed to the transaction identifier via\n+``value_pool_deltas_digest`` and to the v0 signature digest via\n+``value_pool_deltas_sig_digest``, as defined in ZIP 248 [#zip-0248]_. The fee\n+bundle's entry in ``mValuePoolDeltas``, if present, MUST be included in the\n+latter digest regardless of the transparent signature's ``hash_type``. The\n+absence of this entry commits to a zero fee. Since the fee bundle (bundle\n+type 5) has no effecting data and no authorizing data, its only contribution\n+to these digests is through its entry in ``mValuePoolDeltas``.\n+\n+The ``SIGHASH_ANYONECANPAY`` flag therefore permits other transparent inputs\n+to change, subject to the transaction's balance rules, but does not permit\n+the fee to change. In particular, ``SIGHASH_ALL | SIGHASH_ANYONECANPAY``\n+commits to all transparent outputs and to the explicit fee; adding input\n+value cannot increase the fee without invalidating the signature.\n+``SIGHASH_NONE`` and ``SIGHASH_SINGLE`` also retain the fee commitment; any\n+changes to unsigned transparent outputs remain subject to the fixed fee\n+and all other bundles' value pool deltas.\n+\n+Rationale for the Explicit Fee Commitment\n+`````````````````````````````````````````\n+\n+.. raw:: html\n+\n+   <details>\n+   <summary>Click to show/hide</summary>\n+\n+Before V7, the fee was implicit: additional transparent input value not\n+allocated to an output increased the fee. The explicit fee is instead an\n+output of the transaction that every v0 signature commits to. Transparent\n+``hash_type`` rules affect only the transparent bundle, not the separate fee\n+bundle. Thus ``SIGHASH_ANYONECANPAY`` allows changes to funding inputs, but\n+not overpayment by increasing the agreed fee.\n+\n+.. raw:: html\n+\n+   </details>\n \n \n Applicability"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0233.md","title":"ZIP 248 fixes","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"changes-to-zip-248","text":"Changes to ZIP 248"},{"level":2,"id":"nsm-amount","text":"NSM Amount"},{"level":2,"id":"changes-to-zip-237","text":"Changes to ZIP 237"},{"level":2,"id":"changes-to-the-zcash-protocol-specification","text":"Changes to the Zcash Protocol Specification"},{"level":2,"id":"modifications-to-digest-algorithms","text":"Modifications to Digest Algorithms"},{"level":2,"id":"applicability","text":"Applicability"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"new-transaction-field-for-amount-to-remove-from-circulation","text":"New transaction field for amount to remove from circulation"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1441,"title":"[ZIP 316] Add Seed Fingerprint and Key Derivation Paths metadata (Typecodes 0xC1, 0xC2)","author":"nuttycom","createdAt":"2026-11-05T19:22:48Z","updatedAt":"2026-11-05T20:13:40Z","url":"https://github.com/zcash/zips/pull/1441","diffUrl":"https://github.com/zcash/zips/pull/1441.diff","headRef":"zip-0316-key-origin-metadata","headRepo":"zcash/zips","headSha":"9e64fe30dee49fae0eeff7571ba150b38ecd52b9","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #1440 Depends on #1402 and is stacked on its branch. This PR uses the next free Typecodes after #1402's `0xC0`. It also relies on two rule changes in #1402: the narrowed `zu` Registry rule, and Revision 0 derivation when only non-MUST-understand Metadata is present. Adds two non-MUST-understa","zipRefs":[316,32],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0316.rst","status":"modified","additions":150,"deletions":4,"patch":"@@ -21,9 +21,9 @@\n Terminology\n ===========\n \n-The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"RECOMMENDED\", and \"MAY\" in this\n-document are to be interpreted as described in BCP 14 [#BCP14]_ when, and only\n-when, they appear in all capitals.\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"RECOMMENDED\", and\n+\"MAY\" in this document are to be interpreted as described in BCP 14 [#BCP14]_\n+when, and only when, they appear in all capitals.\n \n The terms below are to be interpreted as follows:\n \n@@ -1021,6 +1021,144 @@ Address Expiry Height would direct the Consumer to begin scanning after all\n such transactions. Such a viewing key is inconsistent and is treated as\n invalid.\n \n+.. raw:: html\n+\n+   </details>\n+\n+Key Derivation Metadata\n+-----------------------\n+\n+A wallet that holds only a UFVK cannot sign transactions, but it can construct\n+them for an external signer (such as a hardware wallet) that holds the\n+corresponding seed. A signer generally identifies the spending key to use for an\n+input by its seed fingerprint and derivation path; for example, these are the\n+contents of the ZIP 32 and BIP 32 derivation fields of a partially created Zcash\n+transaction, and of the key origin fields of BIP 174 [#bip-0174]_. Typecodes\n+$\\mathtt{0xC1}$ and $\\mathtt{0xC2}$ are reserved for optional Metadata Items\n+that carry this information in a UFVK, so that a Consumer can obtain it at the\n+time the UFVK is imported.\n+\n+The value of a $\\mathtt{0xC1}$ (Seed Fingerprint) Item MUST be a 32-byte seed\n+fingerprint, as defined in ZIP 32 [#zip-0032-seed-fingerprints]_, of the master\n+seed from which the FVK Items listed in the $\\mathtt{0xC2}$ Item of the same\n+UFVK are derived.\n+\n+The value of a $\\mathtt{0xC2}$ (Key Derivation Paths) Item is encoded as\n+follows:\n+\n+* $\\mathtt{n\\_entries} : \\mathtt{compactSize}$ — the number of entries,\n+  which MUST be at least $1$.\n+\n+* For each of the $\\mathtt{n\\_entries}$ entries:\n+\n+  * $\\mathtt{fvk\\_typecode} : \\mathtt{compactSize}$ — the Typecode of an FVK\n+    Item in the same UFVK.\n+\n+  * $\\mathtt{depth} : \\mathtt{compactSize}$ — the number of elements in the\n+    derivation path, which MUST be at least $1$.\n+\n+  * $\\mathtt{path} : \\mathtt{uint32[depth]}$ — the child indices of the\n+    derivation path, each encoded as an unsigned 32-bit integer in\n+    little-endian order. An index $i$ with $i \\geq 2^{31}$ denotes the hardened\n+    child index $(i - 2^{31})'$.\n+\n+An entry states that the FVK Item having Typecode $\\mathtt{fvk\\_typecode}$ is\n+the encoding of the key at $\\mathtt{path}$, derived from the master seed whose\n+fingerprint is the value of the $\\mathtt{0xC1}$ Item. $\\mathtt{path}$ is the\n+complete path from the master node of the relevant derivation tree, including\n+the purpose level:\n+\n+* for Typecode $\\mathtt{0x03}$ (Orchard), from $m_\\mathsf{Orchard}$ as\n+  defined in ZIP 32 [#zip-0032-orchard-key-path]_; every index in the path\n+  MUST be hardened;\n+\n+* for Typecode $\\mathtt{0x02}$ (Sapling), from $m_\\mathsf{Sapling}$ as\n+  defined in ZIP 32 [#zip-0032-sapling-key-path]_;\n+\n+* for Typecode $\\mathtt{0x00}$ (Transparent P2PKH), from the BIP 32 master\n+  node $m$ [#bip-0032]_.\n+\n+For example, the entries for a UFVK derived at account $a$ according to ZIP 32\n+and BIP 44 have the paths $32' / coin\\_type' / a'$ for the Orchard and Sapling\n+FVK Items, and $44' / coin\\_type' / a'$ for the Transparent P2PKH FVK Item.\n+\n+The following requirements apply:\n+\n+* Entries MUST be in strictly ascending order of $\\mathtt{fvk\\_typecode}$.\n+\n+* $\\mathtt{fvk\\_typecode}$ MUST be $\\mathtt{0x00}$, $\\mathtt{0x02}$, or\n+  $\\mathtt{0x03}$, and the UFVK MUST contain an FVK Item having that Typecode.\n+  A Producer MAY omit the entry for an FVK Item whose derivation it does not\n+  know.\n+\n+* A UFVK that contains a $\\mathtt{0xC1}$ Item MUST contain a $\\mathtt{0xC2}$\n+  Item, and vice versa.\n+\n+* These Items MAY be included in a UFVK of any Revision. They MUST NOT be\n+  included in a UIVK or a Unified Address.\n+\n+* When deriving a UIVK from a UFVK, a Consumer MUST NOT include these Items in\n+  the derived UIVK.\n+\n+* A Consumer that recognises these Items MUST reject a UFVK that violates any\n+  of the above requirements. A Consumer MUST ignore either Item in a UIVK or\n+  Unified Address.\n+\n+A Consumer cannot check these Items against the FVK Items without access to\n+the master seed. Consumers MUST NOT rely on these Items for any security\n+property. Before a Consumer records the derivation of an account as\n+established, or presents the account to the user as linked to a particular\n+signer, it SHOULD confirm the derivation with a signer that holds the seed,\n+for example by obtaining from the signer the FVK at the stated path and\n+comparing it with the corresponding FVK Item.\n+\n+A Producer SHOULD include these Items only in a UFVK that is exported for use\n+by a wallet that will construct transactions for the holder of the seed to\n+sign. A Producer SHOULD NOT include them in a UFVK that is exported only for\n+viewing, for example to an auditor.\n+\n+Rationale for Key Derivation Metadata\n+'''''''''''''''''''''''''''''''''''''\n+\n+.. raw:: html\n+\n+   <details>\n+   <summary>Click to show/hide</summary>\n+\n+Without these Items, a wallet that imports a UFVK has to obtain the seed\n+fingerprint and account index separately, for example through a vendor-specific\n+export format, or by asking the signer each time a transaction is signed.\n+\n+The Items are non-MUST-understand because they neither change the\n+interpretation of the UFVK nor give additional viewing authority: a Consumer\n+that ignores them loses only the ability to construct signing requests without\n+help from the signer. For the same reason they are permitted in `Revision 0`_\n+UFVKs, which a Producer can extend without changing the Human-Readable Part;\n+`Revision 0`_ Consumers are required to ignore Items with Typecodes they do not\n+recognise.\n+\n+The seed fingerprint and the derivation paths are separate Items because one\n+seed fingerprint applies to every FVK Item, while each FVK Item has its own\n+path. The paths are stated in full, rather than as a single account index,\n+because the shielded and transparent trees use different purpose levels, and\n+because some derivations (such as the ``zcashd`` legacy Sapling derivation\n+under account $\\mathtt{0x7FFFFFFF}$ [#zip-0032-sapling-key-path]_) do not\n+follow the account-level convention. The path encoding is the one used by BIP\n+174 key origins [#bip-0174]_.\n+\n+The Items are excluded from UIVKs and Unified Addresses because the holder of\n+an IVK or address cannot construct a spend, and because they would reduce\n+privacy. The seed fingerprint is the same for every account derived from a\n+seed, so any two UFVKs that contain it can be linked to the same seed; the\n+derivation path reveals the account index. A UFVK already gives full viewing\n+authority over its account, but this linkage across accounts is additional\n+information, which is why Producers are advised to include the Items only when\n+they are needed.\n+\n+P2SH FVK Items are not covered, because each key of a multisignature policy\n+can be derived from a different seed. Key origins for such keys would need a\n+per-key encoding, which can be defined in a future revision.\n+\n .. raw:: html\n \n    </details>\n@@ -1074,7 +1212,11 @@ properties of ``zu`` addresses.\n   +-------------------+--------------------------------------+----+--------+--------+\n   | 0xC0              | Viewing Key Birthday Height [2]      | ❌ | ❌     | ❌     |\n   +-------------------+--------------------------------------+----+--------+--------+\n-  | 0xC1..=0xDF       | Unassigned Non-MUST-understand       | ❌ | ❌     | ❌     |\n+  | 0xC1              | Seed Fingerprint [3]                 | ❌ | ❌     | ❌     |\n+  +-------------------+--------------------------------------+----+--------+--------+\n+  | 0xC2              | Key Derivation Paths [3]             | ❌ | ❌     | ❌     |\n+  +-------------------+--------------------------------------+----+--------+--------+\n+  | 0xC3..=0xDF       | Unassigned Non-MUST-understand       | ❌ | ❌     | ❌     |\n   +-------------------+--------------------------------------+----+--------+--------+\n   | 0xE0              | Address Expiry Height                | ❌ | ✅     | ✅     |\n   +-------------------+--------------------------------------+----+--------+--------+\n@@ -1092,6 +1234,8 @@ properties of ``zu`` addresses.\n [2] Permitted only in UFVKs and UIVKs, as specified in\n `Viewing Key Birthday Height Metadata`_.\n \n+[3] Permitted only in UFVKs, as specified in `Key Derivation Metadata`_.\n+\n Additions to this registry are made via 2xxx-series ZIPs. This table will be\n updated by the ZIP Editors when such a ZIP transitions to ``Proposed`` status.\n \n@@ -1665,6 +1809,7 @@ References\n .. [#zip-0032-sapling-internal-key-derivation] `ZIP 32: Shielded Hierarchical Deterministic Wallets — Sapling internal key derivation <zip-0032.rst#sapling-internal-key-derivation>`_\n .. [#zip-0032-orchard-child-key-derivation] `ZIP 32: Shielded Hierarchical Deterministic Wallets — Orchard child key derivation <zip-0032.rst#orchard-child-key-derivation>`_\n .. [#zip-0032-orchard-internal-key-derivation] `ZIP 32: Shielded Hierarchical Deterministic Wallets — Orchard internal key derivation <zip-0032.rst#orchard-internal-key-derivation>`_\n+.. [#zip-0032-seed-fingerprints] `ZIP 32: Shielded Hierarchical Deterministic Wallets — Seed Fingerprints <zip-0032.rst#seed-fingerprints>`_\n .. [#zip-0032-specification-wallet-usage] `ZIP 32: Shielded Hierarchical Deterministic Wallets — Specification: Wallet usage <zip-0032.rst#specification-wallet-usage>`_\n .. [#zip-0032-sapling-key-path] `ZIP 32: Shielded Hierarchical Deterministic Wallets — Sapling key path <zip-0032.rst#sapling-key-path>`_\n .. [#zip-0032-orchard-key-path] `ZIP 32: Shielded Hierarchical Deterministic Wallets — Orchard key path <zip-0032.rst#orchard-key-path>`_\n@@ -1684,6 +1829,7 @@ References\n .. [#bip-0044] `BIP 44: Multi-Account Hierarchy for Deterministic Wallets <https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki>`_\n .. [#bip-0044-path-change] `BIP 44: Multi-Account Hierarchy for Deterministic Wallets — Path levels: Change <https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#change>`_\n .. [#bip-0044-path-index] `BIP 44: Multi-Account Hierarchy for Deterministic Wallets — Path levels: Index <https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#index>`_\n+.. [#bip-0174] `BIP 174: Partially Signed Bitcoin Transaction Format <https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki>`_\n .. [#bip-0350] `BIP 350: Bech32m format for v1+ witness addresses <https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki>`_\n .. [#bip-0380] `BIP 380: Output Script Descriptors General Operation <https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki>`_\n .. [#bip-0388] `BIP 388: Wallet Policies for Descriptor Wallets <https://github.com/bitcoin/bips/blob/master/bip-0388.mediawiki>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0316.rst","title":"Unified Addresses and Unified Viewing Keys","status":"Active, [Revision 1] Withdrawn, [Revision 2] Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":2,"id":"overview","text":"Overview"},{"level":2,"id":"concepts","text":"Concepts"},{"level":2,"id":"addresses","text":"Addresses"},{"level":2,"id":"receivers","text":"Receivers"},{"level":2,"id":"transport-encoding","text":"Transport Encoding"},{"level":2,"id":"transfers","text":"Transfers"},{"level":2,"id":"experimental-usage","text":"Experimental Usage"},{"level":2,"id":"viewing-keys","text":"Viewing Keys"},{"level":2,"id":"open-issues-and-known-concerns","text":"Open Issues and Known Concerns"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"revisions","text":"Revisions"},{"level":2,"id":"encoding-of-unified-addresses","text":"Encoding of Unified Addresses"},{"level":2,"id":"encoding-of-unified-full-incoming-viewing-keys","text":"Encoding of Unified Full/Incoming Viewing Keys"},{"level":3,"id":"rationale-for-address-derivation","text":"Rationale for address derivation"},{"level":2,"id":"requirements-for-both-unified-addresses-and-unified-viewing-keys","text":"Requirements for both Unified Addresses and Unified Viewing Keys"},{"level":3,"id":"rationale-for-dropping-the-at-least-one-shielded-item-restriction-for-uvks","text":"Rationale for dropping the \"at least one shielded Item\" restriction for UVKs"},{"level":3,"id":"rationale-for-removing-transparent-receivers-from-zu-unified-addresses","text":"Rationale for removing Transparent Receivers from zu Unified Addresses"},{"level":3,"id":"rationale-for-item-ordering","text":"Rationale for Item ordering"},{"level":3,"id":"rationale-for-showing-at-least-the-first-20-characters","text":"Rationale for showing at least the first 20 characters"},{"level":2,"id":"adding-new-types","text":"Adding new types"},{"level":2,"id":"metadata-items","text":"Metadata Items"},{"level":3,"id":"rationale-for-making-revision-0-ua-uvks-with-must-understand-typecodes-invalid","text":"Rationale for making Revision 0 UA/UVKs with MUST-understand Typecodes invalid"},{"level":2,"id":"address-expiration-metadata","text":"Address Expiration Metadata"},{"level":3,"id":"rationale","text":"Rationale"},{"level":2,"id":"viewing-key-birthday-height-metadata","text":"Viewing Key Birthday Height Metadata"},{"level":3,"id":"rationale-for-viewing-key-birthday-height-metadata","text":"Rationale for Viewing Key Birthday Height Metadata"},{"level":2,"id":"key-derivation-metadata","text":"Key Derivation Metadata"},{"level":3,"id":"rationale-for-key-derivation-metadata","text":"Rationale for Key Derivation Metadata"},{"level":2,"id":"typecode-registry","text":"Typecode Registry"},{"level":2,"id":"deriving-internal-keys","text":"Deriving Internal Keys"},{"level":2,"id":"deriving-a-uivk-from-a-ufvk","text":"Deriving a UIVK from a UFVK"},{"level":2,"id":"deriving-a-unified-address-from-a-uivk","text":"Deriving a Unified Address from a UIVK"},{"level":2,"id":"usage-of-outgoing-viewing-keys","text":"Usage of Outgoing Viewing Keys"},{"level":2,"id":"jumbling","text":"Jumbling"},{"level":3,"id":"discussion","text":"Discussion"},{"level":3,"id":"solution","text":"Solution"},{"level":3,"id":"usage-for-unified-addresses-ufvks-and-uivks","text":"Usage for Unified Addresses, UFVKs, and UIVKs"},{"level":3,"id":"rationale-for-length-restrictions","text":"Rationale for length restrictions"},{"level":3,"id":"heuristic-analysis","text":"Heuristic analysis"},{"level":3,"id":"efficiency","text":"Efficiency"},{"level":3,"id":"dependencies","text":"Dependencies"},{"level":3,"id":"related-work","text":"Related work"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"changelog","text":"Changelog"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1438,"title":"Add ZIP draft: Zcash Stablecoin Vaults","author":"iJaack","createdAt":"2026-11-05T16:54:31Z","updatedAt":"2026-11-05T17:08:20Z","url":"https://github.com/zcash/zips/pull/1438","diffUrl":"https://github.com/zcash/zips/pull/1438.diff","headRef":"draft-ijaack-zsv","headRepo":"iJaack/zips","headSha":"69f2175f41735c849bcd6cc41bb62226a2f48474","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Adds an unnumbered Draft Consensus ZIP for Zcash Stablecoin Vaults: native ZEC collateral, one public dollar-targeting asset, authenticated prices, liquidation and explicit bad-debt retirement. Public payments are the initial scope; shielded payments require a follow-up ZIP. No arbitrary issuance, r","zipRefs":[],"draftRefs":["draft-ijaack-zsv"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -237,6 +237,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-ijaack-zsv</td> <td class=\"left\"><a href=\"zips/draft-ijaack-zsv.md\">Zcash Stablecoin Vaults</a></td> <td class=\"left\"><a href=\"https://forum.zcashcommunity.com/t/zip-draft-zsv-one-zec-backed-stablecoin-on-zcash/58909\">https://forum.zcashcommunity.com/t/zip-draft-zsv-one-zec-backed-stablecoin-on-zcash/58909</a></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-ijaack-zsv.md","status":"added","additions":972,"deletions":0,"patch":"@@ -0,0 +1,972 @@\n+    ZIP: Unassigned\n+    Title: Zcash Stablecoin Vaults\n+    Owners: Giacomo Barbieri <hey@jaack.me>\n+    Status: Draft\n+    Category: Consensus\n+    Created: 2026-11-05\n+    License: MIT\n+    Discussions-To: <https://forum.zcashcommunity.com/t/zip-draft-zsv-one-zec-backed-stablecoin-on-zcash/58909>\n+    Pull-Request: <https://github.com/zcash/zips/pull/1438>\n+\n+No ZIP number or network upgrade has been assigned. Deployment allocations and outstanding\n+review are listed below.\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"REQUIRED\", \"SHOULD\" and \"MAY\" in this document are to be\n+interpreted as described in BCP 14 [^BCP14] when, and only when, they appear in all capitals.\n+Conformance requirements appear in the Specification section.\n+\n+- **ZSV** means Zcash Stablecoin Vaults.\n+- **zUSD** is the provisional name of the single dollar-targeting stable asset defined here.\n+- **Principal** is debt attached to an active vault.\n+- **Bad debt** is stablecoin liability remaining after a vault closes without burning all its principal.\n+- **Stable supply** includes all unspent zUSD, including coins whose spending keys have been lost.\n+- A **report certificate** is one price report with the required matching reporter signatures.\n+- A **current vault reference** identifies the transaction that created its latest state.\n+\n+# Abstract\n+\n+This proposal adds native ZEC vaults and one protocol-defined stablecoin to Zcash. Borrowing\n+against sufficient collateral creates zUSD; repayment and liquidation destroy existing zUSD and\n+reduce the corresponding liability. Zcash consensus enforces ownership, collateral release,\n+issuance, burns and independent conservation of both assets.\n+\n+The protocol provides no arbitrary asset registration, issuer authority, governance token or\n+general execution environment. It uses a dedicated transaction format, a separate ZEC vault\n+value pool and public zUSD outputs. Existing ZEC transaction formats remain available under\n+their applicable consensus rules. Public vaults and payments expose balances, amounts and\n+financial relationships; shielded zUSD requires a subsequent specified and reviewed protocol.\n+\n+A fixed reporter quorum supplies ZEC/USD observations. Consensus verifies their authentication\n+and deterministic validity conditions, but cannot establish their economic truth. Full\n+liquidation records any uncovered principal as bad debt. Further borrowing stops while bad debt\n+remains; holders can retire that debt by burning existing zUSD without compensation. No\n+dollar-redemption claim or guaranteed market peg is created.\n+\n+The proposal is independent of a Dev Fund allocation or reserve. Those mechanisms may be\n+considered separately. Research code and economic tests support selected design choices; native\n+format, authorization and deployment rules require implementation and independent review.\n+\n+# Motivation\n+\n+ZEC holders may want dollar-denominated liquidity while retaining exposure to their remaining\n+collateral. A native vault would let them borrow under rules checked by Zcash nodes, accepting\n+liquidation risk when collateral loses value. Holders of the issued asset would bear collateral,\n+oracle, liquidity and protocol risk.\n+\n+A data commitment alone cannot lock ZEC or create a native stablecoin. General-purpose Zcash\n+Shielded Assets would support unrelated tokens and issuers. ZSV instead proposes one monetary\n+use case with a closed issuance path. The smaller asset domain reduces issuance cases, but the\n+financial and consensus complexity remains substantial.\n+\n+\n+The intended benefit is native borrowing and a transferable dollar-targeting asset without an\n+arbitrary token-launch interface. Whether that benefit justifies the oracle dependency, privacy\n+cost and additional consensus rules is a question for community review.\n+\n+# Privacy Implications\n+\n+This first version has public vaults and public stablecoin payments. It reveals collateral,\n+principal, owner keys, payment amounts and liquidation events. Creating a vault from previously\n+shielded ZEC requires exposing value to the permitted transparent input path. Key reuse and\n+transaction graph analysis can link borrowing, payments and repayment. Liquidation can reveal\n+financial distress.\n+\n+Adding shielded payments later would not erase these disclosures. A fixed-asset shielded profile\n+could reuse selected ZSA transfer, note and proof techniques, but would need its own precise\n+proof statements, value-pool accounting and privacy review. Existing ZSA audits would not\n+automatically apply to a changed circuit. Private vaults also need liquidation information to\n+remain available without borrower cooperation; this version does not attempt that design.\n+\n+# Requirements\n+\n+The design goals are:\n+\n+- Enforcement of collateral, debt, issuance and liquidation by Zcash consensus, with no bridge, external execution chain or custody committee.\n+- Exactly one additional native asset, issued only through valid collateralized borrowing.\n+- Atomic value accounting, with no alternative ordinary spend of locked collateral and no hidden erasure of stablecoin liabilities.\n+- Repayment, collateral additions, debt-free exit and payment transfers during an oracle outage.\n+- Deterministic branch-specific validation, bounded work and equivalent behavior across implementations.\n+- Continued ordinary ZEC activity without requiring unrelated users to submit ZSV operations.\n+- Defined vault operation without a reserve, Dev Fund allocation or privileged recovery key.\n+\n+# Non-requirements\n+\n+This proposal does not add arbitrary issuers, asset descriptions, NFTs, a VM, a governance\n+token, a bridge or external custody. It does not prevent off-protocol claims, inscriptions,\n+derivatives or speculation. It does not allocate block rewards or NSM value, establish on-demand\n+dollar redemption, or promise that zUSD trades at one dollar.\n+\n+Dev Fund allocation and reserve stabilization are separate candidate work. Shielded payments and\n+wallet address standardization may require companion ZIPs. The oracle rules are part of this\n+core proposal; they are not an optional service that a node may configure differently.\n+\n+# Specification\n+\n+## Scope and activation parameters\n+\n+This specification defines a new, dedicated transaction version with a fixed ZEC/zUSD domain. It\n+does not modify the meaning of an allocated transaction version or enable general ZSA issuance.\n+Deployment allocates its version/group identifiers, consensus branch context, activation heights\n+and fixed oracle roster. Until those allocations and the required implementation/review exist,\n+this is an unactivated proposal.\n+\n+Validators MUST reject the new format before its network-specific activation. After activation,\n+ordinary ZEC transaction versions MUST retain their applicable validation rules. The new format\n+MUST NOT be interpreted as the existing v6 format or as withdrawn ZIP 230. An allocated existing\n+format is not available for reassignment by this draft. The existing format and Zebra\n+integration baseline is recorded in the pinned sources. [^sources]\n+\n+At activation, the ZSV vault set and public zUSD UTXO set are empty, stable supply and bad debt\n+are zero, and no price report is active. Activation MUST NOT create ZEC, zUSD, a reserve balance\n+or a privileged allocation. Borrowing remains unavailable until a valid report certificate is\n+accepted.\n+\n+## Encoding conventions and bounds\n+\n+All integers are unsigned little-endian unless stated otherwise. Hashes are raw 32-byte\n+consensus digest bytes, not reversed display hex. A compressed secp256k1 key K is exactly 33\n+bytes: prefix 02 or 03 followed by the canonical field encoding of a valid non-infinity point's\n+x-coordinate. Invalid/noncanonical keys fail. New asset outputs have no script, arbitrary\n+metadata or asset identifier.\n+\n+Counts are canonical CompactSize. Each count below is at most 64, so its only valid encoding is\n+one byte. Resolved payment inputs and newly created payment outputs have strictly positive\n+values. Zero is permitted only for operation fields that explicitly allow it. Each ZEC payment\n+value, each transaction's ZEC input/output sums and value-balance sides, and the persistent\n+vault-pool balance MUST fit MAX_MONEY = 2,100,000,000,000,000 zatoshi. Each zUSD payment value,\n+each transaction's stable input/output sums and value-balance sides, and the persistent stable\n+supply MUST fit S_MAX = 100,000,000,000 micro-zUSD. These are per-transaction and state bounds;\n+they do not limit the sum of all transfers across a block, which can reuse value through valid\n+same-block dependencies. Signed maximum-burn and minimum-receipt limits are unsigned u64 bounds,\n+not additional input or output value. A serialized ZSV transaction, including authorization, is\n+at most 65,536 bytes. Existing block size limits remain in force. Each block contains at most\n+256 transactions of this new version, at most 1,024 required secp256k1 signature slots across\n+them, and at most 4,096 new native ZEC plus stable output entries across them. Slots count\n+native input signatures, stable input signatures and any operation signature whether valid or\n+not; outputs count both explicit output vectors. The oracle permits at most one certificate and\n+at most 7 Ed25519 report signatures per block. Check these counts before signature verification\n+and state insertion. These are exact proposed limits, not benchmark-calibrated production\n+recommendations. There is no lifetime UTXO cap or permitted-wallet list.\n+\n+The fee/residual calculation uses checked signed arithmetic of sufficient width. Core\n+collateral-ratio and liquidation products use checked u128. A new format has no coinbase input,\n+shielded bundle, JoinSplit, general script vector, asset selector or optional opaque extension\n+field.\n+\n+## Effect body E\n+\n+In the encoding tables, `+` denotes byte concatenation, and fields are listed in wire order.\n+\n+A transaction consists of E followed immediately by its authorization tail A, with no trailing\n+bytes. E is:\n+\n+| Field | Bytes / rule |\n+|---|---|\n+| header | u32: high bit 1; remaining 31 bits equal the unallocated ZSV_VERSION |\n+| versionGroupId | u32: unallocated ZSV_VERSION_GROUP_ID |\n+| consensusBranchId | u32: exactly the active upgrade branch ID |\n+| lockTime | u32, required 0 |\n+| expiryHeight | u32, H<=expiryHeight<500,000,000 and expiryHeight>0, where H is inclusion height |\n+| nZecInputs | CompactSize,0..64 |\n+| zecInputs | nZecInputs*(prevTxid32 + prevOutputIndexu32) |\n+| nZecOutputs | CompactSize,0..64 |\n+| zecOutputs | nZecOutputs*(valueu64 + recipientKey33) |\n+| nStableInputs | CompactSize,0..64 |\n+| stableInputs | nStableInputs*(prevTxid32 + prevOutputIndexu32) |\n+| nStableOutputs | CompactSize,0..64 |\n+| stableOutputs | nStableOutputs*(valueu64 + recipientKey33) |\n+| operationTag | u8, one of 0..5 below |\n+| operationPayload | exactly the tag's payload |\n+| hasOracle | u8, exactly 0 or 1 |\n+| oracleEffects | absent if 0; if 1 the exact 45-byte certificate effects below |\n+\n+Native input sequence numbers are implicitly final (0xffffffff); they have no wire field. Native\n+outputs create ordinary transparent ZEC UTXOs with the exact standard script\n+`0x21||recipientKey33||0xac` (P2PK). The input must resolve to a real unspent transparent output\n+whose locking script matches that exact P2PK grammar. Its value comes from native branch state,\n+not transaction data. Existing coinbase-maturity, duplicate-spend, money-range and other\n+applicable native input rules continue to apply. Existing ZEC transaction versions and their\n+accepted scripts remain unchanged; users may send ZEC to a P2PK output through an ordinary\n+transaction before using it here.\n+\n+Stable outputs create entries only in the new zUSD payment UTXO namespace. They are not native\n+ZEC transparent outputs. Stable input indices resolve only in that namespace. Native and stable\n+`(txid,index)` namespaces are distinct: an equal byte tuple does not make the two assets\n+interchangeable. Every input must exist, be unspent on the exact candidate branch, have an\n+in-range positive value and have the expected fixed-key ownership. Repeated outpoints within a\n+namespace fail. Same-block dependencies use previously validated transaction outputs in block\n+order.\n+\n+Only one optional operation is allowed. Tag 0 means NONE and has no payload; it permits pure\n+stable transfers. If hasOracle=0 and tag=NONE, at least one stable input is required. If\n+hasOracle=1, tag must be NONE and both stable counts must be 0. Native fee inputs and outputs\n+remain permitted in an oracle transaction. Other empty uses of the new version fail; ordinary\n+ZEC transactions continue using their existing formats.\n+\n+## Vault state and operation payloads\n+\n+A current vault is stored in a dedicated ZEC vault value pool, keyed by its `VaultRef`, the raw\n+32-byte effects txid of the OPEN or UPDATE that created it. Because each transaction has at most\n+one operation, no operation index is needed. The state record is `(ownerKey33, collateralu64,\n+principalu64)`. OPEN/UPDATE create one successor at their own txid; UPDATE consumes the previous\n+ref and preserves its owner. CLOSE/LIQUIDATE consume without a successor. Current refs are\n+unique; inserting an existing current ref fails.\n+\n+Vault collateral is not also an ordinary transparent output. There is no OP_TRUE collateral\n+script and no spendable duplicate. Its amount enters the native ZEC vault value pool and can\n+leave only through a valid operation and the fee equation below. All node paths need this\n+accounting, including direct finalization/checkpoints; ordinary native spends can resolve only\n+actual native UTXOs, never the vault pool.\n+\n+| Tag | Payload |\n+|---|---|\n+|0 NONE|empty|\n+|1 OPEN|ownerKey33 + newCollateralu64 + newPrincipalu64 + oracleID32|\n+|2 UPDATE|priorVaultRef32 + newCollateralu64 + newPrincipalu64 + oracleID32 + ownerPayoutIndexu32|\n+|3 CLOSE|priorVaultRef32 + ownerPayoutIndexu32|\n+|4 LIQUIDATE|priorVaultRef32 + oracleID32 + maxBurnu64 + minCollateralu64 + liquidatorKey33 + ownerPayoutIndexu32 + liquidatorPayoutIndexu32|\n+|5 RETIRE_BAD_DEBT|burnAmountu64|\n+\n+OracleID is the exact identifier of the block's parent-selected pricing observation defined by\n+the oracle specification. For OPEN with D=0, and UPDATE without a price requirement, oracleID is\n+exactly 32 zero bytes. A price-requiring operation uses the active nonzero ID; no alternate or\n+stale observation is accepted. This prevents an irrelevant observation from needlessly\n+restricting oracle-free repayment/deposit, and makes its wire encoding unique.\n+\n+OPEN requires C>0. UPDATE requires C'>0 and a changed `(C,D)` pair; `(0,0)` uses CLOSE. Nonzero\n+D' must satisfy the minimum-debt rule. The owner auth key for OPEN is its encoded key;\n+UPDATE/CLOSE use the actual prior state's owner. LIQUIDATE uses its encoded liquidator key.\n+RETIRE has no operation-owner signature: actual stable input authorization controls the burn.\n+\n+A mandatory payout index names an ordinary native ZEC output in this same transaction. The named\n+output must have exactly the required amount and exact P2PK recipient. UPDATE with C'<C requires\n+a payout of C-C' to the immutable owner. UPDATE without released collateral uses the sentinel\n+0xffffffff. CLOSE pays exactly oldC to the owner. Every valid current vault has positive\n+collateral, so this payout always uses an output index. LIQUIDATE pays z to the liquidator and\n+C-z to the owner. A zero required payout uses sentinel 0xffffffff and consumes no output. A\n+positive payout requires an index in range; sentinel is invalid. Both positive liquidation\n+payout indices must be distinct, even for self-liquidation when ownerKey=liquidatorKey. One\n+output cannot satisfy two mandatory payouts. Extra native outputs remain allowed and are covered\n+by all signatures and value conservation.\n+\n+The exact collateral payouts cannot silently pay fees. Any positive residual fee must be\n+supplied by other actual native value (normally free ZEC inputs). Minted stable outputs may use\n+any valid recipient keys chosen in the fully signed transaction: borrowing and sending value to\n+another recipient can be atomic. No amount is inferred from a memo or claimed caller balance.\n+\n+## Per-asset value equations\n+\n+Define C_before as the actual prior vault collateral, or 0 for NONE/OPEN/RETIRE; C_after is\n+OPEN/UPDATE's encoded collateral, or 0 for NONE/CLOSE/LIQUIDATE/RETIRE. Let Mint and Burn come\n+only from the validated core operation:\n+\n+- OPEN: Mint=D', Burn=0.\n+- UPDATE: Mint=max(D'-D,0), Burn=max(D-D',0).\n+- CLOSE: Mint=0, Burn=D (close can atomically repay all principal).\n+- LIQUIDATE: Mint=0, Burn=x from the specified exact liquidation formula.\n+- RETIRE_BAD_DEBT: Mint=0, Burn=r, with 0 < r <= B at transaction entry.\n+- NONE: Mint=Burn=0.\n+\n+Then require:\n+\n+```\n+sum(stableInputs) + Mint = sum(stableOutputs) + Burn\n+Fee = sum(nativeZecInputs) + C_before - C_after - sum(nativeZecOutputs)\n+0 <= Fee <= MAX_MONEY\n+```\n+\n+The fee is counted in ordinary ZEC fee/coinbase accounting, including any separately active\n+fee-disposition rules. Stable value cannot pay it. These rules do not introduce a positive\n+consensus minimum fee; relay/mining fee policy remains separate. The vault value pool changes by\n+C_after-C_before, and total stable supply changes by Mint-Burn. Live principal changes and\n+bad-debt handling follow the core; RETIRE decreases B and S together without touching active\n+collateral or principal. Borrowing requires B=0 at transaction entry, so it cannot be\n+self-financed by another operation in the same transaction (and only one operation is allowed).\n+\n+## Hashes and authorization\n+\n+Let `H(label,bytes)` be BLAKE2b-256 with the ASCII label right-padded with zero bytes to a\n+16-byte personalization. All labels below are exact and case-sensitive; no NUL is appended to\n+message bytes. Let G be the raw 32-byte genesis block hash of the configured network. G comes\n+from trusted network configuration, never transaction data. Hashing APIs therefore need explicit\n+network context; branch IDs alone do not distinguish Mainnet from Testnet. The header already\n+commits the branch ID.\n+\n+```\n+txid = H(\"ZSV_TxId_v1\", G || E)\n+contextHash = H(\"ZSV_Context_v1\", R)\n+signatureDigest(role,index) = H(\"ZSV_Sign_v1\", G || txid || contextHash || u8(role) || u16LE(index))\n+authDigest = H(\"ZSV_Auth_v1\", txid || A)\n+```\n+\n+R is authoritative resolved input context, encoded in this exact order:\n+\n+```\n+CompactSize(nZecInputs)\n+  for each native input, in wire order:\n+    prevTxid32 || prevOutputIndexu32 || actualValueu64 || actualP2pkKey33\n+CompactSize(nStableInputs)\n+  for each stable input, in wire order:\n+    prevTxid32 || prevOutputIndexu32 || actualValueu64 || actualOwnerKey33\n+u8(hasPriorVault)  # 1 for UPDATE/CLOSE/LIQUIDATE, else 0\n+  if 1: priorVaultRef32 || actualOwnerKey33 || actualCollateralu64 || actualPrincipalu64\n+```\n+\n+No caller-supplied prevout value/key or prior vault record can replace these state lookups.\n+Signatures bind every effect, input reference, value, key, operation, oracle bound and payout.\n+There are no selectable SIGHASH modes and no implicit whole-parent-state checkpoint. Same\n+effects on the same live inputs can be valid on competing branches when the contextual\n+oracle/expiry/policy rules also pass; this is normal fork behavior, not a replay across networks\n+or upgrades.\n+\n+A secp256k1 authorization consists of `u8(signatureLength) ||strictDER(signature)`, with length\n+8..72. The DER is the shortest valid ASN.1 encoding of the ECDSA pair; round-trip canonical DER\n+is required, 1<=r<n and 1<=s<=floor(n/2), where n is the secp256k1 subgroup order. No appended\n+hash-type byte, extra push opcode or trailing data is allowed. Verify ECDSA against the\n+indicated 32-byte digest and authoritative key. Low-S and minimal DER are consensus rules for\n+this version.\n+\n+A contains exactly these slots, without counts or padding:\n+\n+1. One signature for every native input, in wire order, role 0/index 0..nZecInputs-1, verified with its actual P2PK key.\n+2. One signature for every stable input, in wire order, role 1/index 0..nStableInputs-1, verified with its actual owner key.\n+3. For OPEN/UPDATE/CLOSE/LIQUIDATE, exactly one operation signature, role 2/index 0, using the operation auth key defined above. NONE/RETIRE have no such slot.\n+4. If hasOracle=1, exactly popcount(signerMask) raw 64-byte Ed25519 report signatures in ascending reporter-set index order, under the separate oracle message/ZIP215 validation rules.\n+\n+A signer may appear in several roles; every required role still has its own signature slot. This\n+prevents treating a fee input signature as authority over an unrelated vault. The effects txid\n+excludes all signatures, allowing dependent transactions to be constructed before authorization.\n+authDigest commits the complete tail. For this version, the transaction Merkle tree leaf is\n+txid, and the corresponding ZIP244 authorization-data tree leaf is authDigest. Existing Merkle\n+and block-commitment algorithms are unchanged. It is not a pre-v5 placeholder leaf. ZIP239\n+witnessed relay treats this version as witness-bearing: wtxid is the exact 64 bytes\n+txid||authDigest, in raw digest order. The version-dispatch rules are extended accordingly;\n+older-version hashes and their relay behavior are unchanged. At the pinned Zebra source, these\n+roles correspond to `zebra-chain/src/block/merkle.rs::AuthDataRoot`, `block/commitment.rs` and\n+`transaction/hash.rs::WtxId` (whose byte conversion concatenates these two 32-byte hashes).\n+[^zip-0244] [^zip-0239]\n+\n+## Oracle effects integration\n+\n+The oracle effect structure is:\n+\n+```\n+observedHeightu32 || observedBlockHash32 || priceu64 || signerMasku8\n+```\n+\n+This is 45 bytes. The mask's high bit is 0 and popcount is 5..7 for the fixed seven-reporter\n+deployment roster. The active roster ID is derived from the specified sorted keys and is not\n+repeated on wire. Oracle signatures bind their separate network/branch/roster/report message.\n+OracleID excludes the signer mask/signatures, so differing valid quorums over identical content\n+identify the same observation. The Oracle rules below provide exact ancestry, age, ordering and\n+rotation rules.\n+\n+\n+## Units, ranges and arithmetic\n+\n+| Symbol | Proposed value or definition |\n+|---|---|\n+| q | 100,000,000 zatoshi per ZEC |\n+| m | 1,000,000 micro-zUSD per zUSD |\n+| C | Vault collateral in zatoshi; 0 <= C <= MAX_MONEY |\n+| MAX_MONEY | 21,000,000 * q = 2,100,000,000,000,000 zatoshi, subject to the base protocol's native ZEC amount rule |\n+| D | Vault principal in micro-zUSD |\n+| P | Accepted micro-USD per ZEC; integer 1 <= P <= 2^64 - 1 |\n+| D_MIN | 100 * m = 100,000,000 micro-zUSD |\n+| S_MAX | 100,000 * m = 100,000,000,000 micro-zUSD |\n+| R_OPEN | 250 percent |\n+| R_LIQ | 200 percent |\n+| K_LIQ | 105 percent, including the 5 percent liquidation bonus |\n+| S | Outstanding native zUSD supply |\n+| B | Explicit bad debt |\n+\n+These opening, liquidation, bonus, debt-floor and supply-cap values are fixed draft defaults\n+carried from the prototype. In particular, the small initial supply ceiling bounds nominal\n+outstanding liabilities; it does not prove their dollar value or economic safety. Changing a\n+value requires the versioned consensus change defined by the activation specification; there is\n+no runtime operator setter.\n+\n+All encoded monetary amounts are unsigned integers. Additions and subtractions MUST reject\n+overflow or underflow. Products and divisions in the rules below MUST produce the exact\n+mathematical integer result. A checked unsigned 128-bit implementation suffices for these\n+ranges: even MAX_MONEY * (2^64 - 1) * 100 is less than 2^128. No floating point, saturating\n+arithmetic, silent truncation, approximate ratio, or overflow-dependent validity is permitted.\n+\n+`floor_div(a,b)` is integer floor for a >= 0 and b > 0. `ceil_div(a,b)` is `a / b + (a % b !=\n+0)` using integer quotient and remainder. An implementation using `a + b - 1` must establish\n+that the intermediate does not overflow.\n+\n+A debt-bearing vault is safe for opening/withdrawal exactly when:\n+\n+    C * P * 100 >= D * q * 250\n+\n+A vault is liquidatable exactly when D > 0 and:\n+\n+    C * P * 100 < D * q * 200\n+\n+Equality at 250 percent passes the opening test. Equality at 200 percent is not liquidatable.\n+Debt-free vault operations do not need a conversion price.\n+\n+## State and transaction shape\n+\n+At activation, the new value pool, vault set, S and B start empty/zero. Existing ZEC is not\n+reissued into a trusted fixture. Ordinary native input authorization and ZEC supply rules\n+continue to apply.\n+\n+Each live vault has an immutable owner authorization key, collateral C > 0, principal D, and one\n+current native reference. D MUST be zero or at least D_MIN. The wire proposal defines a current\n+reference as the creating transaction identifier alone, because each transaction has at most one\n+financial operation. A transition consumes the current reference once and creates at most one\n+successor. A spent or nonexistent reference cannot be revived by asserting its former balances.\n+\n+A native transaction contains at most one optional financial operation: NONE, OPEN, UPDATE,\n+CLOSE, LIQUIDATE or RETIRE_BAD_DEBT. It can also contain permitted native ZEC inputs/outputs and\n+native zUSD inputs/outputs. NONE permits an ordinary zUSD transfer with no issuance or burn.\n+OPEN incorporates initial borrowing; CLOSE incorporates full repayment. This gives atomic\n+opening/borrowing and repayment/closure without an arbitrary program or action list.\n+\n+A transaction with NONE and no oracle certificate requires at least one zUSD input, as specified\n+by the wire grammar. Unrelated ordinary ZEC transactions remain valid under their existing\n+format rules.\n+\n+The owner signs the entire authorized transaction body and relevant resolved previous values\n+under the new native signing domain. Recipient selection for newly borrowed zUSD is covered by\n+that signature. This is equivalent in financial effect to the model's Borrow followed by\n+Transfer, although the model credits the owner first. No conversion between Ed25519 and\n+secp256k1 identities, pre-registered model/native key map, fixture chain domain, account nonce\n+or whole-parent-state intent is adopted by these core rules.\n+\n+For each transaction define native zUSD input value I_s, native output value O_s, permitted mint\n+M and permitted burn R. The required native stable value balance is:\n+\n+    I_s + M = O_s + R\n+\n+Only the explicit rules below authorize M or R. Output omission, a memo, a claimed burn field or\n+an inaccessible key is not an accounting burn. S counts the total value of consensus-recognized\n+unspent zUSD outputs, including outputs whose private keys have been lost.\n+\n+For a vault-changing transaction, write C_old = 0 for OPEN and C_new = 0 for CLOSE/LIQUIDATE.\n+Its ZEC balance is:\n+\n+    I_zec + C_old = O_zec + C_new + F_zec\n+\n+For NONE and RETIRE_BAD_DEBT both collateral terms are zero. F_zec is a nonnegative native\n+transaction fee accounted for by the ordinary block/coinbase fee rules. No zUSD fee, collateral\n+subsidy or increase in ZEC supply is authorized. Normal relay/mining policy may require a\n+positive fee; this equation does not invent a positive consensus minimum. Exact indexed\n+collateral payouts described below cannot silently have fees subtracted from them.\n+\n+After each ordered transaction:\n+\n+    S = sum(live vault principal) + B\n+    S = sum(value in native unspent zUSD outputs)\n+    vaultPoolBalance = sum(live vault collateral)\n+    0 <= vaultPoolBalance <= MAX_MONEY\n+    0 <= S <= S_MAX\n+    0 <= B <= S\n+\n+The second equality is replaced only by an explicitly specified public/shielded value-pool\n+equality in a later shielded extension. It is not a requirement to determine whether an owner\n+still knows a private key.\n+\n+## Oracle use\n+\n+A price-dependent operation MUST bind the exact canonical report identifier accepted by the\n+oracle specification, not merely a height or a caller-asserted price. It MUST reject if that\n+report is absent, no longer canonical, or outside its allowed validity/anchoring window. The\n+Oracle rules below use seven configured reporters, five matching signatures and a maximum\n+twelve-block anchored observation age. Those acceptance and ordering rules are specified in the\n+Oracle rules below, not redefined here.\n+\n+Operations without a price requirement use the canonical zero/absent oracle identifier specified\n+by the wire format. Repayment, collateral addition without borrowing, debt-free\n+withdrawal/closure, ordinary transfer and bad-debt retirement remain possible during oracle\n+outages. No validator fetches a price from the web or uses its local wall clock.\n+\n+## OPEN\n+\n+OPEN supplies C_new > 0, immutable owner authorization and D_new. The owner authorizes the\n+transaction. Native ZEC inputs fund C_new through the value balance above; existing collateral\n+cannot simultaneously remain spendable elsewhere.\n+\n+D_new MUST be zero or at least D_MIN. If D_new > 0, B at transaction entry MUST be zero; the\n+exact fresh canonical report is required; the 250-percent inequality MUST hold for\n+(C_new,D_new); and S + D_new MUST be <= S_MAX. Set M = D_new, R = 0, S' = S + D_new and B' = B.\n+If D_new = 0, no oracle is required.\n+\n+## UPDATE\n+\n+UPDATE consumes one current vault and retains its owner. Let delta_C = C_new - C_old and delta_D\n+= D_new - D_old, understood as mathematical signed differences rather than a wrapping unsigned\n+calculation.\n+\n+C_new MUST be strictly positive. D_new MUST be zero or >= D_MIN. An unchanged pair (C_new,D_new)\n+= (C_old,D_old) is rejected. A transition to (0,0) uses CLOSE; no empty live vault is retained.\n+These restrictions remove two needless successor forms accepted by the account prototype and are\n+explicit proposed encoding/state changes.\n+\n+If delta_D > 0, B at transaction entry MUST be zero, a fresh exact canonical report MUST be\n+bound, the final state MUST satisfy the 250-percent inequality, and S + delta_D MUST be <=\n+S_MAX. Set M = delta_D and R = 0.\n+\n+If delta_D < 0, burn R = -delta_D actual native zUSD and set M = 0. If delta_D = 0 both are\n+zero. There is no owner-authorized debt forgiveness. Repayment that leaves debt strictly between\n+zero and D_MIN is invalid.\n+\n+If C_new < C_old and D_new > 0, a fresh exact canonical report and the final 250-percent\n+inequality are also required, even when debt decreases. If C_new < C_old, the indexed owner\n+payout MUST equal C_old - C_new; it MUST use the owner's prescribed native payment lock. If\n+C_new >= C_old, no collateral-release payout exists. Deposits fund the increase through native\n+inputs.\n+\n+Set S' = S + M - R and B' = B. Owner authorization is required for all UPDATE forms, including\n+pure deposit and pure repayment, matching the reference policy. A future third-party\n+repayment/deposit facility requires an explicit change; it is not silently inferred from the\n+absence of collateral release.\n+\n+## CLOSE\n+\n+CLOSE consumes one current vault, requires its owner authorization, burns exactly R = D_old\n+actual native zUSD, pays exactly C_old to the owner's indexed native payout and creates no\n+successor. M = 0, S' = S - D_old and B' = B. It needs no oracle even when D_old > 0 because the\n+entire debt is paid in the same transaction.\n+\n+This combines the existing model's Repay(full debt) followed by Close. The model's standalone\n+Close requires debt already zero; the native combined encoding does not forgive the old\n+principal.\n+\n+## LIQUIDATE\n+\n+LIQUIDATE consumes and closes one current vault. It requires the exact fresh canonical report,\n+D_old > 0, and the strict 200-percent liquidation inequality. Borrower authorization is\n+unnecessary. The liquidator authorizes the complete transaction and can be any holder, including\n+the borrower.\n+\n+Compute using the original C and D:\n+\n+    x = min(D, floor_div(C * P * 100, q * 105))\n+    z = ceil_div(x * q * 105, P * 100)\n+\n+The verifier MUST establish 0 <= z <= C. It MUST require x <= signed_max_burn and z >=\n+signed_min_collateral. R = x and M = 0. The transaction consumes actual native zUSD sufficient\n+for x and returns permitted change through the stable value equation.\n+\n+The indexed liquidator payout MUST be exactly z zatoshi under the signed liquidator payment key.\n+The indexed owner payout MUST be exactly C-z under the immutable owner's payment key. Payout\n+indices MUST be unambiguous and cannot count the same output twice; the zero-value sentinel\n+convention is specified by the wire format. No successor vault remains.\n+\n+Set:\n+\n+    S' = S - x\n+    B' = B + (D - x)\n+\n+This retains the model's full-vault liquidation policy and rounding. If x = 0, z = 0 and the\n+operation remains valid when the signed bounds permit it. No zUSD input is then required solely\n+for the zero burn, and the owner receives C; the entire D becomes bad debt. The native ZEC\n+fee/value rules still apply. There is no claim that anyone has an economic incentive to submit\n+that dust liquidation.\n+\n+Liquidation with insufficient actual liquidator zUSD rejects; it does not reduce x to the\n+caller's balance or silently leave a partial vault. A price crash can create B > 0 even with\n+honest reporters and immediate execution.\n+\n+## RETIRE_BAD_DEBT\n+\n+This new proposed operation makes recovery possible without a reserve or privileged reset. A\n+holder chooses an integer r satisfying 0 < r <= B at transaction entry. The holder must\n+authorize spending actual zUSD inputs. Set R = r, M = 0, S' = S-r and B' = B-r. No vault is\n+consumed or changed, no oracle is needed, and no ZEC is paid by the protocol.\n+\n+The holder receives no redemption claim, future repayment right, reward entitlement, priority,\n+governance privilege or ownership of another vault. Ordinary input change is returned under the\n+signed transaction. The operation is an uncompensated token burn against recorded bad debt.\n+\n+A NONE or RETIRE_BAD_DEBT transaction cannot create zUSD or borrow. The one-operation grammar\n+prevents combining bad-debt retirement with newly minted debt in the same transaction. A later\n+transaction can borrow only after preceding valid retirement has actually reduced B to zero.\n+\n+This operation is absent from the current Rust model and collateral profile. Its new conformance\n+vectors and future implementation must be tracked separately from passing legacy tests.\n+\n+## Loss and liveness policy\n+\n+Borrowing is prohibited while B > 0. Existing transfers, valid debt repayment, deposits,\n+permitted withdrawals/closure, liquidation and RETIRE_BAD_DEBT continue. The freeze is based on\n+recorded B at the operation's ordered transaction entry; latent losses in unliquidated vaults\n+are not magically detected.\n+\n+Only a valid RETIRE_BAD_DEBT reduces B in this core version. No clock, oracle report, miner\n+field, operator key or bookkeeping reset may erase liabilities. S decreases by the same amount,\n+preserving the debt/supply identity. A future separately approved reserve mechanism could fund\n+voluntary retirement using tokens it actually owns; no such funding or reserve authority is\n+activated by this core.\n+\n+Borrowing resumes automatically when the actual B value reaches zero and the normal\n+fresh-price/collateral/cap conditions pass. If no holder is willing to retire tokens, the freeze\n+can persist indefinitely. The protocol does not guarantee recapitalization, price recovery or a\n+dollar redemption right. This is an explicit complete loss policy with a remaining economic\n+availability risk, rather than an unspecified administrative recovery hook.\n+\n+\n+## Complete block transition and persistence\n+\n+Validation MUST begin from the candidate block's exact parent state, independently of the node's\n+currently selected tip. The candidate includes ordinary native UTXOs and value pools, the ZEC\n+vault pool/current vault set, stable UTXOs, S, B, canonical oracle state, fee accounting and\n+activation context. No caller-asserted balance or post-state replaces validation against that\n+parent.\n+\n+Validators MUST apply every ordinary and ZSV transaction in block order to a private candidate\n+state. Later transactions see the valid outputs, spent references and state changes of earlier\n+transactions in that block. The oracle placement rules below determine when a certificate\n+becomes usable. A missing, already spent or duplicate input/reference is invalid; creating an\n+output or current vault reference MUST NOT overwrite an existing unspent entry in its namespace.\n+The absence of a ZSV operation in an ordinary transaction cannot authorize access to the\n+separate vault or stable value pools.\n+\n+Each transaction MUST satisfy its native and ZSV rules, exact payouts, per-asset balances,\n+monetary bounds and applicable state invariants before the next transaction is evaluated. The\n+vault-pool delta is applied atomically with ordinary ZEC input/output and fee deltas, so\n+collateral is never counted as both an ordinary output and a vault balance. A block that fails\n+any transaction, oracle, resource, commitment or ordinary consensus check MUST be rejected with\n+no persistent changes to any of these states.\n+\n+Only a fully valid block may commit the complete candidate transition. Reorganization,\n+finalization and restart MUST reconstruct the same parent-derived state as ordered replay of the\n+accepted branch. Mempool admission is provisional and MUST NOT substitute for block-context\n+validation. Checkpoint and direct-finalization paths MUST enforce equivalent rules and establish\n+matching ZSV state; a path that merely skips the new checks or imports asserted ZSV balances is\n+invalid. This specification requires equivalent results, not a particular database layout or\n+caching strategy.\n+\n+## Oracle rules\n+\n+### Deployment roster and domains\n+\n+For each activated ZSV consensus branch, the deployment specification MUST provide seven\n+distinct Ed25519 public keys, ordered lexicographically by their 32-byte encodings. The keys\n+MUST use canonical encodings of non-identity points in Ed25519's prime-order subgroup. In group\n+notation, each decoded key A satisfies A != identity and [ell]A = identity, where ell is the\n+Ed25519 subgroup order. Canonical encoding means re-encoding the decoded point using the RFC\n+8032 compressed encoding reproduces the 32-byte input exactly.\n+\n+The reporter set is fixed for that consensus branch. There is no oracle transaction,\n+administrator signature, voting token or runtime configuration that changes the roster, quorum\n+or freshness period. A roster change requires an explicitly specified network upgrade. Local\n+configuration MUST NOT override the deployment roster.\n+\n+Define:\n+\n+```text\n+reporterSetID = SHA256(\n+    ASCII(\"ZSV/OracleSet/v1\\0\") ||\n+    u8(7) || u8(5) ||\n+    key[0] || ... || key[6]\n+)\n+```\n+\n+Here `\\0` is one zero byte, concatenation is byte concatenation, and SHA256 returns its 32 raw\n+digest bytes. The active network's genesis hash is the 32-byte consensus serialization used for\n+block-hash fields, not the human-readable reversed hexadecimal presentation. The active\n+consensus branch ID is encoded as an unsigned 32-bit little-endian integer.\n+\n+### Report encoding\n+\n+A report's effect fields have the following exact encoding. There is no CompactSize or other\n+length prefix within this structure:\n+\n+| Field | Bytes | Encoding |\n+|---|---:|---|\n+| observedHeight | 4 | unsigned little-endian |\n+| observedBlockHash | 32 | raw consensus block-hash serialization |\n+| price | 8 | unsigned little-endian, micro-USD per ZEC |\n+| signerMask | 1 | bit i corresponds to roster key i |\n+\n+The first three fields form the 44-byte `reportBody`. The complete effect structure is 45 bytes.\n+`price` MUST be in `1..=2^64-1`; zero is invalid. Bit 7 of `signerMask` MUST be zero. Its\n+population count MUST be 5, 6 or 7. The authorization section contains exactly that many 64-byte\n+Ed25519 signatures, in increasing signer-index order. Signature count is derived from the mask;\n+no independently asserted count is accepted.\n+\n+Every included signature verifies the same 126-byte message:\n+\n+```text\n+oracleMessage =\n+    ASCII(\"ZSV/Oracle/v1\\0\") ||        # 14 bytes\n+    networkGenesisHash ||              # 32 bytes\n+    LE32(activeConsensusBranchID) ||    # 4 bytes\n+    reporterSetID ||                    # 32 bytes\n+    reportBody                         # 44 bytes\n+\n+oracleID = SHA256(oracleMessage)\n+```\n+\n+`oracleID` deliberately excludes the signer mask and signatures. Different valid quorum subsets\n+for the same observation therefore identify the same price report. The transaction's ordinary\n+effect/authentication digests still commit the mask and signatures as specified by the\n+transaction format.\n+\n+Ed25519 signature verification MUST follow ZIP 215's exact verification criteria [^zip-0215]: A\n+and R decode to Ed25519 points, S_sig < ell, and the cofactored equation [8][S_sig]B_Ed = [8]R +\n+[8][k]A holds with k = SHA512(R_bytes || A_bytes || oracleMessage) interpreted as a\n+little-endian integer modulo ell, using the supplied R and A encodings as specified by ZIP 215\n+and RFC 8032. Implementations MUST NOT substitute a different library's unspecified \"strict\n+Ed25519\" acceptance rules or the uncofactored equation. The deployment-key restrictions above\n+are additional restrictions on the fixed roster; they do not change ZIP 215's acceptance rules\n+for signature R encodings. An included invalid signature invalidates the certificate even when\n+five other signatures verify. In this signature equation, S_sig is the signature scalar and B_Ed\n+is the standard Ed25519 base point, independent of stable supply S and bad debt B. SHA512 here\n+is the 64-byte SHA-512 hash; it is distinct from the SHA256 report identifier.\n+\n+The effect structure and authorization together use 365 bytes for five signatures and 493 bytes\n+for seven signatures, excluding enclosing transaction framing. There is no price median,\n+averaging rule or node-local price selection: a certificate authorizes one identically signed\n+price tuple.\n+\n+### Inclusion, observed ancestry and freshness\n+\n+Let H be the candidate block height, A the first height of the active consensus branch and h the\n+report's `observedHeight`. Validators MUST enforce all of the following before accepting a\n+report:\n+\n+1. `A - 1 <= h < H`, with A positive for an activated ZSV branch.\n+2. `h + 12` is representable as a u32, and `H <= h + 12`.\n+3. `observedBlockHash` equals the actual ancestor block hash at height h on the candidate block's parent chain.\n+4. If an observation was already accepted under this active branch and reporter set, h is strictly greater than that observation's height.\n+5. The price, signer mask, signature lengths and every signature satisfy the rules above.\n+\n+A certificate included at height I can price operations only in descendants at heights `I + 1`\n+through `h + 12`, inclusive, while it remains the parent-selected observation. The earliest\n+possible pricing height is `h + 2`. The twelve-block bound is a maximum observation age, not a\n+guaranteed transaction-validity period. A certificate included at `h + 12` has no remaining\n+pricing lifetime. There is no signed arbitrary expiry field, no free-running sequence field and\n+no future-dated report. The observed height supplies the monotonic sequence. An inclusion delay\n+consumes the report's lifetime; re-inclusion cannot refresh it.\n+\n+At the first ZSV activation or a change of the active consensus branch/domain, the block pricing\n+observation is empty. A valid certificate under the new domain can be recorded in that block,\n+but becomes usable only in its descendants under the rules below. An observation signed for an\n+older branch cannot authorize new price-dependent operations. Reports may use the last block\n+before activation as their observed ancestor, provided their signatures use the newly active\n+domain. Existing vault/payment/liability state is not reset by this oracle-domain change.\n+\n+### Block order and transaction restrictions\n+\n+A block contains at most one ZSV oracle certificate. If present, it MUST occur in the first\n+non-coinbase transaction. That transaction MUST contain no vault operation or stablecoin\n+input/output. Ordinary ZEC inputs and outputs may fund its network fee, subject to the new\n+transaction format's ordinary ZEC rules. The certificate is not carried in coinbase and does not\n+create value.\n+\n+At block entry, validators MUST select the pricing observation from the exact parent's committed\n+oracle state under the active domain. That selection is fixed for all operations in the\n+candidate block. A valid certificate in the candidate block MUST update only the oracle state\n+committed for descendants; it MUST NOT replace the block's pricing observation. With no\n+certificate, retain the applicable parent observation in committed state, including its original\n+observation height. Expiry makes it unusable for pricing but does not erase its height for the\n+strictly advancing report rule. A second certificate, or one after any other non-coinbase\n+transaction, invalidates the block. There is no requirement to include a certificate; a\n+coinbase-only block remains valid under the ordinary block rules.\n+\n+Every borrowing, debt-bearing withdrawal and liquidation operation MUST bind `oracleID` and MUST\n+use the fixed parent-selected pricing observation. The candidate height MUST be no greater than\n+that observation's `observedHeight + 12`. A newly included certificate cannot rescue an absent\n+or expired pricing observation in the same block. An operation naming an observation other than\n+the block's pricing observation, or naming an expired or absent observation, is invalid even if\n+that observation carries an otherwise valid quorum certificate. The operation's ordinary\n+transaction expiry applies independently and cannot extend the oracle window.\n+\n+No node-local clock, URL fetch, API response, observed mempool certificate or external service\n+result is consulted during consensus validation. A producer may omit a certificate or choose\n+among admissible certificates for descendants; all operations in the current block still use its\n+parent-selected observation.\n+\n+### Outages, conflicting reports and recovery\n+\n+An absent or expired block pricing observation disables borrowing, debt-bearing withdrawal and\n+liquidation. It does not disable collateral additions, repayment, stable transfers, debt-free\n+withdrawal/closure or an explicitly specified bad-debt retirement that burns actual stable\n+value. Expiry does not itself move collateral, forgive debt, trigger a privileged settlement or\n+authorize liquidation using a fabricated fallback price.\n+\n+A later valid certificate can resume price-dependent operations from the following block, if\n+still fresh, subject to all other protocol rules, including any bad-debt issuance freeze. Report\n+failure alone does not reset liabilities or replace reporter keys. If the committee cannot\n+recover, the specified remedy is a reviewed network upgrade rather than an administrator bypass.\n+\n+Two certificates with different prices for the same observation height cannot both be accepted\n+on one branch because observed heights strictly advance and a block admits at most one\n+certificate. Competing forks may accept different valid certificates. Fork choice and\n+reorganization select the corresponding complete vault/oracle/payment state. Evidence of\n+reporter equivocation does not automatically change the committee, seize funds or suspend the\n+chain in v1; no slashable reporter bond is specified.\n+\n+\n+# Rationale\n+\n+## A dedicated fixed-asset protocol\n+\n+The proposal uses a dedicated transaction format and implicit zUSD payment fields. There is no\n+caller-chosen asset identifier or issuer path. A dedicated ZEC vault value pool removes\n+collateral from ordinary spendable outputs; the experimental `OP_DROP OP_TRUE` marker is not\n+used as a native lock.\n+\n+General ZSA issuance, a generic VM and external custody were considered. General issuance and a\n+VM broaden the permitted activity, while external custody fails the requirement that Zcash\n+enforce vault rules. Selected ZSA transfer and proof techniques may still be useful in a future\n+shielded profile. The public first version trades payment privacy for a smaller initial\n+specification; it does not claim to deliver a private dollar payment instrument.\n+\n+One financial operation per transaction limits ordering ambiguity. Open-with-debt and\n+repay-and-close remain atomic. A vault update replaces one current state; it does not accumulate\n+reusable owner-local identifiers. The specified native signatures use one owner-key scheme\n+instead of the prototype's permanent mapping between two key schemes.\n+\n+Veylor is one example of dollar access alongside shielded ZEC: its swaps deliver USDC held in\n+Railgun on Ethereum. [^veylor]\n+\n+## Economic policy\n+\n+The 250% borrowing ratio, 200% liquidation ratio, 5% bonus, 100-zUSD minimum debt and\n+100,000-zUSD liability ceiling are concrete proposed initial values taken from the research\n+model. They are not calibrated safety guarantees. A 20% collateral-price decline moves a vault\n+from 250% to 200% before oracle delay, execution costs or market gaps. Full liquidation can\n+create bad debt despite valid accounting.\n+\n+The ceiling limits initial nominal exposure; it does not bound ZEC value lost to a false price,\n+guarantee liquidation depth or ensure a market peg. Repayment supplies a source of demand, but\n+holders receive no unconditional one-dollar redemption right. A reserve held in ZEC shares the\n+collateral's drawdown risk.\n+\n+Retiring bad debt requires burning actual tokens. It transfers no collateral and creates no\n+privileged reset authority. A holder may nevertheless have no incentive to absorb the loss, so\n+borrowing can remain stopped indefinitely. Small shortfalls can trigger the freeze. Adding an\n+incentivized recapitalization or redemption system would change the economic proposal and needs\n+separate specification and review.\n+\n+## Oracle and governance\n+\n+Using the parent observation prevents a certificate included in the same block from invalidating\n+already-signed operations, even when reports arrive every block. Wallets can sign for the next\n+block using the current tip's committed oracle state. Transactions delayed beyond that block or\n+affected by a reorganization may still need re-signing. The cost is one block of additional\n+price delay: even a fresh crash report cannot change borrowing or liquidation prices in its\n+inclusion block. This tradeoff requires economic calibration; no history of user-selectable\n+prices or extra reporter authority is introduced.\n+\n+The reporter quorum is an explicit external trust dependency. Signature thresholds do not prove\n+independent organizations, independent market sources or truthful prices. Fixed upgrade-selected\n+membership removes a runtime membership administrator but makes key replacement and recovery\n+slower. The freshness bound is measured in blocks; its wall-clock duration depends on actual\n+block production.\n+\n+Changing consensus parameters or the reporter set requires a network upgrade under the specified\n+deployment process. No token vote, issuer key or reserve operator can change them. The roster,\n+parameter calibration and operational arrangements need public review before deployment.\n+\n+## Reserve and funding boundary\n+\n+The core has no reserve state, block-reward allocation, discretionary incentive spending or NSM\n+withdrawal. A companion proposal could add native reserve cash and already-issued token\n+inventory, but would need explicit funding, trade, allowance, closure and final-disposition\n+rules.\n+\n+The research studies identify remaining risks: stale-price trading, limited market response,\n+losses occurring after a one-time reserve burn, and cheap terminal auctions under weak\n+competition. Accounting tests do not resolve these incentives. A proposed reduction in another\n+funding recipient's allocation does not itself allocate the difference to ZSV.\n+\n+## Community discussion\n+\n+The [forum discussion](https://forum.zcashcommunity.com/t/zip-draft-zsv-one-zec-backed-stablecoin-on-zcash/58909) opened on 5 November 2026.\n+No community endorsement, editor approval or public review outcome is claimed. Objections, alternatives and responses will be\n+recorded here when they exist. The mathematical studies and coding-agent adversarial reviews are\n+preliminary evidence, not the qualified independent expert review required for release of a\n+security-sensitive ZIP. [^zip-0000]\n+\n+# Backward compatibility\n+\n+Activation introduces a consensus change and requires upgraded validators. Existing ZEC\n+transaction formats and payment pools retain their rules. The new format supports a restricted\n+public funding path; wallet support and safe address presentation remain necessary for users to\n+construct it.\n+\n+The prototype's V4/Sapling signatures, JSON envelopes, trusted funding coinbase, model account\n+nonces and whole-parent-checkpoint intents are not native consensus formats. Existing model\n+vectors remain research fixtures and are not migrated into network state. No experimental\n+fixture balance or key becomes a live allocation.\n+\n+# Deployment\n+\n+No target network upgrade or activation height is selected. Deployment needs explicit Mainnet\n+and Testnet activation parameters, transaction identifiers, domain inputs and public reporter\n+keys. These are deployment allocations, not node-local settings. Neither a ZIP number nor\n+merging a draft activates the feature.\n+\n+Before a Released status such as Proposed, ZIP 0 requires the applicable editor/community\n+process and qualified independent security/privacy review for a proposal with these\n+implications. A complete reference implementation generally need not precede Proposed.\n+Implementation, testing and network activation are later, distinct milestones. [^zip-0000]\n+\n+The deployment evidence should cover independent serialization and signature implementations,\n+cross-implementation valid/invalid block vectors, value conservation, every\n+acceptance/finalization path, reorgs and database restart, resource benchmarks, oracle\n+compromise/outage scenarios and economic losses. These checks cannot establish a guaranteed\n+market peg.\n+\n+# Reference implementation\n+\n+The [ZSV research\n+repository](https://github.com/iJaack/zsv/tree/d0f8529f08653744b7702670bc628b20b7d17b00)\n+provides a signed account model, economic studies and offline profiles linked to pinned Zebra\n+code. Its original code is MIT-licensed; upstream dependencies retain their own licenses. It is\n+not a complete implementation of this ZIP.\n+\n+The model tests collateralized issuance, repayment, liquidation, explicit bad debt and optional\n+reserve accounting. The collateral profile verifies serialized fixture ZEC UTXOs and native P2PK\n+signatures with an experimental guard. The guard is outside Zebra's node verifier, and zUSD\n+remains a model balance. At that revision, 110 reference Rust tests, 39 Zebra-linked tests and\n+96 Python tests passed; all 351 combined economic cases reproduced. [^verification]\n+\n+This candidate introduces rules beyond that code: a dedicated transaction and value-pool format,\n+normalized single-operation transitions, a different oracle policy, native signature domains and\n+permissionless retirement of bad debt. Passing the earlier tests is not evidence that those new\n+rules are implemented. The accompanying [conformance\n+material](https://github.com/iJaack/zsv/tree/main/zips/draft-ijaack-zsv) contains 54 financial\n+cases, seven unsigned wire/context/hash examples covering all operation tags and oracle framing,\n+and a canonical-signature oracle fixture. These are not a complete signed native transaction or\n+full-block consensus suite.\n+\n+# Outstanding allocations and review\n+\n+The protocol choices in Specification are proposed rules, subject to public review. The\n+remaining work before calling this a deployment-ready specification is:\n+\n+1. Allocate the new transaction/group identifiers and upgrade context; select Mainnet/Testnet activation and the public reporter roster through the deployment process.\n+2. Independently review and implement the byte, digest, authorization and branch-state rules, and produce signed transaction and full-block conformance vectors.\n+3. Obtain qualified consensus/cryptographic/privacy review and resource benchmarks, and justify the economic parameters, oracle operations and loss policy against adversarial conditions.\n+4. Incorporate forum feedback and rerun upstream rendering after metadata changes. This draft has been rendered with the ZIP repository's `render.sh --pandoc` path.\n+\n+The public-first scope and Owners contact are confirmed. No editor-assigned number, named\n+reviewer, oracle operator or activation approval has been invented to fill these entries.\n+\n+# References\n+\n+[^BCP14]: [BCP 14: Key words for requirement levels](https://www.rfc-editor.org/info/bcp14).\n+\n+[^zip-0000]: [ZIP 0: ZIP Process](https://github.com/zcash/zips/blob/ff594de6e95bb9e9e7d2ace09510281531c30447/zips/zip-0000.rst), reviewed 30 October 2026. The [Markdown template](https://github.com/zcash/zips/blob/ff594de6e95bb9e9e7d2ace09510281531c30447/zips/zip-template.md) provides the document structure.\n+\n+[^verification]: [ZSV verification receipt at the collateral milestone](https://github.com/iJaack/zsv/blob/d0f8529f08653744b7702670bc628b20b7d17b00/docs/verification.md).\n+\n+[^sources]: [Pinned Zcash and Zebra research sources](https://github.com/iJaack/zsv/blob/d0f8529f08653744b7702670bc628b20b7d17b00/docs/sources.md). The consensus research baseline is separate from the current ZIP-process reference above.\n+\n+[^zip-0215]: [ZIP 215: Explicitly Defining and Modifying Ed25519 Validation Rules](https://github.com/zcash/zips/blob/2bdf39494320e5e04057ac93b1794031ebfb00b5/zips/zip-0215.rst).\n+\n+[^zip-0244]: [ZIP 244: Transaction Identifier Non-Malleability](https://github.com/zcash/zips/blob/2bdf39494320e5e04057ac93b1794031ebfb00b5/zips/zip-0244.rst).\n+\n+[^zip-0239]: [ZIP 239: Relay of Version 5 Transactions](https://github.com/zcash/zips/blob/2bdf39494320e5e04057ac93b1794031ebfb00b5/zips/zip-0239.rst).\n+\n+[^veylor]: [Veylor: How it works](https://veylor.cash/how-it-works/), read 5 November 2026."},{"filename":"zips/draft-ijaack-zsv/README.md","status":"added","additions":23,"deletions":0,"patch":"@@ -0,0 +1,23 @@\n+# ZSV draft conformance material\n+\n+These files support [draft-ijaack-zsv.md](../draft-ijaack-zsv.md). They test selected proposed rules beyond the existing Rust research model. They are not a full native consensus implementation or a cryptographic audit.\n+\n+```sh\n+python3 zips/draft-ijaack-zsv/check_financial_vectors.py\n+python3 zips/draft-ijaack-zsv/check_wire_vectors.py\n+python3 zips/draft-ijaack-zsv/check_oracle_vector.py\n+```\n+\n+Run from the repository root with Python 3.10 or later. Financial and wire checks use the standard library. The oracle fixture checker also needs `cryptography` (verified with version 50.0.0); it tests ordinary canonical Ed25519 signatures, not all ZIP 215 acceptance cases. The scripts check adjacent JSON by default. The wire and oracle scripts accept `--write` to regenerate their fixtures.\n+\n+| Files | What they check | What they do not check |\n+|---|---|---|\n+| `financial-vectors.json`, `check_financial_vectors.py` | 54 operation/range/rounding cases, exact Fraction recomputation and u128 domain bounds | Native parsing, keys, signatures, actual UTXO membership or full branch state |\n+| `wire-vectors.json`, `check_wire_vectors.py` | Seven unsigned effect bodies covering all operation tags and oracle framing, authoritative-context encoding, transaction/context/signature digests and value equations | ECDSA authorization tails, full node parsing, real input history or allocated network identifiers |\n+| `oracle-vector.json`, `check_oracle_vector.py` | Report bytes/domains, seven canonical signatures, quorum subsets, rejection/context probes and 13 block-selection cases | Full ZIP 215 noncanonical edge cases, operator independence, data truth or complete native block validation |\n+\n+All keys, inputs and network identifiers are fictional test data. The oracle seeds are intentionally public. No fixture supplies a deployment roster or an allocation.\n+\n+Remaining conformance work includes independently implemented serialization/digests, complete signed transaction and block vectors, strict-DER/low-S and ZIP 215 edge cases, every operation with real native state, reorg/restart equivalence, resource-limit boundaries and full-node acceptance paths. The [readiness record](https://github.com/iJaack/zsv/blob/d0fe22d/docs/zip-readiness.md) records review and execution results.\n+\n+Pricing uses the exact parent's committed observation for the whole block. A newly included certificate takes effect in descendants, so unchanged-price renewal cannot invalidate operations in its inclusion block. The fixture's report included at height 101 is first usable at 102 and expires after 112. Block-selection checks cover renewal, price changes, expiry, activation/domain changes and sibling parents; they are helper-level checks, not native block validation."},{"filename":"zips/draft-ijaack-zsv/check_financial_vectors.py","status":"added","additions":188,"deletions":0,"patch":"@@ -0,0 +1,188 @@\n+#!/usr/bin/env python3\n+\"\"\"Check ZIP candidate arithmetic with Fraction, not native consensus validation.\"\"\"\n+\n+import json\n+import sys\n+from fractions import Fraction\n+from pathlib import Path\n+\n+Q = 100_000_000\n+UNIT = 1_000_000\n+MAX_C = 21_000_000 * Q\n+CAP = 100_000 * UNIT\n+MIN_DEBT = 100 * UNIT\n+U64_MAX = (1 << 64) - 1\n+U128_MAX = (1 << 128) - 1\n+PARAMETERS = {\n+    \"zatoshi_per_zec\": Q,\n+    \"micro_units_per_zusd\": UNIT,\n+    \"max_collateral\": MAX_C,\n+    \"max_price\": U64_MAX,\n+    \"min_nonzero_principal\": MIN_DEBT,\n+    \"supply_cap\": CAP,\n+    \"borrow_ratio\": \"5/2\",\n+    \"liquidation_ratio\": \"2/1\",\n+    \"liquidation_payout_multiplier\": \"21/20\",\n+    \"intermediate_bits\": 128,\n+}\n+FIELDS = {\n+    \"open\": {\"collateral\", \"principal\", \"supply\", \"bad_debt\", \"price\", \"oracle_fresh\"},\n+    \"update\": {\"collateral\", \"principal\", \"new_collateral\", \"new_principal\", \"supply\", \"bad_debt\", \"price\", \"oracle_fresh\", \"stable_input\"},\n+    \"close\": {\"collateral\", \"principal\", \"supply\", \"bad_debt\", \"stable_input\"},\n+    \"liquidate\": {\"collateral\", \"principal\", \"supply\", \"bad_debt\", \"price\", \"oracle_fresh\", \"stable_input\", \"max_burn\", \"min_collateral\"},\n+    \"retire_bad_debt\": {\"supply\", \"bad_debt\", \"amount\", \"stable_input\"},\n+}\n+\n+\n+class Invalid(ValueError):\n+    \"\"\"A financial precondition failed; no state is committed.\"\"\"\n+\n+\n+def require(condition, reason):\n+    if not condition:\n+        raise Invalid(reason)\n+\n+\n+def bounded(value, limit):\n+    require(type(value) is int and 0 <= value <= limit, \"range\")\n+\n+\n+def debt_valid(value):\n+    return value == 0 or value >= MIN_DEBT\n+\n+\n+def price_for(data):\n+    require(data[\"oracle_fresh\"] and data[\"price\"] is not None, \"oracle\")\n+    return Fraction(data[\"price\"], Q)\n+\n+\n+def enough_collateral(collateral, principal, price):\n+    return collateral * price >= Fraction(5, 2) * principal\n+\n+\n+def post(supply, bad_debt, collateral, principal, minted=0, burned=0,\n+         deposit=0, owner=0, liquidator=0):\n+    return {\n+        \"accepted\": True,\n+        \"supply\": supply,\n+        \"bad_debt\": bad_debt,\n+        \"vault\": None if collateral is None else {\"collateral\": collateral, \"principal\": principal},\n+        \"minted\": minted,\n+        \"burned\": burned,\n+        \"zec_deposited\": deposit,\n+        \"owner_zec\": owner,\n+        \"liquidator_zec\": liquidator,\n+    }\n+\n+\n+def evaluate(operation, data):\n+    if operation not in FIELDS or set(data) != FIELDS[operation]:\n+        raise ValueError(\"unknown operation or input fields\")\n+    try:\n+        for name, value in data.items():\n+            if name == \"oracle_fresh\":\n+                require(type(value) is bool, \"range\")\n+            elif name == \"price\":\n+                if value is not None:\n+                    bounded(value, U64_MAX)\n+                    require(value > 0, \"range\")\n+            else:\n+                limit = MAX_C if \"collateral\" in name else U64_MAX if name == \"max_burn\" else CAP\n+                bounded(value, limit)\n+        supply, bad = data[\"supply\"], data[\"bad_debt\"]\n+        require(bad <= supply, \"prestate\")\n+        collateral, debt = data.get(\"collateral\", 0), data.get(\"principal\", 0)\n+        if operation not in {\"open\", \"retire_bad_debt\"}:\n+            require(collateral > 0 and debt_valid(debt) and supply >= debt + bad, \"prestate\")\n+        other_principal = supply - bad - (debt if operation not in {\"open\", \"retire_bad_debt\"} else 0)\n+        require(debt_valid(other_principal), \"prestate\")\n+        if \"stable_input\" in data:\n+            require(data[\"stable_input\"] <= supply, \"prestate\")\n+\n+        if operation == \"open\":\n+            require(collateral > 0, \"collateral\")\n+            require(debt_valid(debt), \"minimum_debt\")\n+            if debt:\n+                require(bad == 0, \"bad_debt_freeze\")\n+                require(enough_collateral(collateral, debt, price_for(data)), \"collateral_ratio\")\n+            require(supply + debt <= CAP, \"supply_cap\")\n+            return post(supply + debt, bad, collateral, debt, minted=debt, deposit=collateral)\n+\n+        if operation == \"update\":\n+            new_c, new_d = data[\"new_collateral\"], data[\"new_principal\"]\n+            require(new_c > 0, \"collateral\")\n+            require((new_c, new_d) != (collateral, debt), \"no_change\")\n+            require(debt_valid(new_d), \"minimum_debt\")\n+            if new_d > debt:\n+                require(bad == 0, \"bad_debt_freeze\")\n+            if new_d > debt or (new_c < collateral and new_d > 0):\n+                require(enough_collateral(new_c, new_d, price_for(data)), \"collateral_ratio\")\n+            burn, mint = max(0, debt - new_d), max(0, new_d - debt)\n+            require(burn <= data[\"stable_input\"], \"stable_input\")\n+            require(supply + mint <= CAP, \"supply_cap\")\n+            return post(supply + mint - burn, bad, new_c, new_d, minted=mint, burned=burn,\n+                        deposit=max(0, new_c - collateral), owner=max(0, collateral - new_c))\n+\n+        if operation == \"close\":\n+            require(debt <= data[\"stable_input\"], \"stable_input\")\n+            return post(supply - debt, bad, None, None, burned=debt, owner=collateral)\n+\n+        if operation == \"liquidate\":\n+            require(debt > 0, \"healthy\")\n+            price = price_for(data)\n+            require(collateral * price < 2 * debt, \"healthy\")\n+            # Fraction derives affordability and payout independently of the ZIP's\n+            # cross-multiplied integer formulas.\n+            affordable = collateral * price / Fraction(21, 20)\n+            burn = min(debt, affordable.numerator // affordable.denominator)\n+            payment = Fraction(burn) * Fraction(21, 20) / price\n+            paid = -(-payment.numerator // payment.denominator)\n+            require(paid <= collateral, \"collateral\")\n+            require(burn <= data[\"stable_input\"], \"stable_input\")\n+            require(burn <= data[\"max_burn\"] and paid >= data[\"min_collateral\"], \"intent_bounds\")\n+            require(supply - burn == (supply - debt - bad) + (bad + debt - burn), \"liability_invariant\")\n+            if burn < debt:\n+                require((collateral - paid) * price < Fraction(21, 20), \"rounding_invariant\")\n+            return post(supply - burn, bad + debt - burn, None, None, burned=burn,\n+                        owner=collateral - paid, liquidator=paid)\n+\n+        amount = data[\"amount\"]\n+        require(amount > 0 and amount <= bad, \"retirement_amount\")\n+        require(amount <= data[\"stable_input\"], \"stable_input\")\n+        require((supply - amount) - (bad - amount) == supply - bad, \"liability_invariant\")\n+        return {\"accepted\": True, \"supply\": supply - amount, \"bad_debt\": bad - amount,\n+                \"burned\": amount, \"zec_payout\": 0, \"live_principal\": supply - bad}\n+    except Invalid as error:\n+        return {\"accepted\": False, \"reason\": str(error)}\n+\n+\n+def check(path):\n+    document = json.loads(path.read_text())\n+    if document[\"format\"] != \"zsv-zip-financial-vectors-v1\":\n+        raise ValueError(\"unsupported vector format\")\n+    if document[\"parameters\"] != PARAMETERS:\n+        raise ValueError(\"parameter mismatch\")\n+    # This domain cannot overflow the specified unsigned 128-bit intermediates.\n+    assert MAX_C * U64_MAX * 100 <= U128_MAX\n+    assert CAP * Q * 250 <= U128_MAX\n+    assert CAP * Q * 105 <= U128_MAX\n+    names = set()\n+    for case in document[\"cases\"]:\n+        if case[\"id\"] in names:\n+            raise ValueError(f\"duplicate case {case['id']}\")\n+        names.add(case[\"id\"])\n+        if type(case[\"expected\"][\"accepted\"]) is not bool:\n+            raise ValueError(f\"{case['id']}: accepted must be boolean\")\n+        actual = evaluate(case[\"operation\"], case[\"input\"])\n+        if actual != case[\"expected\"]:\n+            raise ValueError(f\"{case['id']}: expected {case['expected']}, computed {actual}\")\n+    print(f\"Financial ZIP vectors checked: {len(names)} cases; exact Fraction arithmetic and u128 bounds.\")\n+\n+\n+if __name__ == \"__main__\":\n+    if len(sys.argv) > 2:\n+        sys.exit(\"usage: check_financial_vectors.py [vectors.json]\")\n+    try:\n+        check(Path(sys.argv[1]) if len(sys.argv) == 2 else Path(__file__).with_name(\"financial-vectors.json\"))\n+    except (KeyError, ValueError, AssertionError) as error:\n+        sys.exit(str(error))"},{"filename":"zips/draft-ijaack-zsv/check_oracle_vector.py","status":"added","additions":266,"deletions":0,"patch":"@@ -0,0 +1,266 @@\n+#!/usr/bin/env python3\n+\"\"\"Check the ZIP's canonical oracle fixture; --write regenerates its exact JSON.\n+\n+Requires the optional `cryptography` package (python3 -m pip install cryptography).\n+This exercises ordinary canonical Ed25519 signatures, not ZIP 215's complete\n+acceptance set, noncanonical point encodings or a native transaction verifier.\n+All seeds and network fields below are explicitly public, synthetic fixtures.\n+\"\"\"\n+\n+import argparse\n+import hashlib\n+import json\n+import struct\n+from pathlib import Path\n+\n+try:\n+    from cryptography.exceptions import InvalidSignature\n+    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey\n+    from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\n+except ImportError as error:\n+    raise SystemExit(\"Optional dependency missing: python3 -m pip install cryptography\") from error\n+\n+\n+PATH = Path(__file__).with_name(\"oracle-vector.json\")\n+SET_PREFIX = b\"ZSV/OracleSet/v1\\0\"\n+REPORT_PREFIX = b\"ZSV/Oracle/v1\\0\"\n+U32_MAX = (1 << 32) - 1\n+U64_MAX = (1 << 64) - 1\n+FIELD_MODULUS = (1 << 255) - 19\n+SUBGROUP_ORDER = (1 << 252) + 27742317777372353535851937790883648493\n+\n+\n+def sha256(data):\n+    return hashlib.sha256(data).digest()\n+\n+\n+def set_id(keys):\n+    return sha256(SET_PREFIX + bytes([7, 5]) + b\"\".join(keys))\n+\n+\n+def body(height, ancestor, price):\n+    return struct.pack(\"<I\", height) + ancestor + struct.pack(\"<Q\", price)\n+\n+\n+def message(genesis, branch, roster_id, report_body):\n+    return REPORT_PREFIX + genesis + struct.pack(\"<I\", branch) + roster_id + report_body\n+\n+\n+def mask_indices(mask):\n+    if type(mask) is not int or not 0 <= mask < 128 or not 5 <= mask.bit_count() <= 7:\n+        raise ValueError(\"invalid signer mask\")\n+    return [index for index in range(7) if mask & (1 << index)]\n+\n+\n+def verify_subset(keys, signed_message, mask, signatures):\n+    indices = mask_indices(mask)\n+    if len(signatures) != len(indices):\n+        raise ValueError(\"signature count does not match mask\")\n+    for index, signature in zip(indices, signatures):\n+        keys[index].public_key().verify(signature, signed_message)\n+\n+\n+def context_valid(height, price, candidate, branch_start, previous, ancestor_matches=True):\n+    \"\"\"Check only the listed numeric/ancestry preconditions, not full consensus.\"\"\"\n+    return (\n+        type(price) is int and 1 <= price <= U64_MAX\n+        and type(height) is int and 0 <= height <= U32_MAX\n+        and 0 < branch_start <= candidate <= U32_MAX\n+        and branch_start - 1 <= height < candidate <= height + 12 <= U32_MAX\n+        and (previous is None or height > previous)\n+        and ancestor_matches\n+    )\n+\n+\n+def fixture():\n+    rows = []\n+    for seed_byte in range(1, 8):\n+        seed = bytes([seed_byte]) * 32\n+        key = Ed25519PrivateKey.from_private_bytes(seed)\n+        public = key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)\n+        rows.append((public, seed, key))\n+    rows.sort(key=lambda row: row[0])\n+    public_keys = [row[0] for row in rows]\n+    keys = [row[2] for row in rows]\n+    roster_bytes = SET_PREFIX + bytes([7, 5]) + b\"\".join(public_keys)\n+    roster_id = set_id(public_keys)\n+    genesis = bytes([0x11]) * 32\n+    branch = 0xA1B2C3D4\n+    ancestor = bytes([0x22]) * 32\n+    height, price = 100, 100_000_000\n+    report_body = body(height, ancestor, price)\n+    signed_message = message(genesis, branch, roster_id, report_body)\n+    signatures = [key.sign(signed_message) for key in keys]\n+    mask = 0x1F\n+    document = {\n+        \"format\": \"zsv-zip-canonical-oracle-vector-v1\",\n+        \"scope\": \"Public deterministic fixture keys and synthetic context only; no deployment key or branch-ID allocation. Canonical signatures exercise common Ed25519 behavior, not all ZIP 215 edge cases.\",\n+        \"genesis_hash_hex\": genesis.hex(),\n+        \"active_branch_id_u32\": branch,\n+        \"active_branch_start_height\": 90,\n+        \"candidate_height\": 101,\n+        \"prior_observed_height\": 99,\n+        \"keys_sorted\": [\n+            {\"index\": index, \"public_key_hex\": row[0].hex(), \"public_fixture_seed_hex\": row[1].hex()}\n+            for index, row in enumerate(rows)\n+        ],\n+        \"roster_bytes_hex\": roster_bytes.hex(),\n+        \"reporter_set_id_hex\": roster_id.hex(),\n+        \"observed_height\": height,\n+        \"observed_block_hash_hex\": ancestor.hex(),\n+        \"price_micro_usd_per_zec\": price,\n+        \"valid_from_height\": 102,\n+        \"valid_through_height\": 112,\n+        \"body_hex\": report_body.hex(),\n+        \"signing_message_hex\": signed_message.hex(),\n+        \"oracle_id_hex\": sha256(signed_message).hex(),\n+        \"signer_mask_u8\": mask,\n+        \"effect_hex\": (report_body + bytes([mask])).hex(),\n+        \"authorization_hex\": b\"\".join(signatures[:5]).hex(),\n+        \"signatures_by_index\": [\n+            {\"index\": index, \"signature_hex\": signature.hex()}\n+            for index, signature in enumerate(signatures)\n+        ],\n+        \"expected_accept\": True,\n+    }\n+    return document, keys\n+\n+\n+def expect_failure(call):\n+    try:\n+        call()\n+    except (ValueError, InvalidSignature):\n+        return\n+    raise AssertionError(\"invalid mutation was accepted\")\n+\n+\n+def check(document, keys):\n+    report_body = bytes.fromhex(document[\"body_hex\"])\n+    signed_message = bytes.fromhex(document[\"signing_message_hex\"])\n+    signatures = [bytes.fromhex(row[\"signature_hex\"]) for row in document[\"signatures_by_index\"]]\n+    assert len(report_body) == 44 and len(signed_message) == 126\n+    assert len(bytes.fromhex(document[\"effect_hex\"])) == 45\n+    assert len(bytes.fromhex(document[\"authorization_hex\"])) == 320\n+    assert len(set(row[\"public_key_hex\"] for row in document[\"keys_sorted\"])) == 7\n+    for key, signature in zip(keys, signatures):\n+        assert len(signature) == 64\n+        assert (int.from_bytes(signature[:32], \"little\") & ((1 << 255) - 1)) < FIELD_MODULUS\n+        assert int.from_bytes(signature[32:], \"little\") < SUBGROUP_ORDER\n+        key.public_key().verify(signature, signed_message)\n+\n+    for mask in (0x1F, 0x3F, 0x7F, 0x7C):\n+        selected = [signatures[index] for index in mask_indices(mask)]\n+        verify_subset(keys, signed_message, mask, selected)\n+        effect = report_body + bytes([mask])\n+        reconstructed_message = signed_message[:-44] + effect[:44]\n+        assert sha256(reconstructed_message) == bytes.fromhex(document[\"oracle_id_hex\"])\n+        assert len(effect) + sum(map(len, selected)) == 45 + 64 * mask.bit_count()\n+\n+    # Mutate each signed field: genesis, branch, roster, height, ancestor, price.\n+    for offset in (14, 46, 50, 82, 86, 118):\n+        mutated = bytearray(signed_message)\n+        mutated[offset] ^= 1\n+        expect_failure(lambda: keys[0].public_key().verify(signatures[0], bytes(mutated)))\n+    mutated_signature = bytearray(signatures[0])\n+    mutated_signature[0] ^= 1\n+    expect_failure(lambda: keys[0].public_key().verify(bytes(mutated_signature), signed_message))\n+    expect_failure(lambda: keys[1].public_key().verify(signatures[0], signed_message))\n+\n+    for invalid_mask in (0, 0x0F, 0x9F, 0xFF):\n+        expect_failure(lambda: mask_indices(invalid_mask))\n+    expect_failure(lambda: verify_subset(keys, signed_message, 0x1F, signatures[:4]))\n+    expect_failure(lambda: verify_subset(keys, signed_message, 0x1F, signatures[:6]))\n+    expect_failure(lambda: verify_subset(keys, signed_message, 0x1F, list(reversed(signatures[:5]))))\n+    expect_failure(lambda: verify_subset(keys, signed_message, 0x3F, signatures[:5] + [bytes(mutated_signature)]))\n+\n+    cases = [\n+        ((100, 100_000_000, 100, 90, 99), False),\n+        ((100, 100_000_000, 101, 90, 99), True),\n+        ((100, 100_000_000, 112, 90, 99), True),\n+        ((100, 100_000_000, 113, 90, 99), False),\n+        ((100, 100_000_000, 101, 90, 99, False), False),\n+        ((88, 100_000_000, 90, 90, None), False),\n+        ((89, 100_000_000, 90, 90, None), True),\n+        ((100, 100_000_000, 101, 90, 100), False),\n+        ((100, 0, 101, 90, 99), False),\n+        ((100, U64_MAX, 101, 90, 99), True),\n+        ((100, U64_MAX + 1, 101, 90, 99), False),\n+        ((U32_MAX - 1, 100_000_000, U32_MAX, 90, None), False),\n+    ]\n+    for arguments, expected in cases:\n+        assert context_valid(*arguments) is expected, arguments\n+    return len(cases)\n+\n+\n+def block_oracle(parent, accepted, height, domain_changed=False):\n+    \"\"\"Oracle selection only; inputs are already-validated (ID, observed height).\n+\n+    This fixture helper does not validate a block, ancestry or certificate.\n+    \"\"\"\n+    parent = None if domain_changed else parent\n+    pricing = parent if parent is not None and height <= parent[1] + 12 else None\n+    return pricing, accepted if accepted is not None else parent\n+\n+\n+def check_rollover(document, keys):\n+    old_message = bytes.fromhex(document[\"signing_message_hex\"])\n+    old = (sha256(old_message), 100)\n+    # Same price, later observation and synthetic ancestor: both quorums verify.\n+    new_message = old_message[:-44] + body(101, bytes([0x33]) * 32, 100_000_000)\n+    verify_subset(keys, new_message, 0x1F, [key.sign(new_message) for key in keys[:5]])\n+    new = (sha256(new_message), 101)\n+    assert old[0] != new[0]\n+    assert context_valid(101, 100_000_000, 102, 90, 100)\n+    crash_message = old_message[:-44] + body(101, bytes([0x33]) * 32, 20_000_000)\n+    verify_subset(keys, crash_message, 0x1F, [key.sign(crash_message) for key in keys[:5]])\n+    crash = (sha256(crash_message), 101)\n+    next_domain_message = message(bytes.fromhex(document[\"genesis_hash_hex\"]),\n+                                  document[\"active_branch_id_u32\"] + 1,\n+                                  bytes.fromhex(document[\"reporter_set_id_hex\"]),\n+                                  body(101, bytes([0x33]) * 32, 100_000_000))\n+    verify_subset(keys, next_domain_message, 0x1F,\n+                  [key.sign(next_domain_message) for key in keys[:5]])\n+    next_domain = (sha256(next_domain_message), 101)\n+    cases = [\n+        # (parent, newly accepted, block height, domain change), (pricing, committed)\n+        ((None, old, 101, False), (None, old)),\n+        ((old, new, 102, False), (old, new)),\n+        ((new, None, 103, False), (new, new)),\n+        ((old, None, 102, False), (old, old)),\n+        ((old, None, 112, False), (old, old)),\n+        ((old, None, 113, False), (None, old)),\n+        ((old, new, 113, False), (None, new)),\n+        ((new, None, 114, False), (None, new)),\n+        ((old, None, 102, True), (None, None)),\n+        # A new-domain certificate must be verified separately before this helper.\n+        ((old, next_domain, 102, True), (None, next_domain)),\n+        ((old, crash, 102, False), (old, crash)),\n+        ((crash, None, 103, False), (crash, crash)),\n+        # Replaying a sibling uses its own parent, not the selected tip's report.\n+        ((old, None, 103, False), (old, old)),\n+    ]\n+    for arguments, expected in cases:\n+        assert block_oracle(*arguments) == expected, arguments\n+    return len(cases)\n+\n+\n+def main():\n+    parser = argparse.ArgumentParser(description=__doc__)\n+    parser.add_argument(\"--write\", action=\"store_true\", help=\"regenerate the checked-in fixture\")\n+    args = parser.parse_args()\n+    document, keys = fixture()\n+    boundary_count = check(document, keys)\n+    rollover_count = check_rollover(document, keys)\n+    encoded = json.dumps(document, indent=2) + \"\\n\"\n+    if args.write:\n+        PATH.write_text(encoded)\n+    if PATH.read_text() != encoded:\n+        raise ValueError(\"oracle-vector.json differs from its deterministic fixture\")\n+    print(f\"Oracle fixture checked: 7 canonical signers, 4 quorum subsets, 16 rejection probes, {boundary_count} context cases; {rollover_count} block-selection cases; exact JSON match.\")\n+\n+\n+if __name__ == \"__main__\":\n+    try:\n+        main()\n+    except (OSError, ValueError, AssertionError) as error:\n+        raise SystemExit(str(error)) from error"},{"filename":"zips/draft-ijaack-zsv/check_wire_vectors.py","status":"added","additions":213,"deletions":0,"patch":"@@ -0,0 +1,213 @@\n+\"\"\"Unsigned ZSV draft vectors; fictional identifiers, no consensus allocations.\n+\n+Standard-library encoding/hash examples, not a node verifier or signature test.\n+The default checks the adjacent wire-vectors.json without changing it.\n+Use --check PATH for another file, or --write to regenerate the adjacent file.\n+\"\"\"\n+import argparse\n+import hashlib\n+import json\n+import struct\n+from pathlib import Path\n+\n+# Deliberately fictional test-network allocations, never deployment values.\n+TEST_VERSION = 0x7FFFFFFE\n+TEST_VERSION_GROUP_ID = 0x5A535600\n+TEST_BRANCH_ID = 0x01020304\n+TEST_GENESIS = bytes.fromhex('44' * 32)\n+OWNER = bytes.fromhex('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')\n+LIQUIDATOR = bytes.fromhex('02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5')\n+PATH = Path(__file__).with_name(\"wire-vectors.json\")\n+\n+\n+def u16(n):\n+    return struct.pack('<H', n)\n+\n+\n+def u32(n):\n+    return struct.pack('<I', n)\n+\n+\n+def u64(n):\n+    return struct.pack('<Q', n)\n+\n+\n+def count(n):\n+    assert 0 <= n <= 64\n+    return bytes([n])\n+\n+\n+def h(label, value):\n+    label = label.encode('ascii')\n+    assert len(label) <= 16\n+    return hashlib.blake2b(value, digest_size=32, person=label.ljust(16, b'\\0')).digest()\n+\n+\n+def coin(previous_byte, index, amount, key):\n+    return (bytes([previous_byte]) * 32 + u32(index), amount, key)\n+\n+\n+def body(zec_inputs, zec_outputs, stable_inputs, stable_outputs, tag, payload, oracle_effects=None):\n+    assert len(payload) == {0: 0, 1: 81, 2: 84, 3: 36, 4: 121, 5: 8}[tag]\n+    header = b''.join(u32(v) for v in (\n+        TEST_VERSION | 0x80000000, TEST_VERSION_GROUP_ID,\n+        TEST_BRANCH_ID, 0, 100))\n+    fields = [header]\n+    for inputs, outputs in ((zec_inputs, zec_outputs), (stable_inputs, stable_outputs)):\n+        fields.append(count(len(inputs)))\n+        fields.extend(outpoint for outpoint, _, _ in inputs)\n+        fields.append(count(len(outputs)))\n+        fields.extend(u64(amount) + key for amount, key in outputs)\n+    fields.extend((bytes([tag]), payload, bytes([oracle_effects is not None])))\n+    if oracle_effects is not None:\n+        assert len(oracle_effects) == 45\n+        assert tag == 0 and not stable_inputs and not stable_outputs\n+        assert oracle_effects[-1] < 128 and 5 <= oracle_effects[-1].bit_count() <= 7\n+        fields.append(oracle_effects)\n+    return b''.join(fields)\n+\n+\n+def context(zec_inputs, stable_inputs, previous_vault):\n+    fields = []\n+    for inputs in (zec_inputs, stable_inputs):\n+        fields.append(count(len(inputs)))\n+        fields.extend(outpoint + u64(amount) + key for outpoint, amount, key in inputs)\n+    fields.append(bytes([previous_vault is not None]))\n+    if previous_vault:\n+        reference, key, collateral, principal = previous_vault\n+        fields.append(reference + key + u64(collateral) + u64(principal))\n+    return b''.join(fields)\n+\n+\n+def vector(name, zec_inputs, zec_outputs, stable_inputs, stable_outputs,\n+           tag, payload, previous_vault=None, action_key=None,\n+           collateral_after=0, minted=0, burned=0, oracle_effects=None, payouts=()):\n+    effects = body(zec_inputs, zec_outputs, stable_inputs, stable_outputs, tag, payload, oracle_effects)\n+    resolved = context(zec_inputs, stable_inputs, previous_vault)\n+    txid = h('ZSV_TxId_v1', TEST_GENESIS + effects)\n+    context_hash = h('ZSV_Context_v1', resolved)\n+    roles = [(0, i, key) for i, (_, _, key) in enumerate(zec_inputs)]\n+    roles += [(1, i, key) for i, (_, _, key) in enumerate(stable_inputs)]\n+    if action_key:\n+        roles.append((2, 0, action_key))\n+    digests = [{\n+        'role': role, 'index': index, 'verification_key_hex': key.hex(),\n+        'digest_hex': h('ZSV_Sign_v1', TEST_GENESIS + txid + context_hash\n+                        + bytes([role]) + u16(index)).hex(),\n+    } for role, index, key in roles]\n+    collateral_before = previous_vault[2] if previous_vault else 0\n+    fee = sum(c[1] for c in zec_inputs) + collateral_before - collateral_after - sum(c[0] for c in zec_outputs)\n+    stable_in = sum(c[1] for c in stable_inputs)\n+    stable_out = sum(c[0] for c in stable_outputs)\n+    assert fee >= 0\n+    assert stable_in + minted == stable_out + burned\n+    assert len({index for index, _, _ in payouts}) == len(payouts)\n+    for index, key, amount in payouts:\n+        assert amount > 0 and zec_outputs[index] == (amount, key)\n+    result = {\n+        'name': name, 'operation_tag': tag, 'effects_length': len(effects),\n+        'effects_hex': effects.hex(), 'txid_hex': txid.hex(),\n+        'resolved_context_hex': resolved.hex(), 'context_hash_hex': context_hash.hex(),\n+        'signature_digests': digests,\n+        'value_check': {\n+            'zec_input': sum(c[1] for c in zec_inputs),\n+            'zec_output': sum(c[0] for c in zec_outputs),\n+            'collateral_before': collateral_before, 'collateral_after': collateral_after,\n+            'zec_fee': fee, 'stable_input': stable_in, 'stable_output': stable_out,\n+            'minted': minted, 'burned': burned,\n+        },\n+    }\n+    if payouts:\n+        result['mandatory_payouts'] = [\n+            {'index': index, 'recipient_key_hex': key.hex(), 'value': amount}\n+            for index, key, amount in payouts\n+        ]\n+    if oracle_effects is not None:\n+        slots = oracle_effects[-1].bit_count()\n+        result['oracle_effect_check'] = {\n+            'effect_hex': oracle_effects.hex(), 'effect_length': len(oracle_effects),\n+            'expected_secp256k1_signature_slots': len(digests),\n+            'expected_ed25519_signature_slots': slots,\n+            'expected_total_signature_slots': len(digests) + slots,\n+            'expected_ed25519_authorization_bytes': 64 * slots,\n+            'scope': 'Unsigned certificate framing only. No reporter signatures are supplied or verified; this fictional context is separate from oracle-vector.json.',\n+        }\n+    return result\n+\n+\n+def vectors():\n+    zero_oracle = bytes(32)\n+    previous_vault = (bytes.fromhex('55' * 32), OWNER, 1_000_000_000, 400_000_000)\n+    liquidation = (previous_vault[0] + bytes.fromhex('66' * 32)\n+                   + u64(400_000_000) + u64(840_000_000)\n+                   + LIQUIDATOR + u32(2) + u32(1))\n+    update = (previous_vault[0] + u64(800_000_000) + u64(300_000_000)\n+              + bytes.fromhex('88' * 32) + u32(1))\n+    # Unlike oracle-vector.json's signed context, this framing example has no\n+    # supplied reporter roster or signatures. A height91 inclusion fits expiry100.\n+    oracle_effects = u32(90) + bytes.fromhex('99' * 32) + u64(100_000_000) + b'\\x1f'\n+    return {\n+        'scope': 'Unsigned effect/context/hash vectors only; no signatures, node execution or assigned network identifiers. Previous outputs, prior vaults and operation oracle IDs are fictional context inputs, not certified chain state.',\n+        'hash_hex_order': 'Raw digest bytes; not reversed display hashes.',\n+        'test_allocations': {\n+            'version': TEST_VERSION, 'version_group_id': TEST_VERSION_GROUP_ID,\n+            'branch_id': TEST_BRANCH_ID, 'genesis_hex': TEST_GENESIS.hex(),\n+            'expiry_height': 100,\n+        },\n+        'cases': [\n+            vector('open_debt_free',\n+                   [coin(0x22, 3, 1_100_000_000, OWNER)], [(99_990_000, OWNER)], [], [],\n+                   1, OWNER + u64(1_000_000_000) + u64(0) + zero_oracle,\n+                   action_key=OWNER, collateral_after=1_000_000_000),\n+            vector('stable_transfer',\n+                   [coin(0x33, 1, 100_000_000, OWNER)], [(99_990_000, OWNER)],\n+                   [coin(0x77, 2, 400_000_000, OWNER)],\n+                   [(125_000_000, LIQUIDATOR), (275_000_000, OWNER)], 0, b''),\n+            vector('solvent_liquidation_at_50',\n+                   [coin(0x33, 1, 100_000_000, LIQUIDATOR)],\n+                   [(99_990_000, LIQUIDATOR), (840_000_000, LIQUIDATOR), (160_000_000, OWNER)],\n+                   [coin(0x77, 2, 400_000_000, LIQUIDATOR)], [], 4, liquidation,\n+                   previous_vault=previous_vault, action_key=LIQUIDATOR, burned=400_000_000,\n+                   payouts=[(1, LIQUIDATOR, 840_000_000), (2, OWNER, 160_000_000)]),\n+            vector('retire_bad_debt',\n+                   [coin(0x33, 1, 100_000_000, OWNER)], [(99_990_000, OWNER)],\n+                   [coin(0x77, 2, 400_000_000, OWNER)], [(300_000_000, OWNER)],\n+                   5, u64(100_000_000), burned=100_000_000),\n+            vector('update_repay_and_withdraw',\n+                   [coin(0x33, 1, 100_000_000, OWNER)],\n+                   [(99_990_000, OWNER), (200_000_000, OWNER)],\n+                   [coin(0x77, 2, 100_000_000, OWNER)], [], 2, update,\n+                   previous_vault=previous_vault, action_key=OWNER,\n+                   collateral_after=800_000_000, burned=100_000_000,\n+                   payouts=[(1, OWNER, 200_000_000)]),\n+            vector('close_with_full_repayment',\n+                   [coin(0x33, 1, 100_000_000, OWNER)],\n+                   [(99_990_000, OWNER), (1_000_000_000, OWNER)],\n+                   [coin(0x77, 2, 400_000_000, OWNER)], [],\n+                   3, previous_vault[0] + u32(1),\n+                   previous_vault=previous_vault, action_key=OWNER,\n+                   burned=400_000_000, payouts=[(1, OWNER, 1_000_000_000)]),\n+            vector('oracle_certificate_effects',\n+                   [coin(0x33, 1, 100_000_000, OWNER)], [(99_990_000, OWNER)],\n+                   [], [], 0, b'', oracle_effects=oracle_effects),\n+        ],\n+    }\n+\n+\n+def main():\n+    parser = argparse.ArgumentParser(description=__doc__)\n+    parser.add_argument('--check', type=Path, nargs='?', const=PATH, default=PATH,\n+                        help='compare a vector file; defaults to the adjacent file')\n+    parser.add_argument('--write', action='store_true',\n+                        help='regenerate the adjacent checked-in vector file')\n+    args = parser.parse_args()\n+    actual = json.dumps(vectors(), indent=2, sort_keys=True) + '\\n'\n+    if args.write:\n+        PATH.write_text(actual)\n+    if args.check.read_text() != actual:\n+        raise SystemExit(f'Vector mismatch: {args.check}')\n+    print('Seven unsigned ZSV wire/context/hash vectors match: all six operation tags and one oracle effect body.')\n+\n+\n+if __name__ == '__main__':\n+    main()"},{"filename":"zips/draft-ijaack-zsv/financial-vectors.json","status":"added","additions":1134,"deletions":0,"patch":"@@ -0,0 +1,1134 @@\n+{\n+  \"format\": \"zsv-zip-financial-vectors-v1\",\n+  \"scope\": \"Candidate ZIP arithmetic only. JSON is not a native encoding. oracle_fresh is an assumed validated precondition; signatures, ownership, prevouts, fee funding, oracle certificates, transaction bytes and full consensus are not checked. Rejection means the financial transition is not committed. RetireBadDebt is proposed behavior absent from the existing Rust model.\",\n+  \"parameters\": {\n+    \"zatoshi_per_zec\": 100000000,\n+    \"micro_units_per_zusd\": 1000000,\n+    \"max_collateral\": 2100000000000000,\n+    \"max_price\": 18446744073709551615,\n+    \"min_nonzero_principal\": 100000000,\n+    \"supply_cap\": 100000000000,\n+    \"borrow_ratio\": \"5/2\",\n+    \"liquidation_ratio\": \"2/1\",\n+    \"liquidation_payout_multiplier\": \"21/20\",\n+    \"intermediate_bits\": 128\n+  },\n+  \"cases\": [\n+    {\n+      \"id\": \"open_borrow_exact_250_percent\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 100000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 250000000,\n+          \"principal\": 100000000\n+        },\n+        \"minted\": 100000000,\n+        \"burned\": 0,\n+        \"zec_deposited\": 250000000,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"open_borrow_one_zatoshi_below_ratio\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 249999999,\n+        \"principal\": 100000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"collateral_ratio\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_nonzero_debt_below_minimum\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 99999999,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"minimum_debt\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_collateral_only_without_oracle\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 1,\n+        \"principal\": 0,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 1,\n+          \"principal\": 0\n+        },\n+        \"minted\": 0,\n+        \"burned\": 0,\n+        \"zec_deposited\": 1,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"open_zero_collateral\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 0,\n+        \"principal\": 0,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"collateral\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_collateral_only_during_bad_debt\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 1,\n+        \"principal\": 0,\n+        \"supply\": 1000000,\n+        \"bad_debt\": 1000000,\n+        \"price\": null,\n+        \"oracle_fresh\": false\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 1000000,\n+        \"bad_debt\": 1000000,\n+        \"vault\": {\n+          \"collateral\": 1,\n+          \"principal\": 0\n+        },\n+        \"minted\": 0,\n+        \"burned\": 0,\n+        \"zec_deposited\": 1,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"open_borrow_during_bad_debt\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 100000000,\n+        \"supply\": 1,\n+        \"bad_debt\": 1,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"bad_debt_freeze\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_exact_supply_cap\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 100000000,\n+        \"supply\": 99900000000,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 250000000,\n+          \"principal\": 100000000\n+        },\n+        \"minted\": 100000000,\n+        \"burned\": 0,\n+        \"zec_deposited\": 250000000,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"open_one_unit_above_supply_cap\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 100000000,\n+        \"supply\": 99900000001,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"supply_cap\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_borrow_without_fresh_observation\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 100000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": false\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"oracle\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_zero_price\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 100000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 0,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"range\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_negative_collateral\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": -1,\n+        \"principal\": 100000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"range\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_collateral_above_native_maximum\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 2100000000000001,\n+        \"principal\": 100000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"range\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_price_above_u64\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 250000000,\n+        \"principal\": 100000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 18446744073709551616,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"range\"\n+      }\n+    },\n+    {\n+      \"id\": \"open_valid_domain_maximum_products\",\n+      \"operation\": \"open\",\n+      \"input\": {\n+        \"collateral\": 2100000000000000,\n+        \"principal\": 100000000000,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 18446744073709551615,\n+        \"oracle_fresh\": true\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 2100000000000000,\n+          \"principal\": 100000000000\n+        },\n+        \"minted\": 100000000000,\n+        \"burned\": 0,\n+        \"zec_deposited\": 2100000000000000,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"update_borrow_exact_ratio\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 1000000000,\n+        \"new_principal\": 400000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 1000000000,\n+          \"principal\": 400000000\n+        },\n+        \"minted\": 300000000,\n+        \"burned\": 0,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"update_borrow_one_unit_above_ratio\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 1000000000,\n+        \"new_principal\": 400000001,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"collateral_ratio\"\n+      }\n+    },\n+    {\n+      \"id\": \"update_borrow_frozen_at_entry\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 1000000000,\n+        \"new_principal\": 400000000,\n+        \"supply\": 100000001,\n+        \"bad_debt\": 1,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"bad_debt_freeze\"\n+      }\n+    },\n+    {\n+      \"id\": \"update_withdraw_exact_ratio\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 250000000,\n+        \"new_principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 250000000,\n+          \"principal\": 100000000\n+        },\n+        \"minted\": 0,\n+        \"burned\": 0,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 750000000,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"update_withdraw_one_zatoshi_too_far\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 249999999,\n+        \"new_principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"collateral_ratio\"\n+      }\n+    },\n+    {\n+      \"id\": \"update_deposit_without_oracle\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 1100000000,\n+        \"new_principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 1100000000,\n+          \"principal\": 100000000\n+        },\n+        \"minted\": 0,\n+        \"burned\": 0,\n+        \"zec_deposited\": 100000000,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"update_repay_without_oracle\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 200000000,\n+        \"new_collateral\": 1000000000,\n+        \"new_principal\": 100000000,\n+        \"supply\": 200000000,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 1000000000,\n+          \"principal\": 100000000\n+        },\n+        \"minted\": 0,\n+        \"burned\": 100000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"update_repay_and_withdraw\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 200000000,\n+        \"new_collateral\": 250000000,\n+        \"new_principal\": 100000000,\n+        \"supply\": 200000000,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 250000000,\n+          \"principal\": 100000000\n+        },\n+        \"minted\": 0,\n+        \"burned\": 100000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 750000000,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"update_full_repay_and_debt_free_withdraw\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 1,\n+        \"new_principal\": 0,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"vault\": {\n+          \"collateral\": 1,\n+          \"principal\": 0\n+        },\n+        \"minted\": 0,\n+        \"burned\": 100000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 999999999,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"update_repay_leaves_below_minimum\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 200000000,\n+        \"new_collateral\": 1000000000,\n+        \"new_principal\": 99999999,\n+        \"supply\": 200000000,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false,\n+        \"stable_input\": 101000000\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"minimum_debt\"\n+      }\n+    },\n+    {\n+      \"id\": \"update_repay_without_enough_tokens\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 1000000000,\n+        \"new_principal\": 0,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false,\n+        \"stable_input\": 99999999\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"stable_input\"\n+      }\n+    },\n+    {\n+      \"id\": \"update_no_change\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 1000000000,\n+        \"new_principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": 100000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"no_change\"\n+      }\n+    },\n+    {\n+      \"id\": \"update_zero_collateral_requires_close\",\n+      \"operation\": \"update\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"new_collateral\": 0,\n+        \"new_principal\": 0,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": null,\n+        \"oracle_fresh\": false,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"collateral\"\n+      }\n+    },\n+    {\n+      \"id\": \"close_repay_and_return_all_collateral\",\n+      \"operation\": \"close\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 100000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 1000000000,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"close_debt_free\",\n+      \"operation\": \"close\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 0,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"stable_input\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 0,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 1000000000,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"close_insufficient_tokens\",\n+      \"operation\": \"close\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"stable_input\": 99999999\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"stable_input\"\n+      }\n+    },\n+    {\n+      \"id\": \"close_preserves_unrelated_bad_debt\",\n+      \"operation\": \"close\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 100000000,\n+        \"supply\": 100000001,\n+        \"bad_debt\": 1,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 1,\n+        \"bad_debt\": 1,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 100000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 1000000000,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_exact_200_percent_is_healthy\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 80000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"healthy\"\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_just_below_200_percent_rounds_up\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 79999999,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 400000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 474999993,\n+        \"liquidator_zec\": 525000007\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_solvent\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 50000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 400000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 160000000,\n+        \"liquidator_zec\": 840000000\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_crash_floor_and_ceiling\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 20000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 209523810,\n+        \"bad_debt\": 209523810,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 190476190,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 2,\n+        \"liquidator_zec\": 999999998\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_crash_preserves_other_liabilities\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 550000000,\n+        \"bad_debt\": 50000000,\n+        \"price\": 20000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 359523810,\n+        \"bad_debt\": 259523810,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 190476190,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 2,\n+        \"liquidator_zec\": 999999998\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_no_fresh_observation\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 50000000,\n+        \"oracle_fresh\": false,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"oracle\"\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_insufficient_tokens\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 50000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 399999999,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"stable_input\"\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_maximum_burn_bound\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 50000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 399999999,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"intent_bounds\"\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_minimum_collateral_bound\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 50000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 840000001\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"intent_bounds\"\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_exact_intent_bounds\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 400000000,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"price\": 50000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 400000000,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 840000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 400000000,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 160000000,\n+        \"liquidator_zec\": 840000000\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_zero_affordable_burn_dust\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1,\n+        \"principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": 1,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0,\n+        \"max_burn\": 0,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 100000000,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 0,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 1,\n+        \"liquidator_zec\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_one_micro_unit_exact_collateral\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 105,\n+        \"principal\": 100000000,\n+        \"supply\": 100000000,\n+        \"bad_debt\": 0,\n+        \"price\": 1000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 1,\n+        \"max_burn\": 1,\n+        \"min_collateral\": 105\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 99999999,\n+        \"bad_debt\": 99999999,\n+        \"vault\": null,\n+        \"minted\": 0,\n+        \"burned\": 1,\n+        \"zec_deposited\": 0,\n+        \"owner_zec\": 0,\n+        \"liquidator_zec\": 105\n+      }\n+    },\n+    {\n+      \"id\": \"liquidate_debt_free_is_healthy\",\n+      \"operation\": \"liquidate\",\n+      \"input\": {\n+        \"collateral\": 1000000000,\n+        \"principal\": 0,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"price\": 50000000,\n+        \"oracle_fresh\": true,\n+        \"stable_input\": 0,\n+        \"max_burn\": 400000000,\n+        \"min_collateral\": 0\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"healthy\"\n+      }\n+    },\n+    {\n+      \"id\": \"retire_partial_bad_debt\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 500000000,\n+        \"bad_debt\": 100000000,\n+        \"amount\": 40000000,\n+        \"stable_input\": 40000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 460000000,\n+        \"bad_debt\": 60000000,\n+        \"burned\": 40000000,\n+        \"zec_payout\": 0,\n+        \"live_principal\": 400000000\n+      }\n+    },\n+    {\n+      \"id\": \"retire_all_bad_debt\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 500000000,\n+        \"bad_debt\": 100000000,\n+        \"amount\": 100000000,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 400000000,\n+        \"bad_debt\": 0,\n+        \"burned\": 100000000,\n+        \"zec_payout\": 0,\n+        \"live_principal\": 400000000\n+      }\n+    },\n+    {\n+      \"id\": \"retire_no_active_principal\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 100000000,\n+        \"bad_debt\": 100000000,\n+        \"amount\": 100000000,\n+        \"stable_input\": 100000000\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"burned\": 100000000,\n+        \"zec_payout\": 0,\n+        \"live_principal\": 0\n+      }\n+    },\n+    {\n+      \"id\": \"retire_zero_rejected\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 500000000,\n+        \"bad_debt\": 100000000,\n+        \"amount\": 0,\n+        \"stable_input\": 40000000\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"retirement_amount\"\n+      }\n+    },\n+    {\n+      \"id\": \"retire_above_bad_debt_rejected\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 500000000,\n+        \"bad_debt\": 100000000,\n+        \"amount\": 100000001,\n+        \"stable_input\": 100000001\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"retirement_amount\"\n+      }\n+    },\n+    {\n+      \"id\": \"retire_actual_tokens_required\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 500000000,\n+        \"bad_debt\": 100000000,\n+        \"amount\": 40000000,\n+        \"stable_input\": 39999999\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"stable_input\"\n+      }\n+    },\n+    {\n+      \"id\": \"retire_no_bad_debt_rejected\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 500000000,\n+        \"bad_debt\": 0,\n+        \"amount\": 40000000,\n+        \"stable_input\": 40000000\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"retirement_amount\"\n+      }\n+    },\n+    {\n+      \"id\": \"retire_bad_debt_cannot_exceed_supply\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 99000000,\n+        \"bad_debt\": 100000000,\n+        \"amount\": 40000000,\n+        \"stable_input\": 40000000\n+      },\n+      \"expected\": {\n+        \"accepted\": false,\n+        \"reason\": \"prestate\"\n+      }\n+    },\n+    {\n+      \"id\": \"retire_one_micro_unit\",\n+      \"operation\": \"retire_bad_debt\",\n+      \"input\": {\n+        \"supply\": 1,\n+        \"bad_debt\": 1,\n+        \"amount\": 1,\n+        \"stable_input\": 1\n+      },\n+      \"expected\": {\n+        \"accepted\": true,\n+        \"supply\": 0,\n+        \"bad_debt\": 0,\n+        \"burned\": 1,\n+        \"zec_payout\": 0,\n+        \"live_principal\": 0\n+      }\n+    }\n+  ]\n+}"},{"filename":"zips/draft-ijaack-zsv/oracle-vector.json","status":"added","additions":90,"deletions":0,"patch":"@@ -0,0 +1,90 @@\n+{\n+  \"format\": \"zsv-zip-canonical-oracle-vector-v1\",\n+  \"scope\": \"Public deterministic fixture keys and synthetic context only; no deployment key or branch-ID allocation. Canonical signatures exercise common Ed25519 behavior, not all ZIP 215 edge cases.\",\n+  \"genesis_hash_hex\": \"1111111111111111111111111111111111111111111111111111111111111111\",\n+  \"active_branch_id_u32\": 2712847316,\n+  \"active_branch_start_height\": 90,\n+  \"candidate_height\": 101,\n+  \"prior_observed_height\": 99,\n+  \"keys_sorted\": [\n+    {\n+      \"index\": 0,\n+      \"public_key_hex\": \"6e7a1cdd29b0b78fd13af4c5598feff4ef2a97166e3ca6f2e4fbfccd80505bf1\",\n+      \"public_fixture_seed_hex\": \"0505050505050505050505050505050505050505050505050505050505050505\"\n+    },\n+    {\n+      \"index\": 1,\n+      \"public_key_hex\": \"8139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394\",\n+      \"public_fixture_seed_hex\": \"0202020202020202020202020202020202020202020202020202020202020202\"\n+    },\n+    {\n+      \"index\": 2,\n+      \"public_key_hex\": \"8a875fff1eb38451577acd5afee405456568dd7c89e090863a0557bc7af49f17\",\n+      \"public_fixture_seed_hex\": \"0606060606060606060606060606060606060606060606060606060606060606\"\n+    },\n+    {\n+      \"index\": 3,\n+      \"public_key_hex\": \"8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c\",\n+      \"public_fixture_seed_hex\": \"0101010101010101010101010101010101010101010101010101010101010101\"\n+    },\n+    {\n+      \"index\": 4,\n+      \"public_key_hex\": \"ca93ac1705187071d67b83c7ff0efe8108e8ec4530575d7726879333dbdabe7c\",\n+      \"public_fixture_seed_hex\": \"0404040404040404040404040404040404040404040404040404040404040404\"\n+    },\n+    {\n+      \"index\": 5,\n+      \"public_key_hex\": \"ea4a6c63e29c520abef5507b132ec5f9954776aebebe7b92421eea691446d22c\",\n+      \"public_fixture_seed_hex\": \"0707070707070707070707070707070707070707070707070707070707070707\"\n+    },\n+    {\n+      \"index\": 6,\n+      \"public_key_hex\": \"ed4928c628d1c2c6eae90338905995612959273a5c63f93636c14614ac8737d1\",\n+      \"public_fixture_seed_hex\": \"0303030303030303030303030303030303030303030303030303030303030303\"\n+    }\n+  ],\n+  \"roster_bytes_hex\": \"5a53562f4f7261636c655365742f76310007056e7a1cdd29b0b78fd13af4c5598feff4ef2a97166e3ca6f2e4fbfccd80505bf18139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b3948a875fff1eb38451577acd5afee405456568dd7c89e090863a0557bc7af49f178a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5cca93ac1705187071d67b83c7ff0efe8108e8ec4530575d7726879333dbdabe7cea4a6c63e29c520abef5507b132ec5f9954776aebebe7b92421eea691446d22ced4928c628d1c2c6eae90338905995612959273a5c63f93636c14614ac8737d1\",\n+  \"reporter_set_id_hex\": \"1efdc9d0b1b0065494a5697252e22484db0a94e008355e68dc696c0f796f52e4\",\n+  \"observed_height\": 100,\n+  \"observed_block_hash_hex\": \"2222222222222222222222222222222222222222222222222222222222222222\",\n+  \"price_micro_usd_per_zec\": 100000000,\n+  \"valid_from_height\": 102,\n+  \"valid_through_height\": 112,\n+  \"body_hex\": \"64000000222222222222222222222222222222222222222222222222222222222222222200e1f50500000000\",\n+  \"signing_message_hex\": \"5a53562f4f7261636c652f7631001111111111111111111111111111111111111111111111111111111111111111d4c3b2a11efdc9d0b1b0065494a5697252e22484db0a94e008355e68dc696c0f796f52e464000000222222222222222222222222222222222222222222222222222222222222222200e1f50500000000\",\n+  \"oracle_id_hex\": \"77a6593c112512e54bc893c3679d643625d325748d9f59c98dc8c3e5df7ef24e\",\n+  \"signer_mask_u8\": 31,\n+  \"effect_hex\": \"64000000222222222222222222222222222222222222222222222222222222222222222200e1f505000000001f\",\n+  \"authorization_hex\": \"203d63deedd196916c72bf4e495268fbcd8c92eb6e5270efd94482141eda07ccea721d4f83857a3b7850626644327a627cc6cf83c7f01d8fd5482550cac6ed005c69a353543835d000ef685333173154bf0a77adf8409f0b1cad0dca55d442670282bb1e3b61f804258384a06bb935a9110d2c575749d052b874be48715fb00b0bfe3c89822e95c713de9a2f7ba029d0675469b27a69c292d9665a8c7052200ebbbcc2d62ab96dd6f52b10a5e04f776d604ff837165b149720de47964795490a25624e1d55c8a56af41b207932ba2d82800d3b159024f257fcb27efb36ce54185762d753100949d0116d59c9c5e469e0c40efab25c14aab74cec8873ac963607be366d6d13e6d206370a381c5924cb4f15eaf18793a0aae8e84572254bffb824bc4f7e84b0b67ba0b20a9ae728e259c7b2fec0f321706a604f08c5d8742ce00f\",\n+  \"signatures_by_index\": [\n+    {\n+      \"index\": 0,\n+      \"signature_hex\": \"203d63deedd196916c72bf4e495268fbcd8c92eb6e5270efd94482141eda07ccea721d4f83857a3b7850626644327a627cc6cf83c7f01d8fd5482550cac6ed00\"\n+    },\n+    {\n+      \"index\": 1,\n+      \"signature_hex\": \"5c69a353543835d000ef685333173154bf0a77adf8409f0b1cad0dca55d442670282bb1e3b61f804258384a06bb935a9110d2c575749d052b874be48715fb00b\"\n+    },\n+    {\n+      \"index\": 2,\n+      \"signature_hex\": \"0bfe3c89822e95c713de9a2f7ba029d0675469b27a69c292d9665a8c7052200ebbbcc2d62ab96dd6f52b10a5e04f776d604ff837165b149720de47964795490a\"\n+    },\n+    {\n+      \"index\": 3,\n+      \"signature_hex\": \"25624e1d55c8a56af41b207932ba2d82800d3b159024f257fcb27efb36ce54185762d753100949d0116d59c9c5e469e0c40efab25c14aab74cec8873ac963607\"\n+    },\n+    {\n+      \"index\": 4,\n+      \"signature_hex\": \"be366d6d13e6d206370a381c5924cb4f15eaf18793a0aae8e84572254bffb824bc4f7e84b0b67ba0b20a9ae728e259c7b2fec0f321706a604f08c5d8742ce00f\"\n+    },\n+    {\n+      \"index\": 5,\n+      \"signature_hex\": \"528057e54f180d6f814db0b5f75f0720cf2c31a6b5eb3309f9247b2b69a81693c19fc946470c846fbcead8c55f30d73c54769098295702b4b818f0b60b733e0d\"\n+    },\n+    {\n+      \"index\": 6,\n+      \"signature_hex\": \"12b445bf670ad1d33e38a735d402500d85b59ad51dfb222df5aef2a0c942c08410e1954d2edf88945e15a54c33eb98711006101656fc85da62f5fa9411475d0d\"\n+    }\n+  ],\n+  \"expected_accept\": true\n+}"},{"filename":"zips/draft-ijaack-zsv/wire-vectors.json","status":"added","additions":298,"deletions":0,"patch":"@@ -0,0 +1,298 @@\n+{\n+  \"cases\": [\n+    {\n+      \"context_hash_hex\": \"91741f9bc6c08eb6392557e4f31dfca69badba1741e57786f00cd78a245f2547\",\n+      \"effects_hex\": \"feffffff0056535a0403020100000000640000000122222222222222222222222222222222222222222222222222222222222222220300000001f0b9f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980000010279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800ca9a3b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\n+      \"effects_length\": 184,\n+      \"name\": \"open_debt_free\",\n+      \"operation_tag\": 1,\n+      \"resolved_context_hex\": \"0122222222222222222222222222222222222222222222222222222222222222220300000000ab9041000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980000\",\n+      \"signature_digests\": [\n+        {\n+          \"digest_hex\": \"08e47466fa5699e0dfde6db8483b4ddb2dc3d77354c039039d0e9effb3696b0f\",\n+          \"index\": 0,\n+          \"role\": 0,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        },\n+        {\n+          \"digest_hex\": \"5dc1a8ca9d5983eac7d5cc6c92248a9ec5a1bf0a505fadf150d7017468f99a3f\",\n+          \"index\": 0,\n+          \"role\": 2,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        }\n+      ],\n+      \"txid_hex\": \"882cc2387534994f748c2723d6446cbeac5ababb8e55da424ba6224f9cba50d8\",\n+      \"value_check\": {\n+        \"burned\": 0,\n+        \"collateral_after\": 1000000000,\n+        \"collateral_before\": 0,\n+        \"minted\": 0,\n+        \"stable_input\": 0,\n+        \"stable_output\": 0,\n+        \"zec_fee\": 10000,\n+        \"zec_input\": 1100000000,\n+        \"zec_output\": 99990000\n+      }\n+    },\n+    {\n+      \"context_hash_hex\": \"8fab1a794d5411f454b7ad2ad099c7d157a61c91736037659d553f47b9879919\",\n+      \"effects_hex\": \"feffffff0056535a0403020100000000640000000133333333333333333333333333333333333333333333333333333333333333330100000001f0b9f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980177777777777777777777777777777777777777777777777777777777777777770200000002405973070000000002c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5c02a6410000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980000\",\n+      \"effects_length\": 221,\n+      \"name\": \"stable_transfer\",\n+      \"operation_tag\": 0,\n+      \"resolved_context_hex\": \"0133333333333333333333333333333333333333333333333333333333333333330100000000e1f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798017777777777777777777777777777777777777777777777777777777777777777020000000084d717000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800\",\n+      \"signature_digests\": [\n+        {\n+          \"digest_hex\": \"c73b0c7c9fbcf1d273884be696f4d408a364d867672b4f0a6c78b375e916a8cb\",\n+          \"index\": 0,\n+          \"role\": 0,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        },\n+        {\n+          \"digest_hex\": \"29b7f8094ec5d024954d328401c3242cc724b5e86bf65c76f84f13f6a3431357\",\n+          \"index\": 0,\n+          \"role\": 1,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        }\n+      ],\n+      \"txid_hex\": \"3c888b3658376f15050f95a77a0eac64cb10de1018df0e72ecd5c868497053a7\",\n+      \"value_check\": {\n+        \"burned\": 0,\n+        \"collateral_after\": 0,\n+        \"collateral_before\": 0,\n+        \"minted\": 0,\n+        \"stable_input\": 400000000,\n+        \"stable_output\": 400000000,\n+        \"zec_fee\": 10000,\n+        \"zec_input\": 100000000,\n+        \"zec_output\": 99990000\n+      }\n+    },\n+    {\n+      \"context_hash_hex\": \"68dd5fe8ae9384097568ab8674a76f29bfd99573d90f764729561c717774901d\",\n+      \"effects_hex\": \"feffffff0056535a0403020100000000640000000133333333333333333333333333333333333333333333333333333333333333330100000003f0b9f5050000000002c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5006211320000000002c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee500688909000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798017777777777777777777777777777777777777777777777777777777777777777020000000004555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666660084d71700000000006211320000000002c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5020000000100000000\",\n+      \"effects_length\": 342,\n+      \"mandatory_payouts\": [\n+        {\n+          \"index\": 1,\n+          \"recipient_key_hex\": \"02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5\",\n+          \"value\": 840000000\n+        },\n+        {\n+          \"index\": 2,\n+          \"recipient_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n+          \"value\": 160000000\n+        }\n+      ],\n+      \"name\": \"solvent_liquidation_at_50\",\n+      \"operation_tag\": 4,\n+      \"resolved_context_hex\": \"0133333333333333333333333333333333333333333333333333333333333333330100000000e1f5050000000002c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5017777777777777777777777777777777777777777777777777777777777777777020000000084d7170000000002c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50155555555555555555555555555555555555555555555555555555555555555550279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800ca9a3b000000000084d71700000000\",\n+      \"signature_digests\": [\n+        {\n+          \"digest_hex\": \"542b940e5a16bec9eab9984a03c7863f304b77c148ca8ac81b63dc5be622de3a\",\n+          \"index\": 0,\n+          \"role\": 0,\n+          \"verification_key_hex\": \"02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5\"\n+        },\n+        {\n+          \"digest_hex\": \"cfaf14158a3568c54d8637272cb685e03558f418623e8df9000a97b79b1f02f3\",\n+          \"index\": 0,\n+          \"role\": 1,\n+          \"verification_key_hex\": \"02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5\"\n+        },\n+        {\n+          \"digest_hex\": \"91850b01bb223a9828703e3a2efb85cb533e5372e79a60a2cec7eeda7730296a\",\n+          \"index\": 0,\n+          \"role\": 2,\n+          \"verification_key_hex\": \"02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5\"\n+        }\n+      ],\n+      \"txid_hex\": \"8262970985d048b708307cc801be46c659e72ef517103d12819d212207bebf1f\",\n+      \"value_check\": {\n+        \"burned\": 400000000,\n+        \"collateral_after\": 0,\n+        \"collateral_before\": 1000000000,\n+        \"minted\": 0,\n+        \"stable_input\": 400000000,\n+        \"stable_output\": 0,\n+        \"zec_fee\": 10000,\n+        \"zec_input\": 100000000,\n+        \"zec_output\": 1099990000\n+      }\n+    },\n+    {\n+      \"context_hash_hex\": \"8fab1a794d5411f454b7ad2ad099c7d157a61c91736037659d553f47b9879919\",\n+      \"effects_hex\": \"feffffff0056535a0403020100000000640000000133333333333333333333333333333333333333333333333333333333333333330100000001f0b9f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798017777777777777777777777777777777777777777777777777777777777777777020000000100a3e111000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980500e1f5050000000000\",\n+      \"effects_length\": 188,\n+      \"name\": \"retire_bad_debt\",\n+      \"operation_tag\": 5,\n+      \"resolved_context_hex\": \"0133333333333333333333333333333333333333333333333333333333333333330100000000e1f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798017777777777777777777777777777777777777777777777777777777777777777020000000084d717000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800\",\n+      \"signature_digests\": [\n+        {\n+          \"digest_hex\": \"a009f7fe0a398cf42492dd9553824603f8c8fc4e741f9d4653f28724a236a8fd\",\n+          \"index\": 0,\n+          \"role\": 0,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        },\n+        {\n+          \"digest_hex\": \"9c73fc800b94cd0007ddf186e84a73a58bec23c2fdc36d75fb77efb8794fc055\",\n+          \"index\": 0,\n+          \"role\": 1,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        }\n+      ],\n+      \"txid_hex\": \"a45e7893591eb07093fec90a68a7e83017a4ee53b731dce3d8966f568bbd75e3\",\n+      \"value_check\": {\n+        \"burned\": 100000000,\n+        \"collateral_after\": 0,\n+        \"collateral_before\": 0,\n+        \"minted\": 0,\n+        \"stable_input\": 400000000,\n+        \"stable_output\": 300000000,\n+        \"zec_fee\": 10000,\n+        \"zec_input\": 100000000,\n+        \"zec_output\": 99990000\n+      }\n+    },\n+    {\n+      \"context_hash_hex\": \"fdee3a9f7337f5e12f0a21f69a82eec86b2620a4ef7fb385abeb3e1b2689efa4\",\n+      \"effects_hex\": \"feffffff0056535a0403020100000000640000000133333333333333333333333333333333333333333333333333333333333333330100000002f0b9f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800c2eb0b000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179801777777777777777777777777777777777777777777777777777777777777777702000000000255555555555555555555555555555555555555555555555555555555555555550008af2f0000000000a3e1110000000088888888888888888888888888888888888888888888888888888888888888880100000000\",\n+      \"effects_length\": 264,\n+      \"mandatory_payouts\": [\n+        {\n+          \"index\": 1,\n+          \"recipient_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n+          \"value\": 200000000\n+        }\n+      ],\n+      \"name\": \"update_repay_and_withdraw\",\n+      \"operation_tag\": 2,\n+      \"resolved_context_hex\": \"0133333333333333333333333333333333333333333333333333333333333333330100000000e1f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980177777777777777777777777777777777777777777777777777777777777777770200000000e1f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980155555555555555555555555555555555555555555555555555555555555555550279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800ca9a3b000000000084d71700000000\",\n+      \"signature_digests\": [\n+        {\n+          \"digest_hex\": \"f14a98eec8db1b1c1013960877858244baba875a65dd2214b0be299715146f76\",\n+          \"index\": 0,\n+          \"role\": 0,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        },\n+        {\n+          \"digest_hex\": \"bd29a395faf56879ca37142562d34f96bf65ed06d502ecb06f4f99b9134f4344\",\n+          \"index\": 0,\n+          \"role\": 1,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        },\n+        {\n+          \"digest_hex\": \"7a1be0bfb48ae575bb348a30e06b86f8614103efe11ad467cafa0396288bbab8\",\n+          \"index\": 0,\n+          \"role\": 2,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        }\n+      ],\n+      \"txid_hex\": \"31eeba82dfa395d35eb81e09e47135c1b2a9f7ec56ebdda1599614853112eae4\",\n+      \"value_check\": {\n+        \"burned\": 100000000,\n+        \"collateral_after\": 800000000,\n+        \"collateral_before\": 1000000000,\n+        \"minted\": 0,\n+        \"stable_input\": 100000000,\n+        \"stable_output\": 0,\n+        \"zec_fee\": 10000,\n+        \"zec_input\": 100000000,\n+        \"zec_output\": 299990000\n+      }\n+    },\n+    {\n+      \"context_hash_hex\": \"6a780de6403e2c5d35836e61011d0dbef46fde1d4c0bb3ce9cefe41b0b410a7d\",\n+      \"effects_hex\": \"feffffff0056535a0403020100000000640000000133333333333333333333333333333333333333333333333333333333333333330100000002f0b9f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800ca9a3b000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179801777777777777777777777777777777777777777777777777777777777777777702000000000355555555555555555555555555555555555555555555555555555555555555550100000000\",\n+      \"effects_length\": 216,\n+      \"mandatory_payouts\": [\n+        {\n+          \"index\": 1,\n+          \"recipient_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n+          \"value\": 1000000000\n+        }\n+      ],\n+      \"name\": \"close_with_full_repayment\",\n+      \"operation_tag\": 3,\n+      \"resolved_context_hex\": \"0133333333333333333333333333333333333333333333333333333333333333330100000000e1f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798017777777777777777777777777777777777777777777777777777777777777777020000000084d717000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980155555555555555555555555555555555555555555555555555555555555555550279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800ca9a3b000000000084d71700000000\",\n+      \"signature_digests\": [\n+        {\n+          \"digest_hex\": \"bda6fbe96c252fff2977ef1a74320f71c564470aab89c8f9f57db1fa52f88f8e\",\n+          \"index\": 0,\n+          \"role\": 0,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        },\n+        {\n+          \"digest_hex\": \"8ffd8ba45127bb00f9dfd0adca9635a3d4944942042b5653f14f5bf41a69dc29\",\n+          \"index\": 0,\n+          \"role\": 1,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        },\n+        {\n+          \"digest_hex\": \"b60337da7b7cc330a8dd8870597ca713d09c0143610fe730900d74249e34a84f\",\n+          \"index\": 0,\n+          \"role\": 2,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        }\n+      ],\n+      \"txid_hex\": \"57d98f2ffd080959c368fc94f3c08ed64b137a78f67ede25bf5c710ee83669f8\",\n+      \"value_check\": {\n+        \"burned\": 400000000,\n+        \"collateral_after\": 0,\n+        \"collateral_before\": 1000000000,\n+        \"minted\": 0,\n+        \"stable_input\": 400000000,\n+        \"stable_output\": 0,\n+        \"zec_fee\": 10000,\n+        \"zec_input\": 100000000,\n+        \"zec_output\": 1099990000\n+      }\n+    },\n+    {\n+      \"context_hash_hex\": \"b97c6231ca2286f72b5d22d30c79d5078c944d6e56e9706e32bad30a7b937972\",\n+      \"effects_hex\": \"feffffff0056535a0403020100000000640000000133333333333333333333333333333333333333333333333333333333333333330100000001f0b9f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798000000015a000000999999999999999999999999999999999999999999999999999999999999999900e1f505000000001f\",\n+      \"effects_length\": 148,\n+      \"name\": \"oracle_certificate_effects\",\n+      \"operation_tag\": 0,\n+      \"oracle_effect_check\": {\n+        \"effect_hex\": \"5a000000999999999999999999999999999999999999999999999999999999999999999900e1f505000000001f\",\n+        \"effect_length\": 45,\n+        \"expected_ed25519_authorization_bytes\": 320,\n+        \"expected_ed25519_signature_slots\": 5,\n+        \"expected_secp256k1_signature_slots\": 1,\n+        \"expected_total_signature_slots\": 6,\n+        \"scope\": \"Unsigned certificate framing only. No reporter signatures are supplied or verified; this fictional context is separate from oracle-vector.json.\"\n+      },\n+      \"resolved_context_hex\": \"0133333333333333333333333333333333333333333333333333333333333333330100000000e1f505000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980000\",\n+      \"signature_digests\": [\n+        {\n+          \"digest_hex\": \"88d9cabb3321e4909d204af66a39ce48c979b171449a58f9599f4747b4c3121e\",\n+          \"index\": 0,\n+          \"role\": 0,\n+          \"verification_key_hex\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"\n+        }\n+      ],\n+      \"txid_hex\": \"195e70de28f766a55058f596f40e86f4ac5c85c07d4bcce9978ee45466cbbe7a\",\n+      \"value_check\": {\n+        \"burned\": 0,\n+        \"collateral_after\": 0,\n+        \"collateral_before\": 0,\n+        \"minted\": 0,\n+        \"stable_input\": 0,\n+        \"stable_output\": 0,\n+        \"zec_fee\": 10000,\n+        \"zec_input\": 100000000,\n+        \"zec_output\": 99990000\n+      }\n+    }\n+  ],\n+  \"hash_hex_order\": \"Raw digest bytes; not reversed display hashes.\",\n+  \"scope\": \"Unsigned effect/context/hash vectors only; no signatures, node execution or assigned network identifiers. Previous outputs, prior vaults and operation oracle IDs are fictional context inputs, not certified chain state.\",\n+  \"test_allocations\": {\n+    \"branch_id\": 16909060,\n+    \"expiry_height\": 100,\n+    \"genesis_hex\": \"4444444444444444444444444444444444444444444444444444444444444444\",\n+    \"version\": 2147483646,\n+    \"version_group_id\": 1515410944\n+  }\n+}"}],"filesTruncated":false,"preview":{"filename":"zips/draft-ijaack-zsv.md","title":"Zcash Stablecoin Vaults","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"scope-and-activation-parameters","text":"Scope and activation parameters"},{"level":2,"id":"encoding-conventions-and-bounds","text":"Encoding conventions and bounds"},{"level":2,"id":"effect-body-e","text":"Effect body E"},{"level":2,"id":"vault-state-and-operation-payloads","text":"Vault state and operation payloads"},{"level":2,"id":"per-asset-value-equations","text":"Per-asset value equations"},{"level":2,"id":"hashes-and-authorization","text":"Hashes and authorization"},{"level":2,"id":"oracle-effects-integration","text":"Oracle effects integration"},{"level":2,"id":"units-ranges-and-arithmetic","text":"Units, ranges and arithmetic"},{"level":2,"id":"state-and-transaction-shape","text":"State and transaction shape"},{"level":2,"id":"oracle-use","text":"Oracle use"},{"level":2,"id":"open","text":"OPEN"},{"level":2,"id":"update","text":"UPDATE"},{"level":2,"id":"close","text":"CLOSE"},{"level":2,"id":"liquidate","text":"LIQUIDATE"},{"level":2,"id":"retire_bad_debt","text":"RETIRE_BAD_DEBT"},{"level":2,"id":"loss-and-liveness-policy","text":"Loss and liveness policy"},{"level":2,"id":"complete-block-transition-and-persistence","text":"Complete block transition and persistence"},{"level":2,"id":"oracle-rules","text":"Oracle rules"},{"level":3,"id":"deployment-roster-and-domains","text":"Deployment roster and domains"},{"level":3,"id":"report-encoding","text":"Report encoding"},{"level":3,"id":"inclusion-observed-ancestry-and-freshness","text":"Inclusion, observed ancestry and freshness"},{"level":3,"id":"block-order-and-transaction-restrictions","text":"Block order and transaction restrictions"},{"level":3,"id":"outages-conflicting-reports-and-recovery","text":"Outages, conflicting reports and recovery"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"a-dedicated-fixed-asset-protocol","text":"A dedicated fixed-asset protocol"},{"level":2,"id":"economic-policy","text":"Economic policy"},{"level":2,"id":"oracle-and-governance","text":"Oracle and governance"},{"level":2,"id":"reserve-and-funding-boundary","text":"Reserve and funding boundary"},{"level":2,"id":"community-discussion","text":"Community discussion"},{"level":1,"id":"backward-compatibility","text":"Backward compatibility"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"outstanding-allocations-and-review","text":"Outstanding allocations and review"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1439,"title":"Add draft ZIP: Coinholder Grants for Ecosystem Work","author":"iJaack","createdAt":"2026-11-05T16:55:11Z","updatedAt":"2026-11-05T16:55:11Z","url":"https://github.com/zcash/zips/pull/1439","diffUrl":"https://github.com/zcash/zips/pull/1439.diff","headRef":"draft-ijaack-coinholder-ecosystem-grants","headRepo":"iJaack/zips","headSha":"a2038976223bd4e77a337a73f4b3880c2134b025","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Add an unnumbered Process ZIP allowing coinholders to approve defined work before delivery and pay after verified completion, alongside retroactive grants. The proposal makes eligibility for BD, DevRel, merchant onboarding, education, regional communities, and ongoing support explicit alongside tech","zipRefs":[],"draftRefs":["draft-ijaack-coinholder-ecosystem-grants"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -237,6 +237,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-ijaack-coinholder-ecosystem-grants</td> <td class=\"left\"><a href=\"zips/draft-ijaack-coinholder-ecosystem-grants.md\">Coinholder Grants for Ecosystem Work</a></td> <td class=\"left\"><a href=\"https://forum.zcashcommunity.com/t/coinholder-grants-need-to-fund-ecosystem-work/58764\">https://forum.zcashcommunity.com/t/coinholder-grants-need-to-fund-ecosystem-work/58764</a></td> </tr>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-ijaack-coinholder-ecosystem-grants.md","status":"added","additions":345,"deletions":0,"patch":"@@ -0,0 +1,345 @@\n+    ZIP: Unassigned\n+    Title: Coinholder Grants for Ecosystem Work\n+    Owners: Giacomo Barbieri <hey@jaack.me>\n+    Status: Draft\n+    Category: Process\n+    Created: 2026-11-05\n+    License: MIT\n+    Discussions-To: <https://forum.zcashcommunity.com/t/coinholder-grants-need-to-fund-ecosystem-work/58764>\n+    Updates: 1016\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" are interpreted as\n+specified in BCP 14 [^bcp14] when they appear in all capitals.\n+\n+The **Fund** and **Keyholders** are the Coinholder-Controlled Fund and its\n+Key-Holder Organizations under ZIP 1016. The **Administrator** is the existing\n+organization authorized to administer the coinholder grants program.\n+A **milestone** is a separately described and priced deliverable or completed\n+service period. An **assessor** is an Administrator staff member or independent\n+expert who evaluates delivery against the approved criteria.\n+\n+# Abstract\n+\n+This ZIP adds prospective grants alongside the coinholder program's\n+retroactive grants. Coinholders can approve defined work before delivery;\n+payment follows verified completion of the work or its milestones. It makes\n+eligibility for business development, developer relations, merchant onboarding,\n+education, regional communities, and ongoing support explicit alongside\n+technical contributions.\n+\n+The existing Administrator verifies delivery, using independent experts where\n+needed. Keyholders execute payments under the existing custody and veto rules.\n+Compensation is denominated in USD and paid in ZEC at a published payment-time\n+exchange rate. Routine milestone payments require no additional funding vote.\n+\n+The process includes review and payment deadlines, written reasons for adverse\n+decisions, reconsideration, and escalation of unresolved disputes. Public\n+records distinguish approved commitments from executed payments. Evidence can\n+be verified confidentially where public disclosure would expose users or\n+partners.\n+\n+No changes to consensus, block-subsidy allocations, or voting weights are\n+proposed. Treasury backing remains open for community discussion; USD\n+denomination does not require cash reserves or a new custodian.\n+\n+# Motivation\n+\n+The retroactive-only program was deliberately ratified in 2025 [^ratification]\n+and remains explicit in the Q4 2026 call [^q4-call]. It requires contributors to\n+complete work before knowing whether they will be paid. This limits what\n+people without substantial reserves can undertake, including sustained\n+technical support, integration work, education, and merchant onboarding.\n+\n+ZIP 1016 already anticipates scheduled payments, missed milestones, and\n+additional votes on continued funding [^zip-1016]. It also refers voters to\n+ZIP 1015's evaluation factors, including well-specified work, reasonable costs,\n+and ecosystem growth [^zip-1015]. This proposal supplies a prospective\n+application and verification process while clarifying access for ecosystem\n+work discussed in the motivating forum thread [^discussion].\n+\n+Advance approval reduces uncertainty about the funding decision. Payment after\n+delivery still requires working capital between milestones and remains subject\n+to disputes, liquidity constraints, and existing veto rules. Eligibility also\n+cannot guarantee that voters will fund public goods. This proposal does not\n+replace a separately governed program or guarantee category allocations.\n+\n+# Privacy Implications\n+\n+Support records, partner confirmations, and adoption evidence can identify\n+users or expose commercial relationships. Confidential assessment and public\n+summaries can reduce disclosure, but introduce trust in assessors. Published\n+funding and payment metadata may still permit correlation. The process changes\n+no voting protocol or wallet behavior. Operational privacy requirements appear\n+in the Specification.\n+\n+# Requirements\n+\n+The design aims to provide:\n+\n+* An actionable decision before delivery, with payment for verified work.\n+* Clear access for technical and nontechnical ecosystem contributions.\n+* USD compensation settled in ZEC and visible outstanding commitments.\n+* Accountable verification, timely decisions, and a route for disputes.\n+* Proportionate evidence, privacy protection, and prevention of double funding.\n+* Compatibility with existing voting, custody, and veto responsibilities.\n+\n+# Specification\n+\n+## 1. Scope and eligibility\n+\n+ZIP 1016's voting thresholds, quarterly grant voting, minimum 30-day community\n+review, custody requirements, and veto grounds continue to apply. Once this\n+ZIP is activated, the Administrator MUST accept both retroactive and prospective\n+applications under the process below. Other operational rules remain applicable\n+unless expressly modified here.\n+\n+Applications MUST demonstrate a specific benefit to Zcash and its ecosystem.\n+Eligible fields MUST explicitly include protocol work, security, infrastructure,\n+applications, developer tools, BD and integrations, DevRel, merchant onboarding,\n+education, regional communities, localization, accessibility, and ongoing support\n+or maintenance. Eligibility establishes a route to apply, not an entitlement\n+to funding. Coinholders retain discretion to reject proposals.\n+\n+This ZIP creates no category quotas, startup advances, separate fund, standing\n+review committee, or new voting mechanism. It neither determines the outcome\n+of ZCG/ZAP proposals nor reallocates their grant-selection authority.\n+\n+## 2. Applications and approval\n+\n+A retroactive application MUST identify the completed contribution or service\n+period, requested compensation, attributable work, and evidence. A completed\n+period of an ongoing service MAY qualify; future periods MUST NOT be presented\n+as completed work. Existing retroactive denomination and payment rules remain\n+applicable.\n+\n+A prospective application MUST specify:\n+\n+* Its scope, intended benefit, delivery term, and completion evidence.\n+* Separately priced milestones and acceptance criteria, or one final\n+  deliverable where staged delivery is unsuitable.\n+* A total USD amount, milestone amounts, and delivery/payment schedule.\n+* The Administrator's verification procedure, required expertise, conflicts,\n+  and independent alternative if the Administrator itself is conflicted.\n+* Correction, reconsideration, dispute, expiry, and termination terms.\n+* Other funding for overlapping work and any confidential-evidence arrangements.\n+\n+Criteria MUST distinguish deliverables within the contributor's control from\n+longer-term outcomes they aim to support. Immediate transaction growth MUST NOT\n+be the sole mandatory measure for every eligible contribution. Guidance MUST\n+include evidence examples suitable for technical work and ecosystem services.\n+\n+Before voting, the Administrator MUST publish requested USD amounts, outstanding\n+USD commitments, and the Fund's ZEC balance with a valuation timestamp. A current\n+USD valuation of ZEC MUST NOT be described as a fixed reserve or a guarantee\n+of future payment capacity.\n+\n+A passing vote and a confirmed commitment MUST be reported separately. A\n+commitment is confirmed only after an allocation under the applicable published\n+funding rules, acceptance of administrative duties, required agreements and\n+compliance checks, and publication of the approved scope, USD amounts, schedule,\n+and effective date. Work undertaken before confirmation remains at the\n+applicant's risk.\n+\n+The agreement MUST make accepted milestones payable under the approved terms,\n+subject to existing veto rules. A proposal that passes without a full allocation\n+MUST be reported as uncommitted; its budget and scope MUST NOT be silently\n+reduced. A revised prospective award requires community review and another vote.\n+This does not change partial-funding rules for retroactive grants.\n+\n+## 3. Verification and timing\n+\n+The existing Administrator MUST verify delivery against the approved criteria\n+and obtain independent expert assessment where necessary. Assessors MUST\n+have their identity, relevant expertise, and material conflicts published before\n+assessment. Conflicted assessors MUST recuse themselves and be replaced. A\n+grantee MUST NOT certify its own entitlement to payment. Where the Administrator\n+itself is materially conflicted, the independent alternative identified in the\n+voted proposal MUST perform verification.\n+\n+The following calendar-day deadlines are proposed defaults. Different deadlines\n+MAY apply only if expressly included in the grant terms approved by coinholders:\n+\n+* Within 14 days of a milestone submission, the Administrator MUST issue an\n+  acceptance, correction request, or rejection against the approved criteria.\n+* Correction requests and rejections MUST identify the unmet criteria and\n+  evidence considered. Corrected submissions receive a new 14-day review period;\n+  assessors MUST NOT introduce new acceptance criteria through that process.\n+* Accepted milestones MUST be paid within 7 days of acceptance and satisfaction\n+  of the agreed payment prerequisites, subject to the existing legal and veto\n+  rules. Missing prerequisites MUST be identified promptly to the applicant.\n+\n+Silence is not acceptance. The Administrator MUST report missed deadlines with\n+a reason and next action date; an applicant MAY invoke reconsideration over an\n+unexplained delay. Expert availability or internal administration MUST NOT\n+be used to reopen the merits of the approved grant.\n+\n+After valid acceptance, the Administrator coordinates payment by Keyholders\n+under ZIP 1016. Routine milestone payments and assessor replacements under the\n+approved procedure MUST NOT require another coinholder vote. Assessment does\n+not confer payment-signing authority or override keyholder veto powers.\n+\n+## 4. USD compensation paid in ZEC\n+\n+Prospective awards MUST be denominated in USD and paid in ZEC. USD amounts\n+MUST be specified to whole cents. For a milestone owing $D$ USD at a payment-time\n+rate of $P$ USD per ZEC, the payment is $D/P$ ZEC, rounded up to the nearest\n+zatoshi (0.00000001 ZEC). The reference rate MUST be finite and positive;\n+otherwise the unavailable-price procedure applies. Exchange-rate changes alter\n+the ZEC delivered, not the approved USD amount, and require no new grant vote.\n+A fixed ZEC ceiling MUST NOT reduce the USD amount owed.\n+\n+Before application review begins, the Administrator MUST publish a consistent\n+price source, valuation time, maximum quote age before broadcast, and fallback\n+procedure for unavailable prices or payment routes. Stale quotes MUST be\n+refreshed. Network and payment costs MUST be separately budgeted and MUST NOT\n+reduce the recipient's approved amount.\n+\n+Keyholders MAY pay directly from existing ZEC holdings. This ZIP requires no\n+cash reserve, hedging, asset conversion, or new custodian. The Administrator\n+MUST track outstanding USD obligations separately from ZEC balances and\n+executed payments. Payment valuation does not guarantee a recipient's eventual\n+sale proceeds.\n+\n+If funds are insufficient for a due payment, the Administrator MUST disclose\n+the shortfall and seek resolution through the applicable program process.\n+The USD obligation MUST NOT be silently reduced or converted into a fixed-ZEC\n+award. Treasury backing and management of price and liquidity risk remain open\n+for community discussion.\n+\n+## 5. Reconsideration, disputes, and changes\n+\n+An applicant MAY request reconsideration within 14 days of a rejection or\n+contested correction request, identifying the disputed criteria or evidence.\n+The Administrator MUST obtain a second assessment from an independent expert\n+who did not make the contested assessment. If the Administrator itself is\n+conflicted, the independent alternative identified in the voted proposal\n+handles reconsideration. The responsible verifier MUST issue a reasoned\n+reconsideration decision within 14 days of the request.\n+\n+An applicant MAY escalate missed review, reconsideration, or payment deadlines\n+without waiting for a rejection. Administrative complaints MUST receive a\n+reasoned response within 14 days. A missed deadline creates no automatic\n+acceptance or transfer of funds.\n+\n+Unresolved substantive disputes MUST be referred to Keyholders with both\n+assessments and the applicant's response for the next coinholder decision\n+eligible under the applicable community-review rules, consistent with ZIP\n+1016's further-vote process. The Administrator MUST publish the expected vote\n+cycle or explain why no cycle has been scheduled. A quarterly escalation may\n+therefore extend beyond the ordinary review deadlines.\n+\n+Only the disputed portion pauses unless a veto or approved grant terms require\n+otherwise. Accepted, undisputed milestones remain payable. Existing legal\n+compliance and veto decisions are governed by ZIP 1016; reconsideration cannot\n+override them.\n+\n+Increases in the USD award, material scope or acceptance changes, and renewals\n+MUST receive community review and a new coinholder decision. Administrative\n+variations expressly permitted by the voted terms need no new vote. Expiry\n+alone MUST NOT cancel an accepted unpaid milestone or extinguish a live dispute.\n+Closure reports MUST reconcile completed work, payments, and remaining or\n+terminated obligations.\n+\n+## 6. Evidence, privacy, and overlapping funding\n+\n+Public applications MUST provide enough information to assess the work and\n+its value. Underlying evidence MAY be reviewed confidentially where necessary,\n+with public attestations stating what was verified and material limitations.\n+Confidential arrangements MUST specify access, retention, and deletion duties\n+and collect no more information than needed.\n+\n+The process MUST NOT require public disclosure of private keys, seed phrases,\n+individual wallet histories, or personal beneficiary information as adoption\n+evidence. Existing lawful recipient-compliance requirements remain applicable.\n+\n+Applicants MUST disclose previous and pending funding for overlapping work.\n+The Administrator MUST check overlaps before commitment and payment. Duplicate\n+compensation for an already funded scope MUST NOT be paid unless expressly\n+disclosed and approved as a distinct reward. A milestone MUST NOT be paid twice\n+under one authorization.\n+\n+## 7. Reporting\n+\n+The Administrator MUST publish application formats, eligibility and evidence\n+guidance, grant status, outstanding USD commitments, and executed payment\n+records. Payment records MUST identify the USD amount settled, ZEC delivered,\n+reference rate and timestamp, and execution evidence consistent with privacy\n+obligations. Approval alone MUST NOT be reported as an executed payment.\n+\n+Eligibility exclusions MUST receive written reasons. After each round, the\n+Administrator SHOULD summarize recurring application weaknesses without\n+attributing motives to anonymous voters or implying that eligibility ensures\n+approval.\n+\n+## 8. Compatibility and deployment\n+\n+This process change requires community acceptance of a change to the previously\n+ratified retroactive-only program. ZIP publication or an ordinary grant vote\n+alone does not activate it. Existing grants retain their approved terms.\n+\n+The related process draft in PR #1112 [^process-draft] requires completed work.\n+Its owners and ZIP Editors will need to coordinate the prospective exception,\n+including references to a numbered successor if adopted before this proposal.\n+\n+Before accepting prospective applications, the Administrator and Keyholders\n+MUST publish the ratification decision, first affected cycle, amended operative\n+documents, responsible and funded administration, grant and dispute procedures,\n+payment-rate method, and confirmation that governing agreements permit the\n+prospective commitments and scheduled payments. Existing custody and veto\n+responsibilities remain in force. No consensus-software activation is proposed.\n+\n+# Rationale\n+\n+Advance approval makes a conditional funding decision available before delivery;\n+an advisory poll alone would leave a further discretionary award decision after\n+the work. Milestone payments reduce how much a contributor must finance at once\n+without introducing startup advances.\n+\n+Using the existing Administrator with independent expertise keeps verification\n+practical without a new committee or a quarterly vote on each payment. Written\n+criteria, recusals, second assessment, and escalation constrain judgment and\n+delay risks; they do not make assessment trustless. The proposed 14-day review\n+and 7-day payment periods provide a clear starting point for discussion while\n+allowing different timelines in the voted grant terms.\n+\n+USD denomination makes the compensation amount predictable in dollars while\n+preserving payment through ZEC. The Fund may retain ZEC, leaving it exposed to\n+changes in the ZEC needed to meet its obligations. The conversion formula does\n+not itself guarantee solvency. This draft leaves treasury policy to discussion\n+rather than prescribe reserve assets or custody arrangements.\n+\n+The forum discussion also raises an objection that process reform cannot\n+provide reliable public-goods funding if voters decline such work. Eligibility\n+clarity does not solve that willingness problem, and this ZIP establishes no\n+spending mandate or replacement for ZCG/ZAP.\n+\n+# Questions for Community Discussion\n+\n+* Are the proposed 14-day review, 7-day payment, and 14-day reconsideration\n+  periods practical, and which grants need different terms?\n+* What expert-selection and conflict procedures will make independent\n+  reconsideration credible without adding a standing committee?\n+* How should the Fund manage outstanding USD commitments against a ZEC treasury?\n+  This draft prescribes no reserve asset, custodian, or hedging policy.\n+* Which organization will undertake and have funding for the administrative\n+  workload beyond the present arrangement?\n+* What ratification route and timetable should apply, and how should this\n+  proposal be coordinated with PR #1112 and keyholder agreements?\n+\n+# References\n+\n+[^bcp14]: [BCP 14](https://www.rfc-editor.org/info/bcp14).\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](zip-1016.md).\n+\n+[^zip-1015]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding](zip-1015.rst).\n+\n+[^ratification]: [Zcash Coinholders Approve Retroactive Grants Program](https://shieldedlabs.net/zcash-coinholders-approve-retroactive-grants-program/), 4 September 2025.\n+\n+[^q4-call]: [Q4 2026 Coinholder-Directed Retroactive Grant Program — Call for Proposals](https://forum.zcashcommunity.com/t/q4-2026-coinholder-directed-retroactive-grant-program-call-for-proposals/58598).\n+\n+[^discussion]: [Coinholder grants need to fund ecosystem work](https://forum.zcashcommunity.com/t/coinholder-grants-need-to-fund-ecosystem-work/58764).\n+\n+[^process-draft]: [Coinholder-Directed Retroactive Grants Program draft, PR #1112](https://github.com/zcash/zips/pull/1112). Draft in the sources inspected on 5 November 2026."}],"filesTruncated":false,"preview":{"filename":"zips/draft-ijaack-coinholder-ecosystem-grants.md","title":"Coinholder Grants for Ecosystem Work","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"1-scope-and-eligibility","text":"1. Scope and eligibility"},{"level":2,"id":"2-applications-and-approval","text":"2. Applications and approval"},{"level":2,"id":"3-verification-and-timing","text":"3. Verification and timing"},{"level":2,"id":"4-usd-compensation-paid-in-zec","text":"4. USD compensation paid in ZEC"},{"level":2,"id":"5-reconsideration-disputes-and-changes","text":"5. Reconsideration, disputes, and changes"},{"level":2,"id":"6-evidence-privacy-and-overlapping-funding","text":"6. Evidence, privacy, and overlapping funding"},{"level":2,"id":"7-reporting","text":"7. Reporting"},{"level":2,"id":"8-compatibility-and-deployment","text":"8. Compatibility and deployment"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"questions-for-community-discussion","text":"Questions for Community Discussion"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1437,"title":"Draft ZIP: Reduce block target spacing from 25 to 20 seconds","author":"evan-forbes","createdAt":"2026-11-05T16:48:32Z","updatedAt":"2026-11-05T16:48:32Z","url":"https://github.com/zcash/zips/pull/1437","diffUrl":"https://github.com/zcash/zips/pull/1437.diff","headRef":"evan/20s-block-target-spacing","headRepo":"valargroup/zips","headSha":"443ae65a5819de37c55b7fca415a798fa63581e4","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Propose a separate network upgrade after NU7 that reduces block target spacing from 25 to 20 seconds. The draft builds on ZIPs 218 and 237, retains existing action and block size limits, and increases the DAA averaging window from 102 to 128 blocks to preserve approximately the same averaging durati","zipRefs":[],"draftRefs":["draft-valargroup-20-second-block-target-spacing"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-valargroup-20-second-block-target-spacing.md","status":"added","additions":412,"deletions":0,"patch":"@@ -0,0 +1,412 @@\n+\n+    ZIP: Unassigned\n+    Title: Reduce Block Target Spacing from 25 to 20 Seconds\n+    Owners: Dev Ojha <dev@valargroup.dev>\n+            Evan Forbes <evan@valargroup.dev>\n+    Status: Draft\n+    Category: Consensus\n+    Created: 2026-11-04\n+    License: MIT\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", and \"SHOULD\" in this document are to be\n+interpreted as described in BCP 14 [^BCP14] when, and only when, they appear\n+in all capitals.\n+\n+The terms \"block chain\", \"consensus branch\", and \"network upgrade\" follow\n+ZIP 200. [^zip-0200] The character § refers to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+\"Block target spacing\" means the interval between blocks targeted by the\n+difficulty adjustment algorithm. Actual block intervals vary.\n+\n+The \"25-second baseline\" means the rules after NU7, including ZIP 218's\n+25-second target spacing and ZIP 237's subsidy definitions. [^zip-0218]\n+[^zip-0237]\n+\n+A \"stale block\" is a valid mined block that does not remain on the selected\n+chain. This document reports \"stale blocks per accepted block\" separately\n+from the fraction of all mined blocks that become stale.\n+\n+\"Reach 90%\" means the time for a block to reach 90% of the specified node\n+sample. \"Effective old-tip mining time\" means the accumulated mining work\n+spent on the old tip, expressed as seconds of the whole network's mining power.\n+\n+MB means 1,000,000 bytes.\n+\n+\n+# Abstract\n+\n+This proposal reduces block target spacing from 25 to 20 seconds in a\n+network upgrade after NU7. It builds on ZIP 218 without changing its action\n+limits or block size limit. [^zip-0218] [^zip-0259]\n+\n+The change reduces the expected wait for a first confirmation by 20%. With\n+unchanged per-block limits, it increases maximum transaction throughput by\n+25%. Nodes and wallets must process 25% more blocks per unit time, increasing\n+worst-case block processing time and sync cost. It adjusts the\n+difficulty averaging window, issuance parameters, and duration-based node\n+defaults to the new spacing. Shorter inclusion intervals can expose finer\n+transaction timing information.\n+\n+# Motivation\n+\n+A 20-second target reduces the expected wait for a first confirmation by\n+five seconds. With unchanged per-block limits, it increases maximum transaction throughput\n+by 25%. The additional blocks increase worst-case block processing time and\n+sync cost per unit time.\n+This proposal evaluates that incremental trade-off from ZIP 218's baseline.\n+\n+\n+# Privacy Implications\n+\n+ZIP 218 identifies timing and anchor-depth privacy effects. [^zip-0218]\n+Shorter blocks give observers finer timing information without changing what\n+individual shielded transactions reveal.\n+\n+A three-block anchor depth spans an expected 60 seconds instead of 75.\n+A rollback past the anchor invalidates the transaction. A replacement spending\n+the same notes exposes the same nullifiers, linking the two transactions.\n+Ordinary stale-block measurements do not bound rollback depth.\n+\n+# Requirements\n+\n+- Reduce target spacing from 25 to 20 seconds after NU7.\n+- Preserve approximately the difficulty averaging duration and issuance schedule,\n+  including partial halving intervals, subject to integer rounding.\n+- Preserve historical consensus rules and integrate the transition with ZIP 237\n+  and active funding-stream schedules.\n+\n+# Specification\n+\n+This section applies to the post-NU7 rules of ZIPs 218 and 237.\n+[^zip-0218] [^zip-0237] Implementations MUST retain all pre-activation rules\n+at heights below the new activation height.\n+\n+## Activation and target spacing\n+\n+Define the following constants:\n+\n+$$\\mathsf{TwentySecondActivationHeight} : \\mathbb{N}$$\n+$$\\mathsf{TwentySecondPoWTargetSpacing} : \\mathbb{N} := 20 \\text{ seconds}$$\n+$$\\mathsf{TwentySecondSpacingRatio} : \\mathbb{Q}_{>0} := 25/20 = 5/4$$\n+\n+A separate deployment ZIP will assign $\\mathsf{TwentySecondActivationHeight}$\n+for each network. It MUST be strictly greater than that network's\n+$\\mathsf{NU7ActivationHeight}$. No NU7 activation constant changes.\n+\n+For a height $h \\in \\mathbb{N}$ at or above this activation height, define:\n+\n+$$\\mathsf{PoWTargetSpacing}(h) := \\mathsf{TwentySecondPoWTargetSpacing}.$$\n+\n+For $h < \\mathsf{TwentySecondActivationHeight}$, retain ZIP 218's\n+definition of $\\mathsf{PoWTargetSpacing}$.\n+In particular, $\\mathsf{PostNU7PoWTargetSpacing}$ remains 25 seconds.\n+\n+## Difficulty averaging window\n+\n+The difficulty adjustment algorithm (DAA) averages over 128 blocks after\n+activation, replacing ZIP 218's 102-block window. The larger block count\n+preserves approximately the same expected averaging duration at the shorter\n+target spacing.\n+\n+Define:\n+\n+$$\\mathsf{TwentySecondPoWAveragingWindow} : \\mathbb{N} := 128.$$\n+\n+For $h \\geq \\mathsf{TwentySecondActivationHeight}$, define:\n+\n+$$\\mathsf{PoWAveragingWindow}(h) := \\mathsf{TwentySecondPoWAveragingWindow}.$$\n+\n+For $h < \\mathsf{TwentySecondActivationHeight}$, retain ZIP 218's\n+averaging window, including 102 blocks after NU7 activation. All uses of this\n+function in the difficulty algorithm use the height of the block under\n+consideration.\n+\n+The averaging window includes pre-activation blocks when its lookback crosses\n+the activation height. Implementations MUST NOT reset it at activation.\n+ZIP 218's difficulty formulae therefore use:\n+\n+- a 128-block lookback for $\\mathsf{MeanTarget}(h)$;\n+- $\\mathsf{ActualTimespan}(h) = \\mathsf{MedianTime}(h) - \\mathsf{MedianTime}(h-128)$;\n+- $\\mathsf{AveragingWindowTimespan}(h) = 128\\times20 = 2{,}560$ seconds.\n+\n+The median-time span, damping factor, adjustment bounds, and proof-of-work\n+limit remain unchanged. Implementations MUST apply the existing difficulty\n+algorithm with these height-dependent inputs from the activation block;\n+there is no separate difficulty reset.\n+\n+## Halving interval and scheduled subsidy\n+\n+Define:\n+\n+$$\\mathsf{TwentySecondHalvingInterval} : \\mathbb{N} := \\mathsf{PostNU7HalvingInterval}\\cdot 5/4 = 6{,}300{,}000.$$\n+\n+For the following formula, let $A_{20} = \\mathsf{TwentySecondActivationHeight}$,\n+$A_7 = \\mathsf{NU7ActivationHeight}$, and\n+$A_B = \\mathsf{BlossomActivationHeight}$. These are nonnegative integer heights.\n+For $h \\geq A_{20}$, define:\n+\n+$$\\mathsf{Halving}(h) := \\left\\lfloor \\frac{A_B-\\mathsf{SlowStartShift}}{840{,}000} +\\frac{A_7-A_B}{1{,}680{,}000} +\\frac{A_{20}-A_7}{5{,}040{,}000} +\\frac{h-A_{20}}{6{,}300{,}000} \\right\\rfloor.$$\n+\n+The denominators are the pre-Blossom, post-Blossom, post-NU7, and 20-second\n+halving intervals, respectively. For $h < A_{20}$, retain ZIP 218's halving\n+function, including its slow-start cases. Implementations MUST preserve the fractional progress through the\n+halving interval at activation. They MUST NOT restart an interval at $A_{20}$.\n+\n+For $h \\geq A_{20}$, replace ZIP 237's scheduled subsidy with:\n+\n+$$\\mathsf{ScheduledBlockSubsidy}(h) := \\left\\lfloor \\frac{\\mathsf{MaxBlockSubsidy}}{2\\cdot3\\cdot(5/4)\\cdot 2^{\\mathsf{Halving}(h)}} \\right\\rfloor.$$\n+\n+The factors 2, 3, and $5/4$ are the Blossom, NU7, and 20-second spacing\n+ratios, respectively. The result is a nonnegative integer number of zatoshi.\n+Implementations MUST use the exact rational ratio $5/4$ and apply the floor only to the complete\n+expression. Multiplying an already-rounded 25-second subsidy by $4/5$ can\n+produce a different answer.\n+\n+For example, at halving index 3 the scheduled subsidy changes from\n+26,041,666 zatoshi to 20,833,333 zatoshi. Flooring\n+$26{,}041{,}666\\times4/5$ would incorrectly produce 20,833,332 zatoshi.\n+\n+## Integration with ZIP 237\n+\n+For $h \\geq A_{20}$, redefine ZIP 237's $\\mathsf{HalvingInterval}(h)$ to return\n+$\\mathsf{TwentySecondHalvingInterval}$. Retain its earlier cases for $h<A_{20}$.\n+Its existing fraction formula then gives:\n+\n+$$\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}(h) = \\frac{\\lfloor 6{,}931{,}680{,}000 / 6{,}300{,}000\\rfloor}{10^{10}} = \\frac{1100}{10^{10}}.$$\n+\n+Retain ZIP 237's definition:\n+\n+$$\\mathsf{BlockSubsidy}(h) := \\mathsf{ScheduledBlockSubsidy}(h) + \\mathsf{AdditionalBlockSubsidy}(h).$$\n+\n+Implementations MUST carry the existing NSM Value Balance through activation.\n+They MUST NOT reseed it or recompute historical balances under the new spacing.\n+ZIP 237's additional-subsidy ceiling and balance-update rules continue to apply.\n+\n+**Open specification item:** define the NSM reissuance trigger for an\n+activation before reissuance starts. ZIP 259's fixed trigger heights use the\n+25-second issuance curve. The amended trigger must use the 20-second curve\n+and reissuance fraction. Its reference reserve must use cumulative scheduled\n+issuance through $h-1$, retaining each historical era's integer rounding.\n+If reissuance starts before this upgrade, retain that activated state.\n+\n+## Action limits\n+\n+This proposal does not change ZIP 218's action limits, global shielded budget,\n+or block size limit. Implementations MUST continue to enforce them.\n+\n+## Minimum difficulty blocks on Testnet\n+\n+Retain ZIP 218's 450-second elapsed-time threshold after this upgrade.\n+A block qualifies only if its timestamp is strictly more than 450 seconds\n+after the preceding block's timestamp. In that case its `nBits` MUST equal\n+$\\mathsf{ToCompact}(\\mathsf{PoWLimit})$, following ZIP 218.\n+\n+At and after $A_{20}$, this fixed threshold replaces the expression\n+$18\\cdot\\mathsf{PoWTargetSpacing}(h)$. Thus 450 seconds does not qualify,\n+while 451 seconds does. This rule does not affect Mainnet.\n+Existing timestamp-validity bounds and local-clock tolerances remain unchanged.\n+The Testnet minimum-difficulty exception remains subject to those rules.\n+\n+## Non-consensus node behaviour\n+\n+When converting a height range to expected elapsed time, implementations\n+SHOULD use the target spacing applicable to each block. The expected time\n+from an accepted tip at height $a$ to height $b>a$ is\n+$\\sum_{h=a+1}^{b} \\mathsf{PoWTargetSpacing}(h)$, including the new spacing\n+for the activation block. Policies defined as elapsed durations SHOULD\n+retain those durations across activation.\n+\n+### Transaction expiry\n+\n+Wallets SHOULD increase the default expiry delta from 120 to 150 blocks.\n+Choose the default using the transaction's target height, which is the next\n+block height after the chain tip. Use 150 when that height is at or above\n+$A_{20}$; otherwise retain the prior default. Compute expiry relative to the\n+target height as specified by ZIP 218. Wallets SHOULD preserve an explicit\n+user-configured delta across activation.\n+\n+### Block-count-based defaults\n+\n+Implementations SHOULD use the following post-activation defaults where\n+they expose the corresponding settings. The table scales duration-based\n+values by $5/4$ relative to ZIP 218. Larger rollback windows conform.\n+\n+| Constant | 25-second baseline | 20-second recommendation |\n+|----------|--------------------|--------------------------|\n+| `MAX_REORG_LENGTH` | At least 600 | At least 750 |\n+| `MAX_BLOCKS_IN_TRANSIT_PER_PEER` | 48 | 60 |\n+| `BLOCK_DOWNLOAD_WINDOW` | 3072 | 3840 |\n+| `MIN_BLOCKS_TO_KEEP` | 864 | 1080 |\n+\n+The consensus coinbase maturity remains 100 blocks. This proposal leaves\n+`TX_EXPIRING_SOON_THRESHOLD` at 3 and\n+`NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD` at 1728, following ZIP 218.\n+\n+### Anchor selection depth\n+\n+Wallets SHOULD retain ZIP 315's recommended anchor depth of 3 blocks.\n+[^zip-0315] Its expected duration changes from 75 to 60 seconds.\n+This recommendation does not guarantee protection against reorganizations.\n+\n+\n+# Rationale\n+\n+## Capacity and resource costs\n+\n+With unchanged per-block limits, nodes and wallets must process 25% more\n+blocks per unit time. If per-block processing time remains unchanged, the\n+worst-case proportion of time spent processing blocks increases by 25%.\n+Syncing blocks produced during a given time offline also requires 25% more\n+work. Maximum two-action Orchard throughput rises from 6.6 to 8.25\n+transactions per second. ZIP 218's compact-field model rises from approximately 169.10 to\n+211.38 MB/day; it excludes serialization and transport overhead. [^zip-0218]\n+\n+This figure describes worst-case compact sync bandwidth when blocks fill the\n+shielded action budget. It does not establish a daily download requirement\n+for every wallet.\n+\n+Ironwood wallet bandwidth constrains the choice of its per-block action limit.\n+This draft retains ZIP 218's limit; it does not increase that limit to obtain\n+more throughput. Shorter target spacing still increases the maximum number\n+of actions per day by 25%. Retaining the per-block limit therefore does not\n+preserve the existing daily bandwidth bound.\n+\n+**Open design item:** evaluate a lower Ironwood per-block action limit against\n+a wallet bandwidth budget. Account for the private information retrieval\n+(PIR) query design and measure its download costs before selecting a limit.\n+The compact-field estimate alone does not establish those query costs.\n+\n+**TODO:** add matched 25-second and 20-second verification, propagation, and\n+wallet sync measurements under sustained near-full-block workloads.\n+\n+## Stale blocks and effective propagation time\n+\n+Following ZIP 218's propagation analysis, we estimate stale blocks per\n+accepted block as the work-weighted time miners remain on the old tip divided\n+by target spacing: $\\phi = \\tau_{\\mathrm{eff}}/T$. [^zip-0218]\n+[^propagation-report]\n+\n+The Zebra-only devnet data accompanying the propagation report contains\n+621 block hashes at heights of at least 400 with bodies of at least\n+1,990,000 bytes. This near-full-block sample has a mean reach-90% time of\n+1.55994 seconds. [^propagation-report]\n+\n+The mixed-client experiment uses 80 geographically distributed nodes:\n+40 run Zakura and 40 run Zebra. **TODO:** identify the mixed-run dataset and\n+report its reach-90% measurement. The 1.56-second input below comes from\n+the Zebra-only sample and has not been established for the mixed run.\n+\n+Effective old-tip mining time accumulates the mining work still spent on\n+the old tip during propagation. For example, half the network mining the\n+old tip for one second contributes 0.5 effective seconds.\n+\n+If dissemination rises linearly from 0% to 90% over 1.56 seconds, the\n+old-tip mining share falls from 100% to 10%. Its average is 55%, giving:\n+\n+$$\\tau_{\\mathrm{eff}}\\approx1.56\\times0.55=0.858\\text{ seconds}.$$\n+\n+At 20 seconds, this gives $\\phi\\approx0.858/20=4.29\\%$. It assumes nodes\n+represent mining work and omits any material tail after reaching 90%.\n+Remaining old-tip mining after that percentile adds to the effective time.\n+\n+Under Poisson discovery, the chance of another block anywhere within\n+1.56 seconds is $1-e^{-1.56/20}\\approx7.50\\%$. That includes blocks found\n+by miners who already received the new tip. The effective-time model counts\n+only old-tip work. Its probability of at least one competitor is\n+$1-e^{-0.858/20}\\approx4.20\\%$, distinct from the expected count of 4.29%.\n+\n+ZIP 218 reports a 3.26% propagation-based estimate at 25 seconds.\n+[^zip-0218] Holding that effective delay fixed gives 4.08% at 20 seconds.\n+The near-full-block filter gives 4.29% under the same model. The difference\n+reflects the sample filter, rather than a different scaling rule.\n+\n+**TODO:** publish the filtered dataset and calculation with node coverage\n+and timestamp semantics. The 4.29% value is a theoretical estimate, not an\n+observed 20-second stale rate. The reach proxy does not predict reorganization\n+depth.\n+\n+## Difficulty averaging duration\n+\n+ZIP 218's 102-block window at 25 seconds spans an expected 2,550 seconds.\n+The equivalent count at 20 seconds is 127.5 blocks. This proposal rounds\n+up to 128, giving 2,560 seconds, about 0.39% longer. A 127-block window\n+would give 2,540 seconds. Keeping 102 blocks would shorten the duration\n+to 2,040 seconds.\n+\n+This proposal preserves the averaging duration because mining hash rate can\n+vary. Reducing that duration would change the DAA's response to hash rate\n+changes as well as changing block target spacing.\n+\n+**TODO: simulate activation from the 25-second target and 102-block window\n+to the 20-second target and 128-block window.** Include hash rate shocks,\n+network splits, timestamp effects, and recovery criteria.\n+\n+## Issuance and schedule continuity\n+\n+The interval change preserves its expected duration:\n+\n+$$6{,}300{,}000\\times20=5{,}040{,}000\\times25\\text{ seconds}.$$\n+\n+The halving formula retains progress through the 25-second era. Scaling the\n+unrounded scheduled subsidy by $4/5$ preserves the expected issuance rate\n+before rounding. Integer rounding changes the realized schedule.\n+\n+The NSM fraction changes from $1375/10^{10}$ to $1100/10^{10}$, also a factor\n+of $4/5$. ZIP 237's ceiling and evolving balance mean this does not establish\n+exact equality of reissuance per unit of wall-clock time.\n+\n+**Open specification item:** amend the funding-stream end heights and address\n+period calculations in ZIPs 207 and 214 for a second spacing transition.\n+[^zip-0207] [^zip-0214] Preserve fractional progress through the active address\n+period and define integer-boundary rounding. Keep ended streams ended and\n+ensure recipient lists cover every extended period. The deployment height\n+and schedule amendments must be settled together; changing NU7 constants\n+retroactively is invalid.\n+\n+\n+# Reference implementation\n+\n+**TODO: link the implementation and activation vectors.** Cover the 25-to-20\n+transition, 102-to-128 averaging window, exact subsidy rounding, partial\n+halving intervals, funding schedules, NSM state continuity, and Testnet's\n+450/451-second boundary. Experiment configurations alone do not establish\n+an interoperable consensus implementation.\n+\n+\n+# Deployment\n+\n+Deploy this proposal in a separate network upgrade after NU7. ZIP 259 defines\n+NU7's deployment; this proposal does not replace that deployment. [^zip-0259]\n+A separate deployment ZIP will assign this upgrade's activation heights and\n+consensus branch IDs.\n+\n+Before this ZIP advances to Proposed status, resolve the funding-stream and\n+NSM reissuance-height specification items. Complete the activation and\n+rounding analysis against the selected deployment parameters.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification](protocol/protocol.pdf)\n+\n+[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)\n+\n+[^zip-0207]: [ZIP 207: Funding Streams](https://zips.z.cash/zip-0207)\n+\n+[^zip-0214]: [ZIP 214: Consensus rules for a Zcash Development Fund](https://zips.z.cash/zip-0214)\n+\n+[^zip-0218]: [ZIP 218: 25-second Block Target Spacing](https://zips.z.cash/zip-0218)\n+\n+[^zip-0237]: [ZIP 237: Network Sustainability Mechanism: Halving-Preserving Issuance](https://zips.z.cash/zip-0237)\n+\n+[^zip-0259]: [ZIP 259: Deployment of the NU7 Network Upgrade](https://zips.z.cash/zip-0259)\n+\n+[^zip-0315]: [ZIP 315: Best Practices for Wallet Implementations](zip-0315.rst)\n+\n+[^propagation-report]: [Zcash Block Time Reduction Appears Safe for NU7 w/ Zebra Only Devnet](https://forum.zcashcommunity.com/t/zcash-block-time-reduction-appears-safe-for-nu7-w-zebra-only-devnet/55586)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-valargroup-20-second-block-target-spacing.md","title":"Reduce Block Target Spacing from 25 to 20 Seconds","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"activation-and-target-spacing","text":"Activation and target spacing"},{"level":2,"id":"difficulty-averaging-window","text":"Difficulty averaging window"},{"level":2,"id":"halving-interval-and-scheduled-subsidy","text":"Halving interval and scheduled subsidy"},{"level":2,"id":"integration-with-zip-237","text":"Integration with ZIP 237"},{"level":2,"id":"action-limits","text":"Action limits"},{"level":2,"id":"minimum-difficulty-blocks-on-testnet","text":"Minimum difficulty blocks on Testnet"},{"level":2,"id":"non-consensus-node-behaviour","text":"Non-consensus node behaviour"},{"level":3,"id":"transaction-expiry","text":"Transaction expiry"},{"level":3,"id":"block-count-based-defaults","text":"Block-count-based defaults"},{"level":3,"id":"anchor-selection-depth","text":"Anchor selection depth"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"capacity-and-resource-costs","text":"Capacity and resource costs"},{"level":2,"id":"stale-blocks-and-effective-propagation-time","text":"Stale blocks and effective propagation time"},{"level":2,"id":"difficulty-averaging-duration","text":"Difficulty averaging duration"},{"level":2,"id":"issuance-and-schedule-continuity","text":"Issuance and schedule continuity"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1429,"title":"Add draft ZIP: Compact Block Relay","author":"upbqdn","createdAt":"2026-10-31T04:37:11Z","updatedAt":"2026-11-05T16:10:59Z","url":"https://github.com/zcash/zips/pull/1429","diffUrl":"https://github.com/zcash/zips/pull/1429.diff","headRef":"compact-block-relay","headRepo":"upbqdn/zips","headSha":"de997f89922978d5727ce7acf764a728a0050ff5","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"[Rendered.](https://github.com/upbqdn/zips/blob/compact-block-relay/zips/draft-marek-compact-block-relay.md) Proposes compact block relay for Zcash, adapting BIP 152, as tracked in #360. A compact block carries the full Zcash header, 6-byte short IDs derived from each transaction's ZIP 239 wtxid (or","zipRefs":[239],"draftRefs":["draft-marek-compact-block-relay"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -233,6 +233,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-marek-compact-block-relay</td> <td class=\"left\"><a href=\"zips/draft-marek-compact-block-relay.md\">Compact Block Relay</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/360\">zips#360</a></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-marek-compact-block-relay.md","status":"added","additions":386,"deletions":0,"patch":"@@ -0,0 +1,386 @@\n+    ZIP: Unassigned\n+    Title: Compact Block Relay\n+    Owners: Marek <marek@zfnd.org>\n+    Status: Draft\n+    Category: Network\n+    Created: 2026-10-30\n+    License: MIT\n+    Discussions-To: <https://github.com/zcash/zips/issues/360>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to\n+be interpreted as described in BCP 14 [^BCP14] when, and only when, they appear\n+in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in\n+section 3.12 of the Zcash Protocol Specification. [^protocol-networks]\n+\n+The terms \"txid\", \"authorizing data commitment\" (denoted `auth_digest`), and\n+\"witnessed transaction identifier\" (\"wtxid\") are to be interpreted as\n+described in ZIP 239. [^zip-0239]\n+\n+best tip\n+:   The tip of a node's best fully validated block chain.\n+\n+early relay\n+:   Announcing or sending a block before the sending node has fully validated\n+    it.\n+\n+authenticated block\n+:   A block that passes the checks in [Early Relay].\n+\n+\n+# Abstract\n+\n+This ZIP specifies compact block relay for the Zcash peer-to-peer network,\n+adapted from BIP 152. [^bip-0152] A compact block contains the block header,\n+short identifiers for the block's transactions, and its coinbase transaction;\n+the receiver reconstructs the block from transactions it already has. Peers\n+that both opt in may also relay a block before fully validating it, once its\n+header and transaction commitments have been checked.\n+\n+\n+# Motivation\n+\n+As in Bitcoin before BIP 152, a Zcash node announces a block only after fully\n+validating it, and a peer that requests the block receives every transaction\n+in it, although it has usually already received most of them through\n+transaction relay. Both delay the propagation of new blocks between miners.\n+\n+BIP 152 replaces transactions the receiver already has with short\n+identifiers, and allows a block to be relayed before it is fully validated.\n+It cannot be used in Zcash unchanged: Zcash block headers include an Equihash\n+solution, and since NU5 a transaction's txid does not commit to its\n+authorizing data [^zip-0244], so short identifiers and the checks made before\n+early relay need to cover the wtxid.\n+\n+\n+# Specification\n+\n+All messages defined in this ZIP are serialized in the standard encoding for\n+P2P messages. [^zip-0204]\n+\n+## Service Flag\n+\n+The service flag `NODE_COMPACT_BLOCKS` is defined as bit 16\n+(`0x0000000000010000`).\n+\n+A node that sets `NODE_COMPACT_BLOCKS` in its `version` message supports the\n+messages defined in this ZIP and follows the rules in [Early Relay] and\n+[Peer Misbehaviour].\n+\n+The messages defined in this ZIP MUST NOT be sent on a connection unless both\n+peers set `NODE_COMPACT_BLOCKS` in their `version` messages, and MUST NOT be\n+sent before the `version`/`verack` handshake completes. Support MUST NOT be\n+inferred from any other source, such as `addr` records, user agents, or\n+protocol versions. A node MUST ignore these messages on other connections.\n+\n+## `sendcmpct`\n+\n+The `sendcmpct` message is defined as a message where the `command` field is\n+(NUL-padded) `\"sendcmpct\"`, with the following payload:\n+\n+| Bytes | Name | Data Type | Description |\n+|-------|------|-----------|-------------|\n+| 1 | `announce` | `uint8` | 1 to request `cmpctblock` announcements, 0 to stop them. |\n+| 8 | `version` | `uint64` | Compact block encoding version. |\n+\n+This ZIP specifies version 1. A message with `announce` other than 0 or 1 is\n+malformed.\n+\n+A node MAY send `cmpctblock` messages to a peer only while the most recent\n+`sendcmpct` received from that peer has `announce` = 1 and `version` = 1. A\n+`sendcmpct` with any other version disables announcements to its sender, and\n+is not a protocol violation.\n+\n+A node MUST NOT request announcements from more than three peers at a time. It\n+SHOULD request them from the peers that most recently first announced blocks\n+it then added to its best chain; announcements of blocks it has not validated\n+MUST NOT be counted. A node MUST ignore `cmpctblock` messages it has not\n+requested. Disabling announcements MUST NOT cancel an outstanding\n+`getblocktxn` request.\n+\n+There is no low-bandwidth mode and no compact block inventory type. Blocks\n+are requested with `getdata` and `MSG_BLOCK`, as before.\n+\n+## `cmpctblock`\n+\n+The `cmpctblock` message is defined as a message where the `command` field is\n+(NUL-padded) `\"cmpctblock\"`, with the following payload:\n+\n+| Bytes | Name | Data Type | Description |\n+|-------|------|-----------|-------------|\n+| varies | `header` | Block header | Encoded as in section 7.6 of the Zcash Protocol Specification. [^protocol-blockheader] |\n+| 8 | `nonce` | `uint64` | Nonce used to compute short IDs. |\n+| varies | `shortidsCount` | `CompactSize` | Number of short IDs. |\n+| `6 * shortidsCount` | `shortids` | `uint8[6][]` | Short IDs of the transactions that are not prefilled, in block order. |\n+| varies | `prefilledCount` | `CompactSize` | Number of prefilled transactions. |\n+| varies | `prefilled` | `PrefilledTransaction[]` | Prefilled transactions, in block order. |\n+\n+The header includes the Equihash solution, and does not include the\n+transaction count that follows it in a `headers` message. On Mainnet and\n+Testnet it is 1,487 bytes.\n+\n+A `PrefilledTransaction` has the following format:\n+\n+| Bytes | Name | Data Type | Description |\n+|-------|------|-----------|-------------|\n+| varies | `index` | `CompactSize` | Differentially encoded index of the transaction in the block. |\n+| varies | `tx` | Transaction | The transaction, encoded as in section 7.1 of the Zcash Protocol Specification. [^protocol-txnencoding] |\n+\n+In a list of differentially encoded indexes, the first entry is the absolute\n+index, and each later entry is the absolute index minus the previous absolute\n+index minus 1. For example, `[0, 0, 2]` encodes the absolute indexes\n+`[0, 1, 4]`. Counts and indexes MUST be checked before allocation or\n+indexing, and count addition and index decoding MUST NOT overflow.\n+\n+Let `n = shortidsCount + prefilledCount` be the number of transactions in the\n+block. A node MUST reject a `cmpctblock` message if:\n+\n+- any of `shortidsCount`, `prefilledCount`, and `n` exceeds 37,037;\n+- the absolute prefilled indexes are not strictly increasing, or any is not\n+  less than `n`;\n+- the first prefilled transaction is not a coinbase transaction at index 0,\n+  or any other prefilled transaction is a coinbase transaction; or\n+- the sizes of the header, the `CompactSize` encoding of `n`, and the\n+  prefilled transactions, plus 54 bytes for each short ID, sum to more than\n+  2,000,000 bytes.\n+\n+Senders SHOULD prefill only the coinbase transaction.\n+\n+### Short Transaction IDs\n+\n+The sender SHOULD choose `nonce` uniformly at random for each `cmpctblock`\n+message. The short ID of a transaction is computed as follows:\n+\n+1. Let `h` be the SHA-256 hash of the encoded block header followed by\n+   `nonce`.\n+2. Let `k0` and `k1` be bytes 0 to 7 and 8 to 15 of `h`, each interpreted as\n+   a little-endian `uint64`.\n+3. Compute SipHash-2-4 with keys `(k0, k1)` of the transaction's 32-byte txid\n+   for v4 and earlier transactions, or of its 64-byte wtxid for v5 and later\n+   transactions.\n+4. The short ID is the first 6 bytes of the little-endian encoding of the\n+   result.\n+\n+## `getblocktxn` and `blocktxn`\n+\n+The `getblocktxn` message is defined as a message where the `command` field\n+is (NUL-padded) `\"getblocktxn\"`, with the following payload:\n+\n+| Bytes | Name | Data Type | Description |\n+|-------|------|-----------|-------------|\n+| 32 | `blockHash` | `uint8[32]` | Hash of the block announced by `cmpctblock`. |\n+| varies | `indexesCount` | `CompactSize` | Number of requested transactions. |\n+| varies | `indexes` | `CompactSize[]` | Differentially encoded indexes of the requested transactions. |\n+\n+The `indexesCount` field MUST NOT exceed 37,037, and the absolute indexes\n+MUST be strictly increasing and less than `n`.\n+\n+The `blocktxn` message is defined as a message where the `command` field is\n+(NUL-padded) `\"blocktxn\"`, with the following payload:\n+\n+| Bytes | Name | Data Type | Description |\n+|-------|------|-----------|-------------|\n+| 32 | `blockHash` | `uint8[32]` | `blockHash` from the request. |\n+| varies | `txCount` | `CompactSize` | Number of transactions. |\n+| varies | `tx` | Transaction[] | The requested transactions, in the order requested. |\n+\n+A `blocktxn` message MUST contain exactly the requested transactions, which\n+MUST NOT exceed 2,000,000 bytes in total.\n+\n+A node that sends a `cmpctblock` message MUST be able to answer a\n+`getblocktxn` request for any subset of the block's transactions, and SHOULD\n+retain the block for this purpose for 15 seconds. It SHOULD limit the\n+`blocktxn` messages it sends for one `cmpctblock` message, including repeated\n+and empty responses, to 2,000,000 bytes in total, and MAY ignore requests\n+beyond that limit or after that time. It MUST ignore requests for blocks it\n+has not announced on that connection or no longer retains, and requests for\n+indexes outside the block.\n+\n+A node MUST ignore a `blocktxn` message that does not answer its outstanding\n+`getblocktxn` request on that connection, and SHOULD wait at most two seconds\n+for the answer.\n+\n+## Sending Compact Blocks\n+\n+A node MUST send each block announcement as `cmpctblock` to at most three\n+peers, and SHOULD do so only when the `cmpctblock` message is smaller than the\n+block. Other peers receive the announcement as before. A node MAY announce a\n+fully validated block with `cmpctblock` to any peer that requested\n+announcements.\n+\n+### Early Relay\n+\n+A node MAY relay a block before fully validating it only to peers that set\n+`NODE_COMPACT_BLOCKS`, and only after checking that:\n+\n+- NU5 is active at the block's height;\n+- the block's parent is the node's best tip, and the block's coinbase height\n+  is one more than the best tip's height;\n+- the header has the expected difficulty, a valid Equihash solution, a hash\n+  that meets its target, and a valid time with respect to both the local\n+  clock and the median time past;\n+- the block is at most 2,000,000 bytes, its first transaction is a coinbase\n+  transaction, and no later transaction has a transparent input with a null\n+  previous output;\n+- the block passes the shielded action limits, and the subsidy and funding\n+  stream checks that do not depend on value pool state;\n+- the txids are distinct and reproduce `hashMerkleRoot`, and each\n+  transaction's consensus branch ID is valid at the block's height;\n+- the `auth_digest` values of all transactions reproduce the authorizing data\n+  root, which together with the parent's chain history reproduces\n+  `hashBlockCommitments`, as specified in ZIP 244; [^zip-0244] and\n+- the block has not been rejected or invalidated by the node.\n+\n+While it completes validation, the node MAY announce the block, and MAY answer\n+`getdata` requests for it from those peers. It MUST stop early relay of the\n+block when its validation fails or is cancelled, or when its parent is no\n+longer the best tip, and MUST check these conditions before sending each\n+message. It SHOULD continue to answer `getblocktxn` requests for a\n+`cmpctblock` message it has already sent.\n+\n+## Receiving Compact Blocks\n+\n+Before retaining a `cmpctblock` message or querying its mempool, a node MUST\n+check the coinbase height, NU5 activation, the header's time, target, and\n+proof of work, and that the block's parent is its best tip. If these checks\n+fail, the node SHOULD treat the message as an announcement of the block hash.\n+A node SHOULD limit the compact blocks it retains and the `getblocktxn`\n+requests it sends, both per peer and in total.\n+\n+The node then reconstructs the block:\n+\n+1. Place the prefilled transactions at their indexes.\n+2. For each index that is not prefilled, if its short ID occurs only once in\n+   the message and exactly one mempool transaction has that short ID, place\n+   that transaction at the index.\n+3. If any index remains empty, send one `getblocktxn` request for all of\n+   them.\n+4. Check that the `blocktxn` response contains one transaction for each\n+   requested index, with the corresponding short ID.\n+5. Check the block size, the coinbase transaction, that the txids are\n+   distinct and reproduce `hashMerkleRoot`, and that the `auth_digest` values\n+   reproduce the commitments as in [Early Relay], then fully validate the\n+   block.\n+\n+Step 5 is required because a short ID can match the wrong transaction, and\n+`hashMerkleRoot` does not commit to authorizing data.\n+\n+If any step fails, or the response does not arrive in time, the node MUST\n+request the block from the same peer with `getdata` and `MSG_BLOCK`, using its\n+usual timeout. A reconstruction failure MUST NOT cause the node to treat the\n+block as invalid.\n+\n+## Peer Misbehaviour\n+\n+A node MAY treat a malformed message defined in this ZIP as it treats other\n+malformed P2P messages. This includes non-canonical `CompactSize` encodings,\n+indexes out of range, and counts or sizes that exceed the limits above.\n+\n+A node MUST NOT penalize a peer for short ID collisions, failed\n+reconstructions, commitment mismatches in reconstructed blocks, `blocktxn`\n+timeouts, or an unknown parent.\n+\n+A node MUST NOT penalize a peer that set `NODE_COMPACT_BLOCKS` when an\n+authenticated block it sent fails later validation; the node still rejects\n+the block. A node MAY penalize a peer that sends a full block failing the\n+proof of work, size, structure, or commitment checks in [Early Relay]. If the\n+node cannot authenticate a block because the parent's chain history is\n+unavailable, but the block passes the checks that do not depend on it, the\n+node MUST NOT penalize the peer for a later transaction or contextual\n+validation failure. Failures of local time policy and local invalidation are\n+not peer misbehaviour.\n+\n+## Deployment\n+\n+This ZIP applies on Mainnet and Testnet from NU5 activation. It does not\n+require a network upgrade. Peers that do not set `NODE_COMPACT_BLOCKS` use\n+block relay as before.\n+\n+\n+# Rationale\n+\n+This ZIP differs from BIP 152 as follows:\n+\n+- The header is the Zcash block header, including the Equihash solution.\n+- Short IDs are computed from the wtxid for v5 and later transactions, so\n+  version 1 of the encoding is not Bitcoin's version 1.\n+- Only high-bandwidth announcements are defined.\n+- Support is signalled by a service flag rather than a protocol version, and\n+  an unsupported version disables announcements rather than being negotiated.\n+- The coinbase transaction is always prefilled, and all counts and sizes are\n+  bounded.\n+- Early relay requires checking authorizing data, and is limited to children\n+  of the best tip.\n+\n+Zebra and zcashd ignore unknown P2P messages after the handshake\n+[^zebra-codec] [^zcashd-messages], so a service flag is not needed to avoid\n+disconnection. It is needed because both peers have to agree on the encoding,\n+and on not penalizing authenticated blocks that fail later validation; an\n+ignored message establishes neither. A protocol version signals support for a\n+network upgrade, not for an optional relay policy. [^zip-0201]\n+\n+The limit of 37,037 transactions is the 2,000,000-byte block size limit\n+divided by 54 bytes, a lower bound on the size of a transaction.\n+\n+Bit 16 avoids the service flags allocated in Zcash and Bitcoin\n+[^bitcoin-services], the range 24 to 31 reserved for temporary experiments,\n+and bits 12 to 14, which are used by pending Bitcoin proposals.\n+[^bitcoin-utreexo] [^bitcoin-utxo-sharing]\n+\n+\n+# Security and Privacy Considerations\n+\n+A short ID identifies a transaction only probabilistically. A node that\n+skipped the checks in step 5 of [Receiving Compact Blocks] could accept a\n+block whose authorizing data differs from the data committed to by its\n+header.\n+\n+An early relayed block can be invalid. Receiving one does not indicate that\n+the block is valid, and nodes MUST fully validate it before accepting it.\n+\n+A `getblocktxn` request reveals which transactions the sender did not have,\n+and the timing of announcements can reveal a node's peers. Setting\n+`NODE_COMPACT_BLOCKS` reveals that a node supports this ZIP.\n+\n+\n+# Reference Implementation\n+\n+Zebra implements this ZIP with the option `compact_block_relay`, on by\n+default. [^zebra]\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^protocol-txnencoding]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.1: Transaction Encoding and Consensus](protocol/protocol.pdf#txnencoding)\n+\n+[^protocol-blockheader]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.6: Block Header Encoding and Consensus](protocol/protocol.pdf#blockheader)\n+\n+[^zip-0201]: [ZIP 201: Network Peer Management for Overwinter](zip-0201.rst)\n+\n+[^zip-0204]: [ZIP 204: Zcash P2P Network Protocol](zip-0204.rst)\n+\n+[^zip-0239]: [ZIP 239: Relay of Version 5 Transactions](zip-0239.rst)\n+\n+[^zip-0244]: [ZIP 244: Transaction Identifier Non-Malleability](zip-0244.rst)\n+\n+[^bip-0152]: [BIP 152: Compact Block Relay](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki)\n+\n+[^zebra]: [Zebra pull request #12186: negotiate compact block relay with its ZIP service bit, on by default](https://github.com/ZcashFoundation/zebra/pull/12186)\n+\n+[^zebra-codec]: [Zebra P2P message codec](https://github.com/ZcashFoundation/zebra/blob/4b8a202e41d41b8ab84a703c72ef77fd03d4d000/zebra-network/src/protocol/external/codec.rs)\n+\n+[^zcashd-messages]: [zcashd P2P message processing](https://github.com/zcash/zcash/blob/master/src/main.cpp)\n+\n+[^bitcoin-services]: [Bitcoin Core service flags](https://github.com/bitcoin/bitcoin/blob/master/src/protocol.h)\n+\n+[^bitcoin-utreexo]: [Proposed BIP 183: Utreexo — Peer Services, Bitcoin BIPs pull request #1923](https://github.com/bitcoin/bips/pull/1923)\n+\n+[^bitcoin-utxo-sharing]: [Proposed BIP: P2P UTXO Set Sharing, Bitcoin BIPs pull request #2137](https://github.com/bitcoin/bips/pull/2137)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-marek-compact-block-relay.md","title":"Compact Block Relay","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"service-flag","text":"Service Flag"},{"level":2,"id":"sendcmpct","text":"sendcmpct"},{"level":2,"id":"cmpctblock","text":"cmpctblock"},{"level":3,"id":"short-transaction-ids","text":"Short Transaction IDs"},{"level":2,"id":"getblocktxn-and-blocktxn","text":"getblocktxn and blocktxn"},{"level":2,"id":"sending-compact-blocks","text":"Sending Compact Blocks"},{"level":3,"id":"early-relay","text":"Early Relay"},{"level":2,"id":"receiving-compact-blocks","text":"Receiving Compact Blocks"},{"level":2,"id":"peer-misbehaviour","text":"Peer Misbehaviour"},{"level":2,"id":"deployment","text":"Deployment"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1436,"title":"Add draft ZIP: Limited Block Serving Service Flag","author":"upbqdn","createdAt":"2026-11-03T23:27:17Z","updatedAt":"2026-11-05T16:09:26Z","url":"https://github.com/zcash/zips/pull/1436","diffUrl":"https://github.com/zcash/zips/pull/1436.diff","headRef":"node-network-limited","headRepo":"upbqdn/zips","headSha":"bbc5d6c903f5fb49f6cb6039844d8c0f2ec59dce","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"[Rendered.](https://github.com/upbqdn/zips/blob/node-network-limited/zips/draft-marek-node-network-limited.md) Proposes `NODE_NETWORK_LIMITED` (bit 10) for the current P2P protocol: a node that sets it serves at least the most recent 2,304 blocks of its best chain, but not necessarily older ones. It","zipRefs":[],"draftRefs":["draft-marek-node-network-limited"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -237,6 +237,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-marek-node-network-limited</td> <td class=\"left\"><a href=\"zips/draft-marek-node-network-limited.md\">Limited Block Serving Service Flag</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-marek-node-network-limited.md","status":"added","additions":120,"deletions":0,"patch":"@@ -0,0 +1,120 @@\n+    ZIP: Unassigned\n+    Title: Limited Block Serving Service Flag\n+    Owners: Marek <marek@zfnd.org>\n+    Status: Draft\n+    Category: Network\n+    Created: 2026-11-03\n+    License: MIT\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and only\n+when, they appear in all capitals.\n+\n+best tip\n+:   The tip of a node's best block chain.\n+\n+limited peer\n+:   A peer that advertises `NODE_NETWORK_LIMITED` and not `NODE_NETWORK`.\n+\n+\n+# Abstract\n+\n+This ZIP defines the service flag `NODE_NETWORK_LIMITED` for the Zcash\n+peer-to-peer protocol specified in ZIP 204 [^zip-0204]. A node that sets it\n+serves at least the most recent 2,304 blocks of its best chain, but not\n+necessarily older blocks. It is the Zcash counterpart of BIP 159\n+[^bip-0159].\n+\n+\n+# Motivation\n+\n+A node that deletes old block bodies, such as a Zebra node with pruning\n+enabled, cannot set `NODE_NETWORK`, and ZIP 204 defines no other flag for\n+serving blocks. Peers cannot tell that such a node serves recent blocks: some\n+ignore it, and others request historical blocks it does not have.\n+\n+The Version 2 P2P protocol draft [^draft-p2p-v2] defines the same flag. This\n+ZIP specifies it for the current protocol, so that it can be deployed without\n+the rest of that draft.\n+\n+\n+# Specification\n+\n+## Service Flag\n+\n+The service flag `NODE_NETWORK_LIMITED` is defined as bit 10\n+(`0x0000000000000400`) of the `services` field.\n+\n+A node that sets `NODE_NETWORK_LIMITED` MUST be able to serve every block of\n+its best chain at a height greater than its best tip height minus 2,304, in\n+response to `getdata` requests with `MSG_BLOCK`.\n+\n+A node that cannot serve the complete block chain SHOULD set\n+`NODE_NETWORK_LIMITED` if it meets this requirement. A node that sets\n+`NODE_NETWORK` MAY also set it.\n+\n+## Serving\n+\n+A limited peer SHOULD NOT serve blocks more than 2,304 blocks below its best\n+tip. It MAY disconnect a peer that requests a block more than 2,306 blocks\n+below its best tip; the 2-block margin allows for the best tip advancing while\n+a request is in flight.\n+\n+## Requesting\n+\n+A node MUST NOT request a block from a limited peer if that block is more than\n+2,304 blocks below the node's own best tip. A node SHOULD rely on limited\n+peers for blocks only while its best tip is within 1,152 blocks of the\n+network's best tip, leaving half of the window for chain reorganizations and\n+for differences between the two nodes' best tips.\n+\n+## Address Relay\n+\n+Nodes SHOULD relay addresses advertising `NODE_NETWORK_LIMITED` as they relay\n+other addresses.\n+\n+## Deployment\n+\n+This ZIP requires no network upgrade. Nodes ignore service flags they do not\n+recognize, so a node can set `NODE_NETWORK_LIMITED` as soon as it implements\n+this ZIP.\n+\n+\n+# Rationale\n+\n+Bit 10 and the 2,304-block window match the Version 2 P2P protocol draft\n+[^draft-p2p-v2], so a node can advertise the same capability in both\n+protocols. Bit 10 is also the bit that BIP 159 assigns to the same capability\n+in Bitcoin.\n+\n+The window is defined in blocks rather than time, so that a peer can check a\n+request against it without knowing the block target spacing. At 75, 25 and 10\n+seconds per block it covers about 48, 16 and 6.4 hours.\n+\n+The 1,152-block margin for requesters, and the rule against serving older\n+blocks, follow BIP 159's 144-block reorganization buffer and its\n+fingerprinting countermeasure. BIP 159 does not specify disconnection; the\n+2-block margin follows Bitcoin Core's implementation [^bitcoin-core-limited].\n+\n+\n+# Security and Privacy Considerations\n+\n+A limited peer that served every block it retains would reveal its retention\n+depth, which helps distinguish it from other nodes. Serving only the window\n+avoids this.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^zip-0204]: [ZIP 204: Zcash P2P Network Protocol](zip-0204.rst)\n+\n+[^bip-0159]: [BIP 159: NODE_NETWORK_LIMITED service bit](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki)\n+\n+[^draft-p2p-v2]: [Draft ZIP: Version 2 Zcash P2P Network Protocol](draft-arya-jvff-p2p-quic-transport.md)\n+\n+[^bitcoin-core-limited]: [Bitcoin Core: block requests below the NODE_NETWORK_LIMITED threshold (`net_processing.cpp`)](https://github.com/bitcoin/bitcoin/blob/master/src/net_processing.cpp)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-marek-node-network-limited.md","title":"Limited Block Serving Service Flag","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"service-flag","text":"Service Flag"},{"level":2,"id":"serving","text":"Serving"},{"level":2,"id":"requesting","text":"Requesting"},{"level":2,"id":"address-relay","text":"Address Relay"},{"level":2,"id":"deployment","text":"Deployment"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":895,"title":"[ZIP 312] Specify key generation, change randomizer handling","author":"conradoplg","createdAt":"2024-08-13T22:52:46Z","updatedAt":"2026-11-04T14:56:03Z","url":"https://github.com/zcash/zips/pull/895","diffUrl":"https://github.com/zcash/zips/pull/895.diff","headRef":"frost-randomizer","headRepo":"zcash/zips","headSha":"be04583c304d90ee210b99fa9cd394d56439c64e","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Changes to randomizer handling after I realized that it couldn't possibly be generated using the message as an input. It does simplify things too. Some additional fixes (using \"Re-Randomized FROST\" consistently; updating Owners) fixes #1051","zipRefs":[0,312],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"static/css/style.css","status":"modified","additions":1,"deletions":1,"patch":"@@ -204,7 +204,7 @@ pre:has(.mermaid) {\n aside {\n   display: flex;\n   flex-direction: column;\n-  gap: 1em;\n+  gap: 0;\n   padding-top: 0.15rem;\n   padding-left: 0.15rem;\n   padding-right: 0.15rem;"},{"filename":"zips/zip-0000.rst","status":"modified","additions":7,"deletions":6,"patch":"@@ -653,17 +653,13 @@ If there are multiple Owners, each should be on a separate line.\n \n Credits: and Original-Authors: fields SHOULD NOT include email addresses.\n \n-The \"Owners\", \"Credits\", and \"Original-Authors\" headers always use\n-these plural spellings even there is only one Owner, one person to be\n-credited, or one original author.\n-\n While a ZIP is in public discussions (usually during the initial Draft\n phase), a Discussions-To header will indicate the URL where the ZIP is\n being discussed. No Discussions-To header is necessary if the ZIP is being\n discussed privately with the Owner.\n \n-The Pull-Request header, if present, gives an URL to a Pull Request for\n-the ZIP.\n+The Pull-Request header, if present, gives one or more URLs to\n+Pull Requests for the ZIP.\n \n The Category header specifies the type of ZIP, as described in\n `ZIP categories`_. Multiple categories MAY be specified, separated by\n@@ -675,6 +671,11 @@ Dates should be in yyyy-mm-dd format, e.g. 2001-08-14.\n For ZIPs written in reStructuredText, URLs in header fields SHOULD be\n surrounded by ``<`` ``>``; this ensures that the link is rendered correctly.\n \n+The \"Owners\", \"Credits\", and \"Original-Authors\" headers always use\n+these plural spellings even if there is only one Owner, one person to\n+be credited, or one original author. The \"Pull-Request\" header always\n+uses this singular spelling even there are multiple Pull Requests.\n+\n Auxiliary Files\n ^^^^^^^^^^^^^^^\n "},{"filename":"zips/zip-0312.rst","status":"modified","additions":398,"deletions":196,"patch":"@@ -3,24 +3,29 @@\n   ZIP: 312\n   Title: FROST for Spend Authorization Multisignatures\n   Owners: Conrado Gouvea <conrado@zfnd.org>\n-          Chelsea Komlo <ckomlo@uwaterloo.ca>\n-          Deirdre Connolly <deirdre@zfnd.org>\n+          Daira-Emma Hopwood <daira-emma@electriccoin.co>\n+  Original-Authors: Chelsea Komlo\n+                    Deirdre Connolly\n   Status: Draft\n   Category: Wallet\n-  Created: 2022-08-dd\n+  Created: 2022-08-01\n   License: MIT\n   Discussions-To: <https://github.com/zcash/zips/issues/382>\n   Pull-Request: <https://github.com/zcash/zips/pull/662>\n+                <https://github.com/zcash/zips/pull/895>\n \n \n Terminology\n ===========\n \n-{Edit this to reflect the key words that are actually used.}\n-The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to\n-be interpreted as described in RFC 2119. [#RFC2119]_\n+The key words \"MUST\", \"SHOULD\", \"RECOMMENDED\", \"NOT RECOMMENDED\", and \"MAY\" in\n+this document are to be interpreted as described in BCP 14 [#BCP14]_ when, and\n+only when, they appear in all capitals.\n \n-The terms below are to be interpreted as follows:\n+The term \"Recovery Protocol\" in this document is to be interpreted as described\n+in [#zip-2005]_.\n+\n+The term below is to be interpreted as follows:\n \n Unlinkability\n   The property of statistical independence of signatures from the\n@@ -33,28 +38,29 @@ Unlinkability\n Abstract\n ========\n \n-This proposal adapts FROST [#FROST]_, a threshold signature scheme,\n-to make it unlinkable, which is a requirement for its use in the Zcash protocol.\n-The adapted scheme generates signatures compatible with spend authorization\n-signatures in the Sapling and Orchard shielded protocols as deployed in Zcash.\n+This proposal adapts FROST [#FROST]_, a threshold signature scheme, to make it\n+support key re-randomization as described in [#protocol-abstractsigrerand]_.\n+This is a requirement in order for it to be used to implement unlinkable spend\n+authorization signatures in the Orchard and Sapling shielded protocols, as\n+deployed in Zcash.\n \n \n Motivation\n ==========\n \n In the Zcash protocol, Spend Authorization Signatures are employed to authorize\n-a transaction. The ability to generate these signatures with the user's\n-private key is what effectively allows the user to spend funds.\n+a transaction. The ability to generate these signatures with the user's private\n+key is what effectively allows the user to spend funds.\n \n This is a security-critical step, since anyone who obtains access to the private\n key will be able to spend the user's funds. For this reason, one interesting\n-possibility is to require multiple parties to allow the transaction to go\n-through. This can be accomplished with threshold signatures, where the private\n-key is split between parties (or generated already split using a distributed\n-protocol) in a way that a threshold (e.g. 2 out of 3) of them must sign the\n-transaction in order to create the final signature. This enables scenarios such\n-as users and third-party services sharing custody of a wallet, or a group of\n-people managing shared funds, for example.\n+possibility is to ensure that authorization from multiple parties is needed in\n+order for the transaction to go through. This can be accomplished with threshold\n+signatures, where the private key is split between parties (or generated already\n+split using a distributed protocol) in such a way that a threshold (e.g. 2 out\n+of 3) of them must sign the transaction in order to create the final signature.\n+This enables scenarios such as users and third-party services sharing custody of\n+a wallet, or a group of people managing shared funds, for example.\n \n FROST is one of such threshold signature protocols. However, it can't be used\n as-is since the Zcash protocol also requires re-randomizing public and private\n@@ -73,6 +79,8 @@ Requirements\n   criteria for Signature with Re-Randomizable Keys as specified in the Zcash\n   protocol [#protocol-concretereddsa]_.\n - The threat model described below must be taken into account.\n+- The key generation must be compatible with Quantum Recoverability\n+  [#zip-2005]_.\n \n Threat Model\n ------------\n@@ -87,9 +95,9 @@ shielded transaction:\n   as an auxiliary (secret) input, among others.\n \n When employing re-randomizable FROST as specified in this ZIP, the goal is to\n-split the spend authorization private key $\\mathsf{ask}$ among multiple\n-possible signers. This means that the proof generation will still be performed\n-by a single participant, likely the one that created the transaction in the first\n+split the spend authorization private key $\\mathsf{ask}$ among multiple possible\n+signers. This means that the proof generation will still be performed by a\n+single participant, likely the one that created the transaction in the first\n place. Note that this user already controls the privacy of the transaction since\n they are responsible for creating the proof.\n \n@@ -101,21 +109,26 @@ With those considerations in mind, the threat model considered in this ZIP is:\n \n - The Coordinator is trusted with the privacy of the transaction (which includes\n   the unlinkability property). A rogue Coordinator will be able to break\n-  unlinkability and privacy, but should not be able to create signed transactions\n-  without the approval of ``MIN_PARTICIPANTS`` participants, as specified in FROST.\n+  unlinkability and privacy, but should not be able to create signed\n+  transactions without the approval of ``MIN_PARTICIPANTS`` participants, as\n+  specified in FROST.\n - All key share holders are also trusted with the privacy of the transaction,\n-  thus a rogue key share holder will be able to break its privacy and unlinkability.\n+  thus a rogue key share holder will be able to break its privacy and\n+  unlinkability.\n+\n+Caveats concerning the security model for use of FROST keys in a Quantum\n+Recovery protocol are covered in [#zip-2005-usage-with-frost]_.\n \n \n Non-requirements\n ================\n \n - This ZIP does not support removing the Coordinator role, as described in\n   [#frost-removingcoordinator]_.\n-- This ZIP does not prevent key share holders from linking the signing operation to a\n-  transaction in the blockchain.\n-- Like the FROST specification [#FROST]_, this ZIP does not specify a key generation\n-  procedure; but refer to that specification for guidelines.\n+- This ZIP does not prevent key share holders from linking the signing operation\n+  to a transaction in the blockchain.\n+- Like the FROST specification [#FROST]_, this ZIP does not require a particular\n+  key generation procedure, although it does suggest one.\n - Network privacy is not in scope for this ZIP, and must be obtained with other\n   tools if desired.\n \n@@ -126,60 +139,161 @@ Specification\n Algorithms in this section are specified using Python pseudo-code, in the same\n fashion as the FROST specification [#FROST]_.\n \n-The types Scalar, Element, and G are defined in [#frost-primeordergroup]_, as\n-well as the notation for elliptic-curve arithmetic, which uses the additive\n-notation. Note that this notation differs from that used in the Zcash Protocol\n-Specification. For example, ``G.ScalarMult(P, k)`` is used for scalar\n-multiplication, where the protocol spec would use $[k] P$ with the group\n-implied by $P$.\n-\n-An additional per-ciphersuite hash function is used, denote ``HR(m)``, which\n-receives an arbitrary-sized byte string and returns a Scalar. It is defined\n-concretely in the Ciphersuites section.\n+We define the following as in [#FROST]_ for a specific FROST ciphersuite:\n+\n+* Let $G$ be a group of prime order $p$ as described in [#frost-primeordergroup]_,\n+  with element type $\\mathtt{Element}$, identity $I \\;{\\small ⦂}\\; \\mathtt{Element}$,\n+  base element $B \\;{\\small ⦂}\\; \\mathtt{Element}$ where $B \\neq I$, and scalar type\n+  $\\mathtt{Scalar} = \\mathbb{F}_p$.\n+* Let $\\mathtt{Ne}$ and $\\mathtt{Ns}$ be the sizes in bytes of a serialized Element\n+  representation and Scalar representation, respectively.\n+* Let functions $\\mathsf{Scalar} \\;{\\small ⦂}\\; \\{ 0 .. p-1 \\} \\rightarrow \\mathtt{Scalar}$,\n+  $\\mathsf{RandomScalar} \\;{\\small ⦂}\\; () \\stackrel{\\tiny\\mathrm{R}}{\\rightarrow} \\mathtt{Scalar}$,\n+  $\\mathsf{ScalarMult} \\;{\\small ⦂}\\; \\mathtt{Element} \\times \\mathtt{Scalar} \\rightarrow \\mathtt{Element}$,\n+  $\\mathsf{ScalarBaseMult} \\;{\\small ⦂}\\; \\mathtt{Scalar} \\rightarrow \\mathtt{Element}$,\n+  $\\mathsf{SerializeElement} \\;{\\small ⦂}\\; \\mathtt{Element} \\rightarrow \\mathbb{B}^{{\\kern-0.05em\\tiny\\mathbb{Y}}[\\mathtt{Ne}]}$,\n+  $\\mathsf{DeserializeElement} \\;{\\small ⦂}\\; \\mathbb{B}^{{\\kern-0.05em\\tiny\\mathbb{Y}}[\\mathtt{Ne}]} \\rightarrow \\mathtt{Element} \\cup \\{ \\bot \\}$,\n+  $\\mathsf{SerializeScalar} \\;{\\small ⦂}\\; \\mathtt{Scalar} \\rightarrow \\mathbb{B}^{{\\kern-0.05em\\tiny\\mathbb{Y}}[\\mathtt{Ns}]}$,\n+  $\\mathsf{DeserializeScalar} \\;{\\small ⦂}\\; \\mathbb{B}^{{\\kern-0.05em\\tiny\\mathbb{Y}}[\\mathtt{Ns}]} \\rightarrow \\mathtt{Scalar} \\cup \\{ \\bot \\}$\n+  also be as described in [#frost-primeordergroup]_ (where \"raising an error\"\n+  corresponds to returning $\\bot$).\n+\n+Note that the notation for elliptic-curve scalar multiplication,\n+$\\mathsf{G.ScalarMult}(P, k)$, differs from that used in the Zcash Protocol\n+Specification, which would use $[k]\\, P$ with the group implied by $P$\n+[#protocol-abstractgroup]_.\n \n \n Key Generation\n --------------\n \n-While key generation is out of scope for this ZIP and the FROST spec [#FROST]_,\n-it needs to be consistent with FROST, see [#frost-tdkg]_ for guidance. The spend\n-authorization private key $\\mathsf{ask}$ [#protocol-spendauthsig]_ is the\n-particular key that must be used in the context of this ZIP. Note that the\n-$\\mathsf{ask}$ is usually derived from the spending key\n-$\\mathsf{sk}$, though that is not required. Not doing so allows using\n-distributed key generation, since the key it generates is unpredictable. Note\n-however that not deriving $\\mathsf{ask}$ from $\\mathsf{sk}$ prevents\n-using seed phrases to recover the original secret (which may be something\n-desirable in the context of FROST).\n+FROST key generation with a trusted dealer is described in [#frost-tdkg]_. That\n+description does not support key re-randomization but can be adapted to do so,\n+as described in section 5 of [#frost-rerandomized]_.\n+\n+.. warning::\n+    Trusted dealer key generation has the severe disadvantage that it requires\n+    absolutely trustworthy hardware and software to be used by the dealer in\n+    order to reliably avoid exposing the private key material. That is, it is\n+    not sufficient simply to have an individual or organization that is trusted\n+    by all participants; their entire hardware and software stack also needs to\n+    be trusted by all participants.\n+\n+    Since this will often be an unrealistic assumption that partly defeats the\n+    point of using a threshold signature scheme, trusted dealer key generation\n+    SHOULD NOT be used.\n+\n+Instead, it is RECOMMENDED to instead use Distributed Key Generation.\n+\n+A suitable DKG protocol is COCKTAIL-DKG [#cocktail-dkg]_.\n+COCKTAIL-DKG assumes that each participant has a static public key known to all\n+other participants.\n+\n+Other Distributed Key Generation protocols MAY be used.\n+\n+To define a spending or viewing key that uses FROST, the Orchard and Sapling key\n+trees [#protocol-orchardkeycomponents]_ [#protocol-saplingkeycomponents]_ are\n+adjusted as follows:\n+\n+- The Spend validating key $\\mathsf{ak}$ is replaced by the FROST group\n+  public key `PK` [#frost-protocol]_ when deriving other key components.\n+- The Spend authorizing key $\\mathsf{ask}$ [#protocol-spendauthsig]_ is\n+  replaced by the logical signing key that corresponds to the group public key\n+  `PK`. By design, this key is never explicitly constructed (unless trusted\n+  dealer key generation was used), and instead is represented by any sufficient\n+  subset of the participant FROST signing key shares `sk_i`.\n+\n+For both Orchard and Sapling, use of Distributed Key Generation precludes\n+generating $\\mathsf{ask}$ from a spending key $\\mathsf{sk}$. Instead the\n+Distributed Key Generation protocol will output the FROST group public key `PK`\n+(equivalent to $\\mathsf{ak}$).\n+\n+When generating FROST key components for the Orchard shielded protocol, the\n+remaining parts of the Orchard key tree apart from $\\mathsf{ask}$ and\n+$\\mathsf{ak}$ SHOULD be generated as described in [#zip-2005-usage-with-frost]_.\n+The specification there is normative, and is only summarized here. It requires\n+the participants to privately agree on a value $\\mathsf{sk}$ (which is no longer\n+sufficient by itself for spend authorization), and then use it to obtain the\n+nullifier deriving key $\\mathsf{nk}$, the quantum spending key $\\mathsf{qsk}$,\n+the quantum intermediate key $\\mathsf{qk}$, and (using the FROST group public\n+key in place of $\\mathsf{ak}$) $\\mathsf{rivk\\_ext}$. This process is specified\n+in § 4.2.3 ‘Orchard Key Components’ [#protocol-orchardkeycomponents]_ as\n+modified by [#zip-2005-usage-with-frost]_.\n+\n+Similarly, when generating a FROST key for the Sapling shielded protocol, the\n+remaining parts of the Sapling key tree apart from $\\mathsf{ask}$ and\n+$\\mathsf{ak}$ (that is, $\\mathsf{nk}$, $\\mathsf{rivk\\_ext}$, and further values\n+derived from them and $\\mathsf{ak}$) SHOULD be generated from a common value\n+$\\mathsf{sk}$ (again, not sufficient by itself for spend authorization) as\n+described in [#protocol-saplingkeycomponents]_.\n+\n+Regardless of how the Orchard or Sapling key components are generated, the\n+participants MUST ensure that they have obtained the same values for\n+$\\mathsf{ak}$, $\\mathsf{nk}$, and $\\mathsf{rivk\\_ext}$.\n+\n+Since Sapling will not be modified to support Quantum Recoverability\n+[#zip-2005]_, Orchard is RECOMMENDED for new FROST keys.\n+\n+In order for all participants to agree on the value of $\\mathsf{sk}$, one\n+of the following options SHOULD be used:\n+\n+- Participants generate a $\\mathsf{sk}$ in a contributory manner, for example\n+  using the method defined in `Contributory Generation of sk`_. This approach\n+  SHOULD be used to generate $\\mathsf{sk}$ if Distributed Key Generation was\n+  employed.\n+- One participant generates a random $\\mathsf{sk}$ and sends it to the\n+  others via an encrypted and authenticated channel. This option SHOULD be used\n+  if trusted dealer key generation was used, and the $\\mathsf{sk}$ value\n+  can be sent along with the FROST shares during key generation. This option is\n+  NOT RECOMMENDED if Distributed Key Generation was employed.\n \n \n-Re-randomizable FROST\n+Randomizer Generation\n ---------------------\n \n-To add re-randomization to FROST, follow the specification [#FROST]_ with the\n-following modifications.\n+Re-Randomized FROST uses randomizers. This section specifies how they are\n+generated; this will be required for the Signature Share Generation\n+specification below.\n \n+Two functions are provided to generate randomizers: `randomizer_generate()` and\n+`randomizer_regenerate()`. Both use helper functions and a constant which are\n+defined as follows:\n \n-Randomizer Generation\n-'''''''''''''''''''''\n+- The `encode_group_commitment_list()` function is defined in\n+  [#frost-listoperations]_. It returns a byte serialization of a\n+  `commitment_list` value.\n+- The `random_bytes(n)` function is defined in [#frost-conventions]_ and it\n+  returns a sequence of `n` bytes sampled uniformly at random.\n+- The `HR(m)` function is a ciphersuite-generic function that maps an arbitrary\n+  byte string to a scalar and is instantiated in the `Ciphersuites`_ section.\n+\n+::\n+\n+  randomizer_generate():\n \n-A new helper function is defined, which generates a randomizer. The\n-`encode_signing_package` is defined as the byte serialization of the `msg`,\n-`commitment_list` values as described in [#frost-serialization]_.\n-Implementations MAY choose another encoding as long as all values (the message,\n-and the identifier, binding nonce and hiding nonce for each participant) are\n-unambiguously encoded.\n+  Inputs:\n+  - commitment_list = [(i, hiding_nonce_commitment_i,\n+    binding_nonce_commitment_i), ...], a list of commitments issued by\n+    each participant, where each element in the list indicates a\n+    NonZeroScalar identifier i and two commitment Element values\n+    (hiding_nonce_commitment_i, binding_nonce_commitment_i). This list\n+    MUST be sorted in ascending order by identifier.\n \n-The function `random_bytes(n)` is defined in [#FROST]_ and it returns a buffer\n-with `n` bytes sampled uniformly at random. The constant `Ns` is also specified\n-in [#FROST]_ and is the size of a serialized scalar.\n+  Outputs: (randomizer_seed, randomizer), a byte sequence and a Scalar\n+\n+  def randomizer_generate(commitment_list):\n+    # Generate a random byte sequence with the size of a serialized scalar.\n+    randomizer_seed = random_bytes(Ns)\n+    signing_commitments_enc = encode_group_commitment_list(commitment_list)\n+    randomizer_input = randomizer_seed || signing_commitments_enc\n+    return (randomizer_seed, HR(randomizer_input))\n \n ::\n \n-  randomizer_generate():\n+  randomizer_regenerate():\n \n   Inputs:\n-  - msg, the message being signed in the current FROST signing run\n+  - randomizer_seed = a sequence of Ns bytes\n   - commitment_list = [(i, hiding_nonce_commitment_i,\n     binding_nonce_commitment_i), ...], a list of commitments issued by\n     each participant, where each element in the list indicates a\n@@ -189,68 +303,127 @@ in [#FROST]_ and is the size of a serialized scalar.\n \n   Outputs: randomizer, a Scalar\n \n-  def randomizer_generate(msg, commitment_list):\n-    # Generate a random byte buffer with the size of a serialized scalar\n-    rng_randomizer = random_bytes(Ns)\n-    signing_package_enc = encode_signing_package(commitment_list, msg)\n-    randomizer_input = rng_randomizer || signing_package_enc\n-    return HR(randomizer_input)\n+  def randomizer_regenerate(randomizer_seed, commitment_list):\n+    signing_commitments_enc = encode_group_commitment_list(commitment_list)\n+    randomizer_input = randomizer_seed || signing_commitments_enc\n+    return H2(randomizer_input)\n+\n+\n+Re-Randomized FROST\n+-------------------\n+\n+To add re-randomization to FROST, the signers MUST follow the specification\n+[#FROST]_ with the following modifications.\n \n \n Round One - Commitment\n ''''''''''''''''''''''\n \n-Roune One is exactly the same as specified [#FROST]_. But for context, it\n+Round One is exactly the same as specified [#FROST]_. But for context, it\n involves these steps:\n \n-- Each signer generates nonces and their corresponding public commitments.\n-  A nonce is a pair of Scalar values, and a commitment is a pair of Element values.\n-- The nonces are stored locally by the signer and kept private for use in the second round.\n+- Each signer generates nonces and their corresponding public commitments. A\n+  nonce is a pair of $\\mathtt{Scalar}$ values, and a commitment is a pair of\n+  $\\mathtt{Element}$ values.\n+- The nonces are stored locally by the signer and kept private for use in the\n+  second round.\n - The commitments are sent to the Coordinator.\n \n \n Round Two - Signature Share Generation\n ''''''''''''''''''''''''''''''''''''''\n \n-In Round Two, the Coordinator generates a random scalar ``randomizer`` by calling\n-``randomizer_generate`` and sends it to each signer, over a confidential and\n-authenticated channel, along with the message and the set of signing\n-commitments. (Note that this differs from regular FROST which just requires an\n-authenticated channel.)\n+In Round Two of regular FROST, the Coordinator picks a message and sends it to\n+each signer along with the commitments received in Round One. In Re-Randomized\n+FROST, the Coordinator MUST additionally call $\\mathtt{randomizer\\_generate}()$\n+and send the $\\mathtt{randomizer\\_seed}$ along with the message and the\n+commitments. Each participant, upon receiving those values, MUST compute\n+$\\mathtt{randomizer}$ by calling $\\mathtt{randomizer\\_regenerate}()$ with the\n+received $\\mathtt{randomizer\\_seed}$ and commitments.\n+\n+In Zcash, the message that needs to be signed is the SIGHASH transaction hash.\n+This by itself does not convey enough information for the signers to decide\n+whether they want to authorize the transaction or not. Therefore, in addition\n+to the serialized transaction, more data (such as openings of value commitments,\n+decryption of note ciphertexts, etc.) needs to be sent from the Coordinator to\n+the signers. This SHOULD be done by sending a Partially Constructed Zcash Transaction\n+[#draft-zip-0374]_ over the same encrypted, authenticated channel. In order to sign,\n+each signer MUST be satisfied that the transaction should be authorized, using\n+the additional information provided in the PCZT where necessary. That information\n+MUST be checked for consistency with the transaction and not trusted simply\n+because the Coordinator is providing it.\n+\n+The details of this checking, and of what needs to be included in the PCZT in\n+order for the signers to be satisfied that the transaction should be authorized,\n+are to be specified in [#draft-zip-0374]_.\n+\n+Let $\\textsf{FROST.sign}$ be the regular FROST signing function with arguments\n+``(identifier, sk_i, group_public_key, nonce_i, msg, commitment_list)``,\n+as defined in [#frost-sign]_.\n+\n+The $\\textsf{Rerandomized-FROST.sign}$ function takes an additional input\n+$\\mathtt{randomizer\\_seed}$, and adjusts the inputs relative to the\n+$\\mathtt{randomizer}$ computed by $\\mathtt{randomizer\\_regenerate}()$::\n+\n+  def Rerandomized-FROST.sign(identifier, sk_i, group_public_key, nonce_i, msg,\n+                              commitment_list, randomizer_seed):\n+    randomizer = randomizer_regenerate(randomizer_seed, commitment_list)\n+    randomized_sk_i = sk_i + randomizer\n+    randomized_group_public_key = group_public_key + G.ScalarBaseMult(randomizer)\n+    return FROST.sign(identifier, randomized_sk_i, randomized_group_public_key,\n+                      nonce_i, msg, commitment_list)\n+\n+The signer MAY cache $\\mathtt{randomizer}$ and\n+$\\mathsf{G.ScalarBaseMult}(\\mathtt{randomizer})$ for use in the\n+$\\textsf{Rerandomized-FROST.}$ {aggregate, verify_signature_share}\n+functions defined below.\n \n-In Zcash, the message that needs to be signed is actually the SIGHASH\n-transaction hash, which does not convey enough information for the signers to\n-decide if they want to authorize the transaction or not. Therefore, in practice,\n-more data is needed to be sent (over the same encrypted, authenticated channel)\n-from the Coordinator to the signers, possibly the transaction itself, openings of\n-value commitments, decryption of note ciphertexts, etc.; and the signers MUST check\n-that the given SIGHASH matches the data sent from the Coordinator, or compute the\n-SIGHASH themselves from that data. However, the specific mechanism for that process\n-is outside the scope of this ZIP.\n \n-The randomized ``sign`` function is defined as the regular FROST ``sign``\n-function, but with its inputs modified relative to the ``randomizer`` as\n-following:\n+Signature Share Verification and Aggregation\n+''''''''''''''''''''''''''''''''''''''''''''\n \n-- ``sk_i = sk_i + randomizer``\n-- ``group_public_key = group_public_key + G.ScalarBaseMult(randomizer)``\n+Let $\\textsf{FROST.aggregate}$ be the regular FROST aggregation function with\n+arguments ``(commitment_list, msg, group_public_key, sig_shares)``, as defined\n+in [#frost-aggregate]_.\n \n+The $\\textsf{Rerandomized-FROST.aggregate}$ function takes an additional input\n+$\\mathtt{randomizer}$ computed as above, and adjusts the inputs relative to this\n+randomizer::\n \n-Signature Share Verification and Aggregation\n-''''''''''''''''''''''''''''''''''''''''''''\n+ def Rerandomized-FROST.aggregate(commitment_list, msg, group_public_key, sig_shares,\n+                                  randomizer):\n+   randomized_group_public_key = group_public_key + G.ScalarBaseMult(randomizer)\n+   return FROST.aggregate(commitment_list, msg, randomized_group_public_key, sig_shares)\n+\n+Similarly, let $\\textsf{FROST.verify\\_signature\\_share}$ be the regular FROST signature\n+share verification function with arguments\n+``(identifier, PK_i, comm_i, sig_share_i, commitment_list, group_public_key, msg)``,\n+as defined also in [#frost-aggregate]_.\n+\n+The $\\textsf{Rerandomized-FROST.verify\\_signature\\_share}$ function takes an\n+additional input $\\mathtt{randomizer}$ computed as above, and adjusts the inputs\n+relative to this randomizer::\n \n-The randomized ``aggregate`` function is defined as the regular FROST\n-``aggregate`` function, but with its inputs modified relative to the\n-``randomizer`` as following:\n+  def Rerandomized-FROST.verify_signature_share(identifier, PK_i, comm_i, sig_share_i,\n+                                                commitment_list, group_public_key, msg):\n+    group_offset = G.ScalarBaseMult(randomizer)\n+    randomized_PK_i = PK_i + group_offset\n+    randomized_group_public_key = group_public_key + group_offset\n+    return FROST.verify_signature_share(identifier, randomized_PK_i, comm_i, sig_share_i,\n+                                        commitment_list, randomized_group_public_key, msg)\n \n-- ``group_public_key = group_public_key + G.ScalarBaseMult(randomizer)``\n \n-The randomized ``verify_signature_share`` function is defined as the regular\n-FROST ``verify_signature_share`` function, but with its inputs modified relative\n-to the ``randomizer`` as following:\n+Contributory Generation of sk\n+-----------------------------\n \n-- ``PK_i = PK_i + G.ScalarBaseMult(randomizer)``\n-- ``group_public_key = group_public_key + G.ScalarBaseMult(randomizer)``\n+The contributory generation of $\\mathsf{sk}$ uses the application-defined\n+payloads feature of the COCKTAIL-DKG protocol [#cocktail-dkg]_, as follows:\n+\n+- In Round 1, generate ``sk_i = random_bytes(32)``\n+- Also in Round 1, set $\\mathsf{payload}_{i,j} = \\mathtt{sk\\_i}$ for all `j`\n+- When running Round 3, use the following as the ``extension`` argument:\n+  $ext = H(n \\parallel len(payload_1) \\parallel payload_1 \\parallel \\cdots \\parallel len(payload_n) \\parallel payload_n)$\n+- After Round 3 is finished, set $\\mathsf{sk} = ext$.\n \n \n Ciphersuites\n@@ -259,47 +432,51 @@ Ciphersuites\n FROST(Jubjub, BLAKE2b-512)\n ''''''''''''''''''''''''''\n \n-This ciphersuite uses Jubjub for the Group and BLAKE2b-512 for the Hash function ``H``\n-meant to produce signatures indistinguishable from RedJubjub Sapling Spend\n-Authorization Signatures as specified in [#protocol-concretespendauthsig]_.\n+This ciphersuite uses Jubjub for the Group and BLAKE2b-512 for the Hash function\n+$\\mathtt{H}$ meant to produce signatures indistinguishable from RedJubjub\n+Sapling Spend Authorization Signatures as specified in [#protocol-concretespendauthsig]_.\n \n - Group: Jubjub [#protocol-jubjub]_ with base point $\\mathcal{G}^{\\mathsf{Sapling}}$\n   as defined in [#protocol-concretespendauthsig]_.\n \n-  - Order: $r_\\mathbb{J}$ as defined in [#protocol-jubjub]_.\n+  - Order: $r_{\\mathbb{J}}$ as defined in [#protocol-jubjub]_.\n   - Identity: as defined in [#protocol-jubjub]_.\n-  - RandomScalar(): Implemented by returning a uniformly random Scalar in the range\n-    \\[0, ``G.Order()`` - 1\\]. Refer to {{frost-randomscalar}} for implementation guidance.\n-  - SerializeElement(P): Implemented as $\\mathsf{repr}_\\mathbb{J}(P)$ as defined in [#protocol-jubjub]_\n-  - DeserializeElement(P): Implemented as $\\mathsf{abst}_\\mathbb{J}(P)$ as defined in [#protocol-jubjub]_,\n-    returning an error if $\\bot$ is returned. Additionally, this function\n-    validates that the resulting element is not the group identity element,\n-    returning an error if the check fails.\n-  - SerializeScalar: Implemented by outputting the little-endian 32-byte encoding\n-    of the Scalar value.\n-  - DeserializeScalar: Implemented by attempting to deserialize a Scalar from a\n-    little-endian 32-byte string. This function can fail if the input does not\n-    represent a Scalar in the range \\[0, ``G.Order()`` - 1\\].\n-\n-- Hash (``H``): BLAKE2b-512 [#BLAKE]_ (BLAKE2b with 512-bit output and 16-byte personalization string),\n-  and Nh = 64.\n+  - $\\mathsf{RandomScalar}()$: Implemented by returning a uniformly random\n+    $\\mathtt{Scalar}$ in the range $[0, \\mathsf{G.Order}() - 1]$. Refer to\n+    [#frost-randomscalar]_ for implementation guidance.\n+  - $\\mathsf{SerializeElement}(P)$: Implemented as\n+    $\\mathsf{repr}_{\\mathbb{J}}(P)$ as defined in [#protocol-jubjub]_.\n+  - $\\mathsf{DeserializeElement}(P)$: Implemented as\n+    $\\mathsf{abst}_{\\mathbb{J}}(P)$ as defined in [#protocol-jubjub]_, returning\n+    an error if $\\bot$ is returned. Additionally, this function validates that\n+    the resulting element is not the group identity element, returning an error\n+    if the check fails.\n+  - $\\mathsf{SerializeScalar}$: Implemented by outputting the little-endian\n+    32-byte encoding of the $\\mathtt{Scalar}$ value.\n+  - $\\mathsf{DeserializeScalar}$: Implemented by attempting to deserialize a\n+    $\\mathtt{Scalar}$ from a little-endian 32-byte string. This function can\n+    fail if the input does not represent a $\\mathtt{Scalar}$ in the range\n+    $[0, \\mathsf{G.Order}() - 1]$.\n+\n+- Hash ($\\mathtt{H}$): BLAKE2b-512 [#BLAKE]_ (BLAKE2b with 512-bit output and\n+  16-byte personalization string), and $\\mathtt{Nh} = 64$.\n \n   - H1(m): Implemented by computing BLAKE2b-512(\"FROST_RedJubjubR\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n   - H2(m): Implemented by computing BLAKE2b-512(\"Zcash_RedJubjubH\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n     (This is equivalent to $\\mathsf{H}^\\circledast(m)$, as defined by\n     the $\\mathsf{RedJubjub}$ scheme instantiated in [#protocol-concretereddsa]_.)\n   - H3(m): Implemented by computing BLAKE2b-512(\"FROST_RedJubjubN\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n   - H4(m): Implemented by computing BLAKE2b-512(\"FROST_RedJubjubM\", m).\n   - H5(m): Implemented by computing BLAKE2b-512(\"FROST_RedJubjubC\", m).\n   - HR(m): Implemented by computing BLAKE2b-512(\"FROST_RedJubjubA\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n \n Signature verification is as specified in [#protocol-concretespendauthsig]_\n for RedJubjub.\n@@ -317,37 +494,41 @@ Authorization Signatures as specified in [#protocol-concretespendauthsig]_.\n \n   - Order: $r_\\mathbb{P}$ as defined in [#protocol-pallasandvesta]_.\n   - Identity: as defined in [#protocol-pallasandvesta]_.\n-  - RandomScalar(): Implemented by returning a uniformly random Scalar in the range\n-    \\[0, ``G.Order()`` - 1\\]. Refer to {{frost-randomscalar}} for implementation guidance.\n-  - SerializeElement(P): Implemented as $\\mathsf{repr}_\\mathbb{P}(P)$ as defined in [#protocol-pallasandvesta]_.\n-  - DeserializeElement(P): Implemented as $\\mathsf{abst}_\\mathbb{P}(P)$ as defined in [#protocol-pallasandvesta]_,\n-    failing if $\\bot$ is returned. Additionally, this function validates that the resulting\n-    element is not the group identity element, returning an error if the check fails.\n-  - SerializeScalar: Implemented by outputting the little-endian 32-byte encoding\n-    of the Scalar value.\n-  - DeserializeScalar: Implemented by attempting to deserialize a Scalar from a\n-    little-endian 32-byte string. This function can fail if the input does not\n-    represent a Scalar in the range \\[0, ``G.Order()`` - 1\\].\n-\n-- Hash (``H``): BLAKE2b-512 [#BLAKE]_ (BLAKE2b with 512-bit output and 16-byte personalization string),\n-  and Nh = 64.\n+  - $\\mathsf{RandomScalar}()$: Implemented by returning a uniformly random\n+    $\\mathtt{Scalar}$ in the range $[0, \\mathsf{G.Order}() - 1]$. Refer to\n+    [#frost-randomscalar]_ for implementation guidance.\n+  - $\\mathsf{SerializeElement}(P)$: Implemented as $\\mathsf{repr}_\\mathbb{P}(P)$\n+    as defined in [#protocol-pallasandvesta]_.\n+  - $\\mathsf{DeserializeElement}(P)$: Implemented as $\\mathsf{abst}_\\mathbb{P}(P)$\n+    as defined in [#protocol-pallasandvesta]_, failing if $\\bot$ is returned.\n+    Additionally, this function validates that the resulting element is not the\n+    group identity element, returning an error if the check fails.\n+  - $\\mathsf{SerializeScalar}$: Implemented by outputting the little-endian\n+    32-byte encoding of the $\\mathtt{Scalar}$ value.\n+  - $\\mathsf{DeserializeScalar}$: Implemented by attempting to deserialize a\n+    $\\mathtt{Scalar}$ from a little-endian 32-byte string. This function can\n+    fail if the input does not represent a $\\mathtt{Scalar}$ in the range\n+    $[0, \\mathsf{G.Order}() - 1]$.\n+\n+- Hash ($\\mathtt{H}$): BLAKE2b-512 [#BLAKE]_ (BLAKE2b with 512-bit output and\n+  16-byte personalization string), and $\\mathtt{Nh} = 64$.\n \n   - H1(m): Implemented by computing BLAKE2b-512(\"FROST_RedPallasR\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n   - H2(m): Implemented by computing BLAKE2b-512(\"Zcash_RedPallasH\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n     (This is equivalent to $\\mathsf{H}^\\circledast(m)$, as defined by\n     the $\\mathsf{RedPallas}$ scheme instantiated in [#protocol-concretereddsa]_.)\n   - H3(m): Implemented by computing BLAKE2b-512(\"FROST_RedPallasN\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n   - H4(m): Implemented by computing BLAKE2b-512(\"FROST_RedPallasM\", m).\n   - H5(m): Implemented by computing BLAKE2b-512(\"FROST_RedPallasC\", m).\n   - HR(m): Implemented by computing BLAKE2b-512(\"FROST_RedPallasA\", m), interpreting\n     the 64 bytes as a little-endian integer, and reducing the resulting integer\n-    modulo ``G.Order()``.\n+    modulo $\\mathsf{G.Order}()$.\n \n Signature verification is as specified in [#protocol-concretespendauthsig]_\n for RedPallas.\n@@ -364,25 +545,26 @@ practice this entails making sure that the generated signature can be verified\n by the $\\mathsf{RedDSA.Validate}$ function specified in\n [#protocol-concretereddsa]_:\n \n-- The FROST signature, when split into R and S in the first step of\n+- The FROST signature, when split into $R$ and $S$ in the first step of\n   $\\mathsf{RedDSA.Validate}$, must yield the values expected by the\n-  function. This is ensured by defining SerializeElement and SerializeScalar in\n-  each ciphersuite to yield those values.\n+  function. This is ensured by defining $\\mathsf{SerializeElement}$ and\n+  $\\mathsf{SerializeScalar}$ in each ciphersuite to yield those values.\n \n-- The challenge c used during FROST signing must be equal to the challenge c\n-  computed during $\\mathsf{RedDSA.Validate}$. This requires defining the\n+- The challenge $c$ used during FROST signing must be equal to the challenge\n+  $c$ computed during $\\mathsf{RedDSA.Validate}$. This requires defining the\n   ciphersuite H2 function as the $\\mathsf{H}^\\circledast(m)$ Zcash\n   function in the ciphersuites, and making sure its input will be the same.\n-  Fortunately FROST and Zcash use the same input order (R, public key, message)\n-  so we just need to make sure that SerializeElement (used to compute the\n-  encoded public key before passing to the hash function) matches what\n-  $\\mathsf{RedDSA.Validate}$ expects; which is possible since both `R` and\n-  `vk` (the public key) are encoded in the same way as in Zcash.\n-\n-- Note that ``r`` (and thus ``R``) will not be generated as specified in RedDSA.Sign.\n-  This is not an issue however, since with Schnorr signatures it does not matter\n-  for the verifier how the ``r`` value was chosen, it just needs to be generated\n-  uniformly at random, which is true for FROST.\n+  Fortunately FROST and Zcash use the same input order ($R$, public key, message)\n+  so we just need to make sure that $\\mathsf{SerializeElement}$ (used to compute\n+  the encoded public key before passing to the hash function) matches what\n+  $\\mathsf{RedDSA.Validate}$ expects; which is possible since both $R$ and\n+  $\\mathsf{vk}$ (the public key) are encoded in the same way as in Zcash.\n+\n+- Note that $r$ (and thus $R$) will not be generated as specified in\n+  $\\mathsf{RedDSA.Sign}$. This is not an issue however, since with Schnorr\n+  signatures it does not matter for the verifier how the $r$ value was chosen,\n+  it just needs to be a uniformly distributed random element, which is true for\n+  FROST.\n \n - The above will ensure that the verification equation in\n   $\\mathsf{RedDSA.Validate}$ will pass, since FROST ensures the exact same\n@@ -395,59 +577,79 @@ signing generates a re-randomized signature:\n   This is exactly what is done in the functions defined above.\n - The re-randomization must be done in each signature share generation, such\n   that the aggregated signature must be valid under verification with the\n-  randomized public key. The ``R`` value from the signature is not influenced by\n-  the randomizer so we just need to focus on the ``z`` value (using FROST\n-  notation). Recall that ``z`` must equal to ``r + (c * sk)``, and that each\n-  signature share is ``z_i = (hiding_nonce + (binding_nonce * binding_factor)) +\n-  (lambda_i * c * sk_i)``. The first terms are not influenced by the randomizer\n-  so we can only look into the second term of each top-level addition, i.e. ``c\n-  * sk`` must be equal to ``sum(lambda_i * c * sk_i)`` for each participant\n-  ``i``. Under re-randomization these become ``c * (sk + randomizer)`` (see\n-  $\\mathsf{RedDSA.RandomizedPrivate}$, which refers to the randomizer as\n-  $\\alpha$) and ``sum(lambda_i * c * (sk_i + randomizer))``. The latter\n-  can be rewritten as ``c * (sum(lambda_i * sk_i) + randomizer *\n-  sum(lambda_i)``. Since ``sum(lambda_i * sk_i) == sk`` per the Shamir secret\n-  sharing mechanism used by FROST, and since ``sum(lambda_i) == 1``\n-  [#sum-lambda-proof]_, we arrive at ``c * (sk + randomizer)`` as required.\n+  randomized public key. This is shown to be true in [#frost-rerandomized]_.\n \n - The re-randomization procedure must be exactly the same as in\n   [#protocol-concretereddsa]_ to ensure that re-randomized keys are uniformly\n   distributed and signatures are unlinkable. This is also true; observe that\n-  ``randomizer_generate`` generates randomizer uniformly at random as required\n-  by $\\mathsf{RedDSA.GenRandom}$; and signature generation is compatible\n-  with $\\mathsf{RedDSA.RandomizedPrivate}$,\n-  $\\mathsf{RedDSA.RandomizedPublic}$, $\\mathsf{RedDSA.Sign}$ and\n-  $\\mathsf{RedDSA.Validate}$ as explained in the previous item.\n+  $\\mathtt{randomizer\\_generate} generates randomizer uniformly at random as\n+  required by $\\mathsf{RedDSA.GenRandom}$; and signature generation is compatible\n+  with $\\mathsf{RedDSA.RandomizePrivate}$, $\\mathsf{RedDSA.RandomizePublic}$,\n+  $\\mathsf{RedDSA.Sign}$ and $\\mathsf{RedDSA.Validate}$ as explained in the\n+  previous item.\n \n The security of Re-Randomized FROST with respect to the security assumptions of\n regular FROST is shown in [#frost-rerandomized]_.\n \n+Regarding randomizer handling, in Zcash, the $\\mathtt{randomizer}$ is called\n+$\\alpha$ and is usually generated using the $\\mathsf{RedDSA.GenRandom}$ function\n+as defined in the Zcash specification [#protocol-spendauthsig]_. Note that the\n+choice of $\\alpha$ influences the SIGHASH computation, so it is impossible to\n+compute the $\\mathtt{randomizer}$ based on the message (SIGHASH), as suggested\n+in [#frost-rerandomized]_. This is not an issue as long as the\n+$\\mathtt{randomizer}$ is generated with the same security properties as\n+$\\mathsf{RedDSA.GenRandom}$. We ensure that by using a very similar approach;\n+while the original $\\mathsf{RedDSA.GenRandom}$ uses $\\mathsf{H}^\\circledast(T)$\n+where $T$ is a random byte sequence with a certain size, in this ZIP we\n+effectively use $\\mathsf{H}^\\circledast(T || \\mathsf{signing\\_commitments\\_enc})$,\n+i.e. we concatenate the random bytes with the encoding of the signing\n+commitments. This preserves the security assumptions and also hedges against\n+issues in the Coordinator random byte generator and prevents the Coordinator\n+from fully influencing the randomizer, reducing its trust assumptions.\n+\n \n Reference implementation\n ========================\n \n-The `reddsa` crate [#crate-reddsa]_ contains a re-randomized FROST implementation of\n-both ciphersuites.\n+The `reddsa` crate [#crate-reddsa]_ contains a re-randomized FROST implementation\n+of both ciphersuites.\n \n \n References\n ==========\n \n+.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n .. [#BLAKE] `BLAKE2: simpler, smaller, fast as MD5 <https://blake2.net/#sp>`_\n-.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_\n .. [#FROST] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures <https://www.rfc-editor.org/rfc/rfc9591.html>`_\n-.. [#frost-rerandomized] `Re-Randomized FROST <https://eprint.iacr.org/2024/436>`_\n-.. [#frost-protocol] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 5: Two-Round FROST Signing Protocol <https://www.rfc-editor.org/rfc/rfc9591.html#name-two-round-frost-signing-pro>`_\n-.. [#frost-removingcoordinator] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 7.3: Removing the Coordinator Role <https://www.rfc-editor.org/rfc/rfc9591.html#name-removing-the-coordinator-ro>`_\n+.. [#frost-rerandomized] `Re-Randomized FROST (ePrint 2024/436) <https://eprint.iacr.org/2024/436>`_\n+.. [#frost-serialization] `The ZF FROST Book, Serialization Format <https://frost.zfnd.org/user/serialization.html>`_\n+.. [#frost-term-broadcast-channel] `The ZF FROST Book, Terminology — Broadcast Channel <https://frost.zfnd.org/terminology.html#broadcast-channel>`_\n+.. [#frost-conventions] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 2: Conventions and Definitions <https://www.rfc-editor.org/rfc/rfc9591.html#name-conventions-and-definitions>`_\n .. [#frost-primeordergroup] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 3.1: Prime-Order Group <https://www.rfc-editor.org/rfc/rfc9591.html#name-prime-order-group>`_\n+.. [#frost-hash] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 3.2: Cryptographic Hash Function <https://www.rfc-editor.org/rfc/rfc9591.html#name-cryptographic-hash-function>`_\n+.. [#frost-listoperations] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 4.3: List Operations <https://www.rfc-editor.org/rfc/rfc9591.html#name-list-operations>`_\n+.. [#frost-protocol] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 5: Two-Round FROST Signing Protocol <https://www.rfc-editor.org/rfc/rfc9591.html#name-two-round-frost-signing-pro>`_\n+.. [#frost-sign] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 5.2: Round Two - Signature Share Generation <https://www.rfc-editor.org/rfc/rfc9591.html#name-round-two-signature-share-g>`_\n+.. [#frost-aggregate] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 5.3: Signature Share Aggregation <https://www.rfc-editor.org/rfc/rfc9591.html#name-signature-share-aggregation>`_\n+.. [#frost-removingcoordinator] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 7.5: Removing the Coordinator Role <https://www.rfc-editor.org/rfc/rfc9591.html#name-removing-the-coordinator-ro>`_\n .. [#frost-primeorderverify] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Appendix B: Schnorr Signature Generation and Verification for Prime-Order Groups <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-schnorr-signature-generatio>`_\n-.. [#frost-tdkg] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Appendix B: Trusted Dealer Key Generation <https://www.rfc-editor.org/rfc/rfc9591.html#name-trusted-dealer-key-generati>`_\n-.. [#frost-randomscalar] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Appendix C: Random Scalar Generation <https://www.rfc-editor.org/rfc/rfc9591.html#name-random-scalar-generation>`_\n-.. [#frost-serialization] `The ZF FROST Book, Serialization Format <https://frost.zfnd.org/user/serialization.html>`_\n-.. [#protocol-concretereddsa] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7: RedDSA, RedJubjub, and RedPallas <protocol/protocol.pdf#concretereddsa>`_\n-.. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7.1: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#concretespendauthsig>`_\n-.. [#protocol-spendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 4.15: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#spendauthsig>`_\n-.. [#protocol-jubjub] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.3: Jubjub <protocol/protocol.pdf#jubjub>`_\n-.. [#protocol-pallasandvesta] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.6: Pallas and Vesta <protocol/protocol.pdf#pallasandvesta>`_\n-.. [#crate-reddsa] `reddsa <https://github.com/ZcashFoundation/reddsa>`_\n+.. [#frost-tdkg] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Appendix C: Trusted Dealer Key Generation <https://www.rfc-editor.org/rfc/rfc9591.html#name-trusted-dealer-key-generati>`_\n+.. [#frost-randomscalar] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Appendix D: Random Scalar Generation <https://www.rfc-editor.org/rfc/rfc9591.html#name-random-scalar-generation>`_\n+.. [#protocol-addressesandkeys] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 3.1: Payment Addresses and Keys <protocol/protocol.pdf#addressesandkeys>`_\n+.. [#protocol-abstractsigrerand] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.1.7.1: Signature with Re-Randomizable Keys <protocol/protocol.pdf#abstractsigrerand>`_\n+.. [#protocol-abstractgroup] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.1.9: Represented Group <protocol/protocol.pdf#abstractgroup>`_\n+.. [#protocol-saplingkeycomponents] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.2.2: Sapling Key Components <protocol/protocol.pdf#saplingkeycomponents>`_\n+.. [#protocol-orchardkeycomponents] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.2.3: Orchard Key Components <protocol/protocol.pdf#orchardkeycomponents>`_\n+.. [#protocol-spendauthsig] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.15: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#spendauthsig>`_\n+.. [#protocol-concretereddsa] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.7: RedDSA, RedJubjub, and RedPallas <protocol/protocol.pdf#concretereddsa>`_\n+.. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.7.1: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#concretespendauthsig>`_\n+.. [#protocol-jubjub] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.9.3: Jubjub <protocol/protocol.pdf#jubjub>`_\n+.. [#protocol-pallasandvesta] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.9.6: Pallas and Vesta <protocol/protocol.pdf#pallasandvesta>`_\n+.. [#crate-reddsa] `reddsa Rust crate <https://github.com/ZcashFoundation/reddsa>`_\n .. [#sum-lambda-proof] `Prove that the sum of the Lagrange (interpolation) coefficients is equal to 1 <https://math.stackexchange.com/questions/1325292/prove-that-the-sum-of-the-lagrange-interpolation-coefficients-is-equal-to-1/1325342#1325342>`_\n+.. [#zf-frost-dkg] `FROST reference implementation (ZcashFoundation/frost) — Distributed Key Generation <https://github.com/ZcashFoundation/frost/blob/main/frost-core/src/keys/dkg.rs>`_\n+.. [#cocktail-dkg] `COCKTAIL-DKG: Distributed Key Generation protocol for FROST <https://c2sp.org/cocktail-dkg>`_ (v0.2.0 draft at [#cocktail-dkg-draft]_)\n+.. [#cocktail-dkg-draft] `C2SP Pull Request 194 — cocktail-dkg: add application-specific data to transcript hash <https://github.com/C2SP/C2SP/pull/194>`_\n+.. [#draft-zip-0374] `Draft ZIP 374 (zcash/zips PR 1063): Partially Created Zcash Transaction Format <https://github.com/zcash/zips/pull/1063>`_\n+.. [#zip-2005] `ZIP 2005: Quantum recoverability <zip-2005.md>`_\n+.. [#zip-2005-usage-with-frost] `ZIP 2005: Quantum recoverability - Usage with FROST <zip-2005.md#usage-with-frost>`_"},{"filename":"zips/zip-guide-markdown.md","status":"modified","additions":11,"deletions":10,"patch":"@@ -158,19 +158,20 @@ ZIP editors will catch any inconsistencies in review.\n \n <div class=\"note\"></div>\n \n-\"`.. note::`\" in reStructuredText or \"`<div class=\"note\"></div>`\" in Markdown\n-(followed by a blank line in either case), can be used for an aside from the main\n-text. The rendering of notes is colourful and may be distracting, so they should\n-only be used for important points.\n+\"`.. note::`\" (followed by a block indented 4 spaces) in reStructuredText, or\n+\"`<div class=\"note\"></div>`\" (with a blank line before the following paragraph)\n+in Markdown, can be used for an aside from the main text. The rendering of notes\n+is colourful and may be distracting, so they should only be used for important\n+points.\n \n <div class=\"warning\"></div>\n \n-\"`.. warning::`\" in reStructuredText or \"`<div class=\"warning\"></div>`\" in\n-Markdown (followed by a blank line in either case), can be used for warnings.\n-Warnings should be used very sparingly — for example to signal that a\n-entire specification, or part of it, may be inapplicable or could cause\n-significant interoperability or security problems. In most cases, a \"MUST\"\n-or \"SHOULD\" conformance requirement is more appropriate.\n+\"`.. warning::`\" (followed by a block indented 4 spaces) in reStructuredText, or\n+\"`<div class=\"warning\"></div>`\" (with a blank line before the following paragraph)\n+in Markdown, can be used for warnings. Warnings should be used very sparingly —\n+for example to signal that an entire specification, or part of it, may be\n+inapplicable or could cause significant interoperability or security problems.\n+In most cases, a \"MUST\" or \"SHOULD\" conformance requirement is more appropriate.\n \n In Markdown, notes and warnings can currently only be a single paragraph.\n "},{"filename":"zips/zip-guide.rst","status":"modified","additions":7,"deletions":6,"patch":"@@ -167,18 +167,19 @@ Notes and warnings\n ------------------\n \n .. note::\n-    \"``.. note::``\" in reStructuredText or \"``<div class=\"note\"></div>``\" in\n-    Markdown (followed by a blank line in either case), can be used for an aside\n-    from the main text.\n+    \"``.. note::``\" (followed by a block indented 4 spaces) in reStructuredText,\n+    or \"``<div class=\"note\"></div>``\" (with a blank line before the following\n+    paragraph) in Markdown, can be used for an aside from the main text.\n \n     The rendering of notes is colourful and may be distracting, so they should\n     only be used for important points.\n \n .. warning::\n-    \"``.. warning::``\" in reStructuredText, or \"``<div class=\"warning\"></div>``\"\n-    in Markdown (followed by a blank line in either case), can be used for warnings.\n+    \"``.. warning::``\" (followed by a block indented 4 spaces) in reStructuredText,\n+    or \"``<div class=\"warning\"></div>``\" (with a blank line before the following\n+    paragraph) in Markdown, can be used for warnings.\n \n-    Warnings should be used very sparingly — for example to signal that a\n+    Warnings should be used very sparingly — for example to signal that an\n     entire specification, or part of it, may be inapplicable or could cause\n     significant interoperability or security problems. In most cases, a \"MUST\"\n     or \"SHOULD\" conformance requirement is more appropriate."}],"filesTruncated":false,"preview":{"filename":"zips/zip-0000.rst","title":"ZIP Process","status":"Active","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"zip-workflow","text":"ZIP Workflow"},{"level":3,"id":"zip-numbering-conventions","text":"ZIP Numbering Conventions"},{"level":3,"id":"transferring-zip-ownership","text":"Transferring ZIP Ownership"},{"level":3,"id":"zip-editors","text":"ZIP Editors"},{"level":3,"id":"zip-editor-responsibilities","text":"ZIP Editor Responsibilities"},{"level":3,"id":"reviewing-a-zip","text":"Reviewing a ZIP"},{"level":3,"id":"rejecting-a-zip-or-update","text":"Rejecting a ZIP or update"},{"level":3,"id":"communicating-with-the-zip-editors","text":"Communicating with the ZIP Editors"},{"level":3,"id":"zip-editor-meeting-practices","text":"ZIP Editor Meeting Practices"},{"level":2,"id":"zip-format-and-structure","text":"ZIP format and structure"},{"level":3,"id":"zip-stubs","text":"ZIP stubs"},{"level":3,"id":"zip-header-preamble","text":"ZIP header preamble"},{"level":3,"id":"auxiliary-files","text":"Auxiliary Files"},{"level":2,"id":"zip-categories","text":"ZIP categories"},{"level":2,"id":"zip-status-field","text":"ZIP Status Field"},{"level":3,"id":"specification-of-status-workflow","text":"Specification of Status Workflow"},{"level":2,"id":"zip-comments","text":"ZIP Comments"},{"level":2,"id":"zip-licensing","text":"ZIP Licensing"},{"level":3,"id":"recommended-licenses","text":"Recommended licenses"},{"level":3,"id":"not-recommended-but-acceptable-licenses","text":"Not recommended, but acceptable licenses"},{"level":3,"id":"not-acceptable-licenses","text":"Not acceptable licenses"},{"level":3,"id":"rationale","text":"Rationale"},{"level":1,"id":"see-also","text":"See Also"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1432,"title":"Add draft ZIP: FROST Member Backup Strings for Shielded Zcash","author":"KHADEVS","createdAt":"2026-11-02T16:33:35Z","updatedAt":"2026-11-04T14:49:55Z","url":"https://github.com/zcash/zips/pull/1432","diffUrl":"https://github.com/zcash/zips/pull/1432.diff","headRef":"draft-khadim-aryaethn-frost-member-backup","headRepo":"KHADEVS/zips","headSha":"f20eefc453d50ece630ba4919ae250eb78c2ed05","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Adds a draft Wallet ZIP for backing up one member of a FROST group for shielded Zcash: a Bech32m string holding the member's identifier, the threshold, the group size, the signing share and sk, restored together with the group's unified full viewing key. Discussed in #1413. Version 1 is the `zkfrost","zipRefs":[2005],"draftRefs":["draft-khadim-aryaethn-frost-member-backup"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -233,6 +233,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-khadim-aryaethn-frost-member-backup</td> <td class=\"left\"><a href=\"zips/draft-khadim-aryaethn-frost-member-backup.md\">FROST Member Backup Strings for Shielded Zcash</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1413\">zips#1413</a></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-khadim-aryaethn-frost-member-backup.md","status":"added","additions":532,"deletions":0,"patch":"@@ -0,0 +1,532 @@\n+    ZIP: Unassigned\n+    Title: FROST Member Backup Strings for Shielded Zcash\n+    Owners: Khadim <openpgp-wallet@protonmail.com>\n+            aryaethn <aryaethn@gmail.com>\n+    Credits: Hanh (hhanh00), for the zkfrost string in Zkool\n+    Status: Draft\n+    Category: Wallet\n+    Created: 2026-10-26\n+    License: MIT\n+    Discussions-To: <https://github.com/zcash/zips/issues/1413>\n+    Pull-Request: <https://github.com/zcash/zips/pull/1432>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"SHOULD NOT\" in this document\n+are to be interpreted as described in BCP 14 [^BCP14] when, and only when,\n+they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as\n+defined in the Zcash protocol specification [^protocol-networks].\n+\n+The terms below are to be interpreted as follows:\n+\n+Group\n+\n+: A set of $n$ participants holding a FROST threshold key for Orchard-protocol\n+  spend authorization [^zip-0312], any $t$ of whom can sign.\n+\n+Member\n+\n+: One participant of a group, identified by its FROST identifier.\n+\n+Signing share\n+\n+: The member's secret share of the group's Spend authorizing key, as output\n+  by key generation.\n+\n+Group verifying key, $\\mathsf{ak}$\n+\n+: The FROST group public key, used as the Spend validating key\n+  $\\mathsf{ak}$ of the group's Orchard full viewing key. Its encoding has a\n+  $\\tilde{y}$ bit of $0$, as § 4.2.3 of the protocol specification requires\n+  [^protocol-updated-orchardkeycomponents].\n+\n+$\\mathsf{sk}$\n+\n+: The 32-byte value the members agree on during key generation, from which\n+  the rest of the group's key components are derived [^zip-2005-usagewithfrost].\n+\n+Derivation\n+\n+: How $\\mathsf{nk}$ and $\\mathsf{rivk}$ are obtained from $\\mathsf{sk}$ and\n+  $\\mathsf{ak}$. This ZIP knows two, defined in [Derivations].\n+\n+Member backup string\n+\n+: The string defined in this ZIP.\n+\n+\n+# Abstract\n+\n+This ZIP defines a backup string for one member of a FROST group that spends\n+shielded ZEC with Orchard-protocol keys (in the Orchard or Ironwood pool). The\n+string holds the member's identifier, the threshold, the group size, the\n+member's signing share and the group's $\\mathsf{sk}$. Together with the group's\n+unified full viewing key, it is enough to rebuild the member in any wallet that\n+implements this ZIP, without the other members being online. Restoring checks\n+the string against the viewing key, which both detects a string from another\n+group and tells the wallet which key derivation the group uses.\n+\n+Version 1 of the payload is byte-for-byte the `zkfrost` string that Zkool\n+already ships, so existing backups stay valid.\n+\n+The string is more sensitive than the signing share it carries: it also\n+carries $\\mathsf{sk}$, from which a holder can compute the group's quantum\n+spending key $\\mathsf{qsk}$ [^zip-2005]. It never appears on chain.\n+\n+\n+# Motivation\n+\n+Several wallets now offer FROST groups for shielded ZEC: Zkool, Zafe,\n+Konclave, and the Zcash Foundation's tools. Each one backs up its members in its\n+own format, so a member can only restore their seat in the app that created\n+it. In July 2026, Zkool's author described the situation: \"There is no\n+standard for it. I choose a way that makes it restorable on Zkool only. One\n+day, someone will write a ZIP.\" [^zkool-forum]\n+\n+Since then, Zkool has moved to a minimal backup string (`zkfrost`, Zkool\n+6.35.0) that holds only what a member needs. This ZIP takes that string as its\n+first version, so that:\n+\n+- a user can move a seat from one wallet to another, or restore it in a\n+  different app after losing a device;\n+- a group can mix implementations, which avoids having every signer come from\n+  one vendor;\n+- funds stay reachable if one app is abandoned.\n+\n+ZIP 2005 adds a reason. To recover notes with the Recovery Protocol, \"it is\n+necessary to retain, or be able to rederive, $\\mathsf{sk}$, and also to know\n+whether $\\mathsf{use\\_qsk}$ was used to derive the $\\mathsf{ivk}$ and\n+addresses\" [^protocol-updated-orchardkeycomponents]. A backup format is where\n+that knowledge has to live.\n+\n+\n+# Privacy Implications\n+\n+The string never leaves the member's custody in normal use and is not\n+published on chain, so this ZIP changes nothing for on-chain privacy.\n+\n+Combined with the group's unified full viewing key, a string gives no viewing\n+capability beyond what that viewing key already gives. Without the viewing\n+key, $\\mathsf{sk}$ yields $\\mathsf{nk}$ but not the incoming viewing key, which\n+also depends on $\\mathsf{ak}$. The string does reveal $t$, $n$ and the member's\n+identifier.\n+\n+A wallet that shows the string as a QR code exposes it to cameras and\n+onlookers, as with any secret displayed on screen.\n+\n+\n+# Requirements\n+\n+- A member can be rebuilt from its string and the group's unified full viewing\n+  key, with no other member online.\n+- A wallet detects a string that belongs to another group before using it.\n+- The restore tells which derivation the group uses.\n+- The format does not depend on how $\\mathsf{sk}$ was agreed.\n+- Backups made with Zkool's `zkfrost` string stay valid.\n+- The format can gain new versions without breaking existing strings.\n+\n+\n+# Non-requirements\n+\n+- Key generation, including how the members agree on $\\mathsf{sk}$\n+  [^zip-0312-key-generation].\n+- Signing, and the transport between members.\n+- Encryption of the string at rest (see [Open issues]).\n+- A birthday height. zips#1401 proposes to carry it in the unified full\n+  viewing key [^zips-1401], which the restore already needs.\n+- Splitting $\\mathsf{qsk}$ between members.\n+- FROST groups using Sapling keys.\n+\n+\n+# Specification\n+\n+## Derivations\n+\n+Let $\\mathsf{H^{ask}}$, $\\mathsf{H^{nk}}$, $\\mathsf{H^{rivk}}$,\n+$\\mathsf{H^{qsk}}$, $\\mathsf{H^{qk}}$ and\n+$\\mathsf{H}^{\\mathsf{rivk\\_ext}}_{\\mathsf{qk}}$ be as defined in § 4.2.3\n+of the protocol specification as updated by ZIP 2005\n+[^protocol-updated-orchardkeycomponents].\n+\n+ZIP 2005 derivation\n+\n+: $\\mathsf{nk} = \\mathsf{H^{nk}}(\\mathsf{sk})$, and\n+  $\\mathsf{rivk} = \\mathsf{H}^{\\mathsf{rivk\\_ext}}_{\\mathsf{qk}}(\\mathsf{ak}, \\mathsf{nk})$\n+  where $\\mathsf{qk} = \\mathsf{H^{qk}}(\\mathsf{H^{qsk}}(\\mathsf{sk}))$. This is\n+  the $\\mathsf{use\\_qsk} = \\mathsf{true}$ branch that ZIP 2005 requires for\n+  FROST [^zip-2005-usagewithfrost]. Notes received by such a group are\n+  quantum recoverable.\n+\n+Legacy derivation\n+\n+: $\\mathsf{nk} = \\mathsf{H^{nk}}(\\mathsf{sk})$ and\n+  $\\mathsf{rivk} = \\mathsf{H^{rivk}}(\\mathsf{sk})$, with the group verifying key\n+  as $\\mathsf{ak}$. Zkool created groups this way before supporting ZIP 2005.\n+  Such a group follows neither branch of § 4.2.3\n+  [^protocol-updated-orchardkeycomponents]: $\\mathsf{ak}$ is not derived from\n+  $\\mathsf{sk}$, and $\\mathsf{rivk}$ does not commit to a $\\mathsf{qk}$. Notes\n+  received by such a group are therefore not quantum recoverable.\n+\n+## Encoding\n+\n+A member backup string is the Bech32m [^bip-0350] encoding of a payload, with\n+the human-readable part `zkfrost`. As in ZIP 316 [^zip-0316], the length limit\n+of Bech32m is not applied; a version 1 string is 123 characters long.\n+\n+Encoders MUST produce lowercase strings. Decoders MUST accept a string that is\n+entirely lowercase or entirely uppercase, and MUST reject a string in mixed\n+case, as Bech32m requires. A wallet can still show the string in uppercase,\n+for instance in a QR code (see [Rationale]), by converting it after encoding.\n+\n+The human-readable part is the same on Mainnet and Testnet, and the payload\n+names no network: the network is given by the unified full viewing key used to\n+restore. A string paired with another group's viewing key, on either network,\n+fails the check in [Restore].\n+\n+## Payload, version 1\n+\n+| Offset | Size | Field |\n+|--------|------|-------|\n+| 0      | 1    | format version, $\\mathtt{0x01}$ |\n+| 1      | 1    | member identifier $i$ |\n+| 2      | 1    | threshold $t$ |\n+| 3      | 1    | group size $n$ |\n+| 4      | 32   | signing share |\n+| 36     | 32   | $\\mathsf{sk}$ |\n+\n+The member's FROST identifier is the Pallas scalar with integer value $i$.\n+The group size $n$ is the number of members.\n+\n+The signing share is the little-endian encoding of a Pallas scalar, as in the\n+FROST(Pallas, BLAKE2b-512) ciphersuite [^zip-0312]. It MUST be the share that\n+matches the $\\mathsf{ak}$ in the group's viewing key. Key generation can output\n+a group key with either $\\tilde{y}$ bit; when it is $1$, the group key and\n+every share are negated. The `reddsa` crate does this at the end of its key\n+generation, and frost-client does it after the DKG. An implementation that\n+skips this step would export a wrong share for about half of all groups.\n+\n+## Decoding\n+\n+A decoder MUST reject the string if any of the following holds:\n+\n+- it is not valid Bech32m, including a bad checksum, a Bech32 checksum, or\n+  mixed case;\n+- the bits left over when converting its data part to bytes are more than 4\n+  or not all zero, as ZIP 173 requires [^zip-0173]. A version 1 string leaves\n+  1 bit, so each payload has only one lowercase string;\n+- its human-readable part, compared case-insensitively, is not `zkfrost`;\n+- the format version is not $\\mathtt{0x01}$;\n+- the payload is not exactly 68 bytes long;\n+- $n = 0$, $i = 0$, $i > n$, $t < 2$, or $t > n$. FROST needs at least two\n+  signers.\n+\n+Checking the version before the length lets a decoder report a string of a\n+later version as unsupported rather than as corrupt.\n+\n+## Restore\n+\n+The inputs are a member backup string and the group's unified full viewing\n+key. The viewing key MUST contain an Orchard component (typecode\n+$\\mathtt{0x03}$ [^zip-0316]), which holds $\\mathsf{ak}$, $\\mathsf{nk}$ and\n+$\\mathsf{rivk}$. Ironwood uses the same Orchard-protocol keys.\n+\n+1. Decode the string as above.\n+2. Reject the string if $\\mathsf{sk}$ is not a valid Orchard spending key,\n+   that is if $\\mathsf{H^{ask}}(\\mathsf{sk}) = 0$, or if the signing share is\n+   not the canonical encoding of a Pallas scalar. (A group's $\\mathsf{ask}$ is\n+   not derived from $\\mathsf{sk}$; the first check only keeps decoders in\n+   agreement with the validity rule for Orchard spending keys.)\n+3. Take $\\mathsf{ak}$ from the viewing key. Compute $\\mathsf{nk}$ and\n+   $\\mathsf{rivk}$ under each derivation of [Derivations].\n+4. If the ZIP 2005 derivation gives the viewing key's $\\mathsf{nk}$ and\n+   $\\mathsf{rivk}$, the group follows the ZIP 2005 derivation. Otherwise, if\n+   the legacy derivation does, the group follows the legacy derivation.\n+   Otherwise the wallet MUST reject the string: it belongs to another group.\n+5. Rebuild the member's key material from the identifier, the signing share,\n+   the threshold $t$ and $\\mathsf{ak}$ as the group verifying key. If the\n+   wallet knows the group's verifying shares, it SHOULD check that the\n+   member's verifying share matches.\n+\n+A wallet SHOULD tell the user which derivation was found, and SHOULD warn when\n+it is the legacy one, whose notes are not quantum recoverable.\n+\n+The check in step 4 proves that the viewing key was derived from the string's\n+$\\mathsf{sk}$, not that its $\\mathsf{ak}$ is the group's (see\n+[Security Considerations]). Before it shows an address to receive funds, a\n+wallet SHOULD check the viewing key against a source the member trusts, such as\n+the export made with its own string (see [Export]), an address of the group\n+that the member recorded before and that the viewing key gives, or the same\n+viewing key obtained from at least two other members. A member that lost its\n+device may have lost its export too, and then has to ask the others for the\n+key, which is the case the attack needs: if one of the members it asks is\n+honest, a forged key shows up as a mismatch.\n+\n+After the restore, the wallet MUST retain $\\mathsf{sk}$ when the group follows\n+the ZIP 2005 derivation. ZIP 2005 makes $\\mathsf{qsk}$, which is derived from\n+$\\mathsf{sk}$, essential for recovering the group's notes once the current\n+Orchard protocol is disabled [^zip-2005-usagewithfrost], and the signing\n+share and viewing key alone do not give it. A wallet SHOULD also retain\n+$\\mathsf{sk}$ for a legacy group, since without it the member cannot export\n+its string again.\n+\n+A restored member holds no signing nonces. It MUST generate fresh ones for its\n+next signing session.\n+\n+The check in step 4 proves $\\mathsf{sk}$, not the signing share. A wrong share,\n+such as one stored with the wrong sign, passes the restore and is caught at the\n+member's first signing: the verifying shares of the $t$ signers, which travel\n+with their commitments, interpolate to $\\mathsf{ak}$ only if every one of them\n+is right. The signing transport is out of scope for this ZIP, but a signing\n+protocol used with these strings SHOULD carry each signer's verifying share,\n+so that a member restored from a string is checked when it first signs.\n+\n+## Export\n+\n+A wallet that exports a member backup string SHOULD export the group's unified\n+full viewing key with it, or tell the user that the restore needs it. ZIP 2005\n+asks FROST members to retain the group verifying key $\\mathsf{ak}$\n+[^protocol-updated-orchardkeycomponents]; in version 1 it is only held by the\n+viewing key.\n+\n+A wallet MUST NOT put signing nonces, or any other secret beyond the fields\n+above, in a version 1 string.\n+\n+## Storage and transmission\n+\n+Wallets MUST protect every member backup string at least as securely and\n+reliably as $\\mathsf{ask}$, as ZIP 2005 requires of $\\mathsf{qsk}$\n+[^zip-2005-usagewithfrost], and not merely like one signing share.\n+\n+Wallets SHOULD NOT store or transmit a string encrypted only under public-key\n+encryption based on discrete logarithms or on factoring, such as Orchard note\n+encryption, X25519, or RSA and elliptic-curve OpenPGP keys.\n+\n+\n+# Rationale\n+\n+**Bech32m.** It gives a checksum that catches copy errors and a string users\n+can paste or show as a QR code. An uppercase string fits the QR alphanumeric\n+mode, which makes the code smaller. Its checksum guarantees the detection of\n+any error affecting at most 4 characters only for strings of at most 90\n+characters [^zip-0173] [^bip-0350]. A version 1 string is 123 characters\n+long, so it keeps the 30-bit checksum without that guarantee, as unified\n+addresses and viewing keys do [^zip-0316].\n+\n+**No network in the string.** A member's share is the same on every network\n+the group lives on. The viewing key carries the network.\n+\n+**$\\mathsf{ak}$ is not stored.** The restore needs the viewing key anyway to\n+scan the chain, and checking $\\mathsf{nk}$ and $\\mathsf{rivk}$ against it\n+catches a string from another group. The cost is that the string does not\n+authenticate $\\mathsf{ak}$ (see [Security Considerations]).\n+\n+**$\\mathsf{sk}$ is stored whole.** ZIP 2005 makes it necessary \"to retain, or\n+be able to rederive, $\\mathsf{sk}$\" [^protocol-updated-orchardkeycomponents],\n+and every member already holds it, along with $\\mathsf{qsk}$\n+[^zip-2005-usagewithfrost].\n+\n+**The derivation is found, not recorded.** Version 1 is Zkool's existing\n+payload, which has no field for it. With two derivations and the viewing key at\n+hand, trying both is cheap and unambiguous: both matching would need a\n+collision in the hash functions behind $\\mathsf{rivk}$. A later version can\n+record the derivation explicitly (see [Open issues]).\n+\n+**One-byte identifier.** It covers groups whose identifiers are the integers\n+1 to $n$, as in Zkool and in frost-client's trusted-dealer key generation.\n+frost-client's DKG and Zafe derive 32-byte identifiers, which need another\n+version (see [Open issues]).\n+\n+**Version 1 is Zkool's payload.** Backups users already made keep working,\n+and the first implementation exists today.\n+\n+\n+# Security Considerations\n+\n+A string holds one signing share. Fewer than $t$ strings cannot spend, as long\n+as discrete logarithms on the Pallas curve stay hard.\n+\n+Every string also holds $\\mathsf{sk}$, which yields $\\mathsf{qsk}$. ZIP 2005\n+notes that \"a quantum adversary may be able to steal the funds with only\n+access to $\\mathsf{qsk}$, which is held by every participant\"\n+[^zip-2005-usagewithfrost]. Against such an adversary the threshold may give no\n+protection: one leaked string, from any member, may be enough to steal the\n+funds. ZIP 2005 requires each participant to \"treat $\\mathsf{qsk}$ as a\n+secret held at least as securely and reliably as $\\mathsf{ask}$\"\n+[^zip-2005-usagewithfrost], which is why [Storage and transmission] asks the\n+same for every string.\n+\n+For the same reason, a string that is stored or sent encrypted only under\n+public-key encryption based on discrete logarithms or on factoring is exposed\n+to an adversary who records the ciphertext now and gains a quantum computer\n+later. A passphrase with a memory-hard KDF and an AEAD (see [Open issues])\n+does not have this weakness.\n+\n+The restore does not prove the signing share (see [Restore]). A wrong share,\n+for instance one exported with the wrong sign (see [Payload, version 1]),\n+gives signature shares that fail verification. It stops that member from\n+signing until it is restored from a correct string, but it cannot sign\n+anything or leak the other shares.\n+\n+The restore does not authenticate $\\mathsf{ak}$ either. Every member holds\n+$\\mathsf{sk}$, so a dishonest member can build a viewing key around another\n+$\\mathsf{ak}$, for instance one whose secret it holds alone, with the\n+$\\mathsf{nk}$ and $\\mathsf{rivk}$ that either derivation gives for it. A string\n+restored with that key passes every check in [Restore], and the wallet then\n+shows addresses that only that member can spend from. The wallet would not see\n+the group's notes, and the group's signatures would not verify under that\n+$\\mathsf{ak}$, but funds received at those addresses before anyone notices are\n+lost to the group. Checking the viewing key against a trusted source, as\n+[Restore] asks, prevents this; so would carrying $\\mathsf{ak}$ in the string\n+(see [Open issues]).\n+\n+A member restored from a string has the same authority as before: the string\n+adds no signer to the group. It carries no nonces, so a restore cannot bring\n+back a nonce that was already used or published.\n+\n+\n+# Deployment\n+\n+This ZIP changes no consensus rule. Wallets can adopt it at any time.\n+\n+\n+# Reference implementation\n+\n+- Zkool: `rust/src/frost/backup.rs` in hhanh00/zkool2, released in Zkool\n+  6.35.0 [^zkool-backup].\n+- frost-zec: `src/backup.rs` in OpenPGP-Wallet/frost-zec, by one of this\n+  ZIP's owners, written from this ZIP's rules apart from Zkool's code, with\n+  `backup export` and `backup restore` commands [^frost-zec-backup].\n+\n+\n+# Test vectors\n+\n+The test vectors are generated by the Python code proposed in\n+zcash/zcash-test-vectors#136 [^zcash-test-vectors-pr], in three files:\n+`frost_member_backup`, `frost_member_backup_decoding` and\n+`frost_member_backup_restore`.\n+They cover a 2-of-3 ZIP 2005 group, a 2-of-3 legacy group together with the\n+ZIP 2005 viewing key built from the same $\\mathsf{sk}$ and $\\mathsf{ak}$ (whose\n+strings restore under either key, with a different derivation), and a 3-of-5\n+ZIP 2005 group; an uppercase string; seventeen strings that decoders must\n+reject, two of them for the padding rule and one with a threshold of 1; and\n+three cases that decode but that the restore must reject: a valid string\n+paired with another group's viewing key, a string whose signing share is not\n+canonical, and a viewing key with no Orchard component.\n+\n+Every valid string decodes to the same fields and matches the stated\n+derivation with Zkool's code, and every $t$-subset of members restored from\n+their strings produces a FROST signature that verifies under $\\mathsf{ak}$.\n+Zkool 6.35.0 rejects every invalid string except three. It does not check\n+the padding bits, and decodes the two padding strings to the same fields as\n+the valid string they come from; it also accepts a threshold of 1. Zkool\n+6.36.0 rejects all three: it has the padding fix, hhanh00/zkool2#1468\n+[^zkool-padding-fix], and its decoder now refuses a threshold below 2.\n+A third decoder, by the other owner of this ZIP, with the derivations written\n+directly from BIP 350, ZIP 316 and ZIP 2005 and no Zcash library, gives the\n+expected result for every string, both viewing keys of each group, and every\n+rejected case. With frost-client's version of `reddsa`, every $t$-subset also\n+rebuilds $\\mathsf{ak}$ and signs.\n+\n+One member of the 2-of-3 ZIP 2005 group (byte strings in hex):\n+\n+    sk      24cf13c724758886048e05834d5d0a644e9c8e155d7e0bac3f25586b6caa11c3\n+    ak      a0dffa5f21db1b2c6cc72d9b6af9d777884c2c7d3e06ad479732bec46cee110d\n+    nk      e32868e0d0770d63ad17b8a807c8133bfb957c81fb973f1c43531c7cc15e5831\n+    qsk     7193049fb014e408fd139cfcc47c58d6c20451473bab44ccb9fffc983ecf8ada\n+    qk      eec37a6202cd2f8b1bea8279bf87ef0dcb2bf73bbec28f077c4c5e1a743dd79f\n+    rivk    d0971e49314b6ad96d67af2766afc7db08fe6bf01f3ab0d84376016aad6ef419\n+    t, n    2, 3\n+    id      1\n+    share   2caaa5adfcad8ac0bb8c50dcdbbc1e954825be403f97f44bc1d6c543e15d4f3b\n+    string  zkfrost1qyqsyqev42j6ml9d3tqthrzsmndmc854fqjmuspljl6yhswkc4p7zh208vjv7y78y36c3psy3czcxn2apfjya8ywz4whuzav8uj4s6mv4gguxm4ktp7\n+    ufvk    uview1mvknnjr2s9cuc7z5kjsd3twesswde37tpfw2z4v5a8jmsnwdwsg8sc2hdq302ud7j57d7dvmmt6mtc6c3fk7zkdyx5mrhq8fvyw8xp0dlgzl8kkwawt9j7m9z5adwpuskfscg5fyrpc0sca8lydtxfw7hfaaydtg88ayqj6y89a9jllhupn3hzgp7vq6d\n+\n+\n+# Open issues\n+\n+- **Identifier encoding.** frost-client's DKG derives identifiers as\n+  `Identifier::derive(session_id || pubkey)`, and Zafe from the vault id and\n+  the member's identity key, so groups created by either use full 32-byte\n+  identifiers (frost-client's trusted dealer uses 1 to $n$). For their groups\n+  to fit, a version 2 needs a type byte followed by a short or a full\n+  identifier.\n+- **Identifiers after a refresh.** Refreshing shares to remove a member keeps\n+  the other members' identifiers [^frost-refresh], so a group can end up with\n+  identifiers 1, 2 and 4 and $n = 3$. Version 1 rejects member 4 ($i > n$), so\n+  it covers only groups whose identifiers are 1 to $n$, which is what Zkool\n+  creates. A version 2 could drop that check: the restore can match the\n+  identifier against the group's verifying shares when it knows them.\n+- **Explicit derivation.** A version 2 could record the derivation, so a\n+  restore does not depend on trying each one, and a third derivation could be\n+  added without ambiguity.\n+- **Groups with no $\\mathsf{sk}$.** zcash-sign's `generate`, which Konclave\n+  uses, derives $\\mathsf{nk}$ and $\\mathsf{rivk}$ from a random $\\mathsf{sk}$\n+  and then discards it, so version 1 cannot hold its groups. A version 2 could\n+  record that $\\mathsf{nk}$ and $\\mathsf{rivk}$ come from the viewing key,\n+  leave $\\mathsf{sk}$ out, and carry the viewing key itself (next item).\n+  Carrying only $\\mathsf{ak}$, as first suggested in zcash/zips#1413, would let\n+  the restore catch a viewing key from another group, but not a different\n+  viewing key for the same group. Such groups are not quantum recoverable\n+  under ZIP 2005.\n+- **The viewing key in the string**, so that a restore needs only the string.\n+  For a group with no $\\mathsf{sk}$, the viewing key is the only source of\n+  $\\mathsf{nk}$ and $\\mathsf{rivk}$. A version 2 would carry the raw 96-byte\n+  Orchard full viewing key rather than its unified encoding, so that the\n+  string stays network-agnostic. With the viewing key in the string, nothing\n+  outside it is left to mismatch. For a group with $\\mathsf{sk}$,\n+  $\\mathsf{ak}$ is enough, since $\\mathsf{nk}$ and $\\mathsf{rivk}$ follow from\n+  $\\mathsf{sk}$ and $\\mathsf{ak}$. Either way, $\\mathsf{ak}$ would then come\n+  from the member's own backup rather than from a viewing key obtained\n+  elsewhere (see [Security Considerations]). A string for a group with no\n+  $\\mathsf{sk}$ would be about 230 characters long, or 280 with a 32-byte\n+  identifier (see [Rationale] on Bech32m).\n+- **Birthday height in version 2.** A raw Orchard full viewing key does not\n+  carry the birthday height that zips#1401 would add to the unified encoding\n+  [^zips-1401], so a string that restores on its own would need a birthday\n+  field of its own, as Konclave noted in zcash/zips#1413. A height only means\n+  something on one network, so the field would either name the network or\n+  apply to the network the wallet restores on.\n+- **Passphrase profile.** An optional version encrypting the payload with a\n+  memory-hard KDF and an AEAD, as Zafe does for its backups.\n+- **Human-readable part.** `zkfrost` is kept for compatibility with existing\n+  backups. Should a later version use a more neutral one?\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2026.7.0 [NU6.2] or later. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^protocol-updated-orchardkeycomponents]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.2.3: Orchard Key Components](protocol/protocol.pdf#orchardkeycomponents)\n+\n+[^zip-0173]: [ZIP 173: Bech32 Format](zip-0173.rst)\n+\n+[^zip-0312]: [ZIP 312: FROST for Spend Authorization Multisignatures](zip-0312.rst)\n+\n+[^zip-0312-key-generation]: [ZIP 312: FROST for Spend Authorization Multisignatures — Key Generation](zip-0312.rst#key-generation)\n+\n+[^zip-0316]: [ZIP 316: Unified Addresses and Unified Viewing Keys](zip-0316.rst)\n+\n+[^zip-2005]: [ZIP 2005: Ironwood Quantum Recoverability](zip-2005.md)\n+\n+[^zip-2005-usagewithfrost]: [ZIP 2005: Ironwood Quantum Recoverability — Usage with FROST](zip-2005.md#usagewithfrost)\n+\n+[^bip-0350]: [BIP 350: Bech32m format for v1+ witness addresses](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)\n+\n+[^zkool-forum]: [Hanh, Zcash Community Forum, 2026-07-15](https://forum.zcashcommunity.com/t/56660/5)\n+\n+[^zkool-backup]: [Zkool: the zkfrost member backup string](https://github.com/hhanh00/zkool2/blob/4ad5a89e09e939a415dd7376ec94f0a241f98c8c/rust/src/frost/backup.rs)\n+\n+[^zkool-padding-fix]: [hhanh00/zkool2#1468: Bech32 decoders reject non-canonical padding](https://github.com/hhanh00/zkool2/pull/1468)\n+\n+[^zcash-test-vectors-pr]: [zcash/zcash-test-vectors#136: Add test vectors for FROST member backup strings](https://github.com/zcash/zcash-test-vectors/pull/136)\n+\n+[^frost-refresh]: [ZcashFoundation/frost: frost-core `keys::refresh`](https://github.com/ZcashFoundation/frost/blob/main/frost-core/src/keys/refresh.rs)\n+\n+[^zips-1401]: [zcash/zips#1401: Assign a Metadata Typecode for viewing key birthday height](https://github.com/zcash/zips/issues/1401)\n+\n+[^frost-zec-backup]: [frost-zec: member backup strings](https://github.com/OpenPGP-Wallet/frost-zec/blob/master/src/backup.rs)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-khadim-aryaethn-frost-member-backup.md","title":"FROST Member Backup Strings for Shielded Zcash","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"derivations","text":"Derivations"},{"level":2,"id":"encoding","text":"Encoding"},{"level":2,"id":"payload-version-1","text":"Payload, version 1"},{"level":2,"id":"decoding","text":"Decoding"},{"level":2,"id":"restore","text":"Restore"},{"level":2,"id":"export","text":"Export"},{"level":2,"id":"storage-and-transmission","text":"Storage and transmission"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"security-considerations","text":"Security Considerations"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"test-vectors","text":"Test vectors"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1421,"title":"Add draft ZIP: Miner Sentiment Polling","author":"upbqdn","createdAt":"2026-10-28T20:47:12Z","updatedAt":"2026-11-04T02:49:47Z","url":"https://github.com/zcash/zips/pull/1421","diffUrl":"https://github.com/zcash/zips/pull/1421.diff","headRef":"miner-sentiment-polling","headRepo":"upbqdn/zips","headSha":"d585275fa0bf197d4aee424414ac1263d24f327e","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"[Rendered.](https://github.com/upbqdn/zips/blob/miner-sentiment-polling/zips/draft-marek-miner-sentiment-polling.md) Proposes polling miners on upcoming network upgrades alongside the ZCAP polls. Miners answer the ZCAP poll's questions with a compact ASCII ballot (`zpoll::`) in the coinbase miner da","zipRefs":[],"draftRefs":["draft-marek-miner-sentiment-polling"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -233,6 +233,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-marek-miner-sentiment-polling</td> <td class=\"left\"><a href=\"zips/draft-marek-miner-sentiment-polling.md\">Miner Sentiment Polling</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-marek-miner-sentiment-polling.md","status":"added","additions":618,"deletions":0,"patch":"@@ -0,0 +1,618 @@\n+    ZIP: Unassigned\n+    Title: Miner Sentiment Polling\n+    Owners: Marek <marek@zfnd.org>\n+    Status: Draft\n+    Category: Process / Ecosystem\n+    Created: 2026-10-27\n+    License: MIT\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to be\n+interpreted as described in BCP 14 [^BCP14] when, and only when, they appear in all\n+capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in § 3.12\n+‘Mainnet and Testnet’. [^protocol-networks]\n+\n+The terms \"best valid block chain\" and \"block height\" are to be interpreted as\n+described in § 3.3 ‘The Block Chain’ [^protocol-blockchain], and the term \"coinbase\n+transaction\" as described in § 3.11 ‘Coinbase Transactions’.\n+[^protocol-coinbasetransactions]\n+\n+The term \"network upgrade\" is to be interpreted as described in ZIP 200.\n+[^zip-0200]\n+\n+The term \"Zcash Community Advisory Panel\" (or \"ZCAP\") is to be interpreted as\n+described in ZIP 1015. [^zip-1015]\n+\n+The terms below are to be interpreted as follows:\n+\n+Election\n+:   A round of sentiment polling on the scope or timing of one or more upcoming\n+    network upgrades, consisting of a ZCAP Poll and the Miner Poll specified in\n+    this ZIP.\n+\n+ZCAP Poll\n+:   The poll of ZCAP members held for an Election. [^nu7-poll-results]\n+\n+Miner Poll\n+:   The poll of miners held for an Election, as specified in this ZIP.\n+\n+Poll Organizer\n+:   The ZCAP, as the organizer of the ZCAP Poll of an Election.\n+\n+Poll Definition\n+:   The document that specifies a Miner Poll; see [Poll Definitions].\n+\n+Election Identifier\n+:   The short string that identifies an Election in Miner Ballots.\n+\n+Voting Period\n+:   The inclusive range of block heights in which Miner Ballots for an Election\n+    are counted, from the Voting Start Height to the Voting End Height.\n+\n+Miner Data\n+:   The bytes of the script of the transparent input of a coinbase transaction\n+    that follow the encoding of the block height required by § 7.1.2\n+    ‘Transaction Consensus Rules’. [^protocol-txnconsensus]\n+\n+Coinbase Memo\n+:   The memo field of a shielded output of a coinbase transaction, as recovered by\n+    decrypting the output using a sequence of 32 zero bytes as the outgoing\n+    viewing key, as specified in § 4.20.3 ‘Decryption using an Outgoing Viewing\n+    Key (Sapling and Orchard)’. [^protocol-decryptovk] [^zip-0213]\n+\n+Miner Ballot\n+:   A string that encodes a miner's answers to the questions of an Election; see\n+    [Miner Ballots].\n+\n+Option Character\n+:   A byte that is an ASCII digit `1` to `9` or an uppercase ASCII letter `A` to\n+    `Z`.\n+\n+Answer Character\n+:   A byte that is an Option Character, a hyphen `-`, or a parenthesis `(` or\n+    `)`.\n+\n+Participation Threshold\n+:   The minimum percentage of the blocks in a Voting Period that answer a\n+    question, for the Miner Poll results on that question to be considered\n+    representative of miners.\n+\n+\n+# Abstract\n+\n+This proposal specifies a process for gauging miners' sentiment on upcoming\n+network upgrades. For each Election, miners answer the same questions, with the\n+same options, as the ZCAP.\n+\n+A miner votes by including a compact ASCII Miner Ballot in the coinbase\n+transaction of each block it mines within a Voting Period, which the community\n+agrees upon through public review and which is fixed in block heights in\n+advance. The Miner Ballot goes in the Miner Data of the coinbase script or in a\n+Coinbase Memo. Each block in the Voting Period is one vote, and a block without\n+a valid Miner Ballot answers no question, so results are shares of all blocks in\n+the Voting Period and approximate shares of hashrate. Anyone can compute them\n+from the block chain.\n+\n+Miner Poll results are published alongside ZCAP Poll results. Miner Ballots are\n+public and linked to the blocks that carry them. No consensus change is\n+required.\n+\n+\n+# Motivation\n+\n+The scope of network upgrades is decided with input from polls of the ZCAP,\n+whose NU7 results ZIP 259 cites. [^zip-0259] Coinholder polls, such as the NU7\n+coinholder vote [^nu7-poll-questions], are also held, but are not officially\n+recognized.\n+\n+Miners are not polled, although network upgrades affect them directly. NU7\n+changes the block target spacing and the block subsidy per block [^zip-0218], and\n+removes 60% of transaction fees from circulation [^zip-0235]. An upgrade also\n+activates safely only if miners run node software that supports it.\n+\n+Miners already put free-form text in coinbase transactions, and ZIP 213\n+anticipated identifying text in shielded coinbase memos [^zip-0213], but without\n+a common format that text cannot be tallied. A common encoding makes the tally\n+reproducible by anyone, and asking the questions of the ZCAP Poll makes the\n+results directly comparable.\n+\n+\n+# Privacy Implications\n+\n+Miner Ballots are public by design and linked to the blocks that carry them and\n+to their coinbase outputs. Coinbase outputs are already public: transparent\n+outputs directly, and shielded outputs because they decrypt with the all-zero\n+outgoing viewing key [^zip-0213]. A Miner Ballot therefore reveals only the\n+miner's answers.\n+\n+Unlike a ZCAP Poll, a Miner Poll has no ballot secrecy: the positions of mining\n+pools and solo miners become public. A mining pool votes with its hashers' work;\n+individual hashers are not identified, and their preferences are not recorded.\n+\n+\n+# Requirements\n+\n+* Miners can answer exactly the questions of the ZCAP Poll, with exactly its\n+  options.\n+* Casting a Miner Ballot requires no consensus change or new transaction type.\n+* Existing node software can cast Miner Ballots through string-valued\n+  configuration.\n+* Every block counts as a single vote.\n+* Support for each option is reported as a share of all blocks in the Voting\n+  Period, including blocks without a Miner Ballot.\n+* The tally is fully determined by the Poll Definition and public block chain\n+  data.\n+* The community agrees upon the Voting Period of each Election through public\n+  review, and the Voting Period is fixed in advance and unambiguous.\n+* Miner Poll results are comparable with ZCAP Poll results.\n+\n+\n+# Non-requirements\n+\n+* Ballot secrecy for miners.\n+* A binding effect of the results, or activation signalling enforced by\n+  consensus.\n+* Separate representation of hashers within a mining pool.\n+\n+\n+# Specification\n+\n+For each Election, the Poll Organizer MUST hold a Miner Poll on Mainnet as\n+specified below.\n+\n+## Poll Definitions\n+\n+For each Election, the Poll Organizer MUST publish a Poll Definition on the Zcash\n+Community Forum, in the announcement of the ZCAP Poll or in a post linked from\n+it. A Poll Definition gives:\n+\n+* the Election Identifier;\n+* the network (Mainnet or Testnet);\n+* the Voting Start Height and the Voting End Height;\n+* the questions, the options for each question, and the maximum number of\n+  selections for each multiple-choice question;\n+* the Participation Threshold, as a percentage;\n+* a link to the announcement of the ZCAP Poll.\n+\n+The questions and options MUST be identical in wording, order, and number to\n+those of the ZCAP Poll. Questions and the options of each question are numbered\n+from 1 in order of appearance; an explicit \"Abstain\" option is an ordinary\n+option. A question MUST NOT have more than 35 options.\n+\n+A question is single-choice if the ZCAP Poll allows selecting at most one of its\n+options, and multiple-choice otherwise. The maximum number of selections of a\n+multiple-choice question MUST be the number of options the ZCAP Poll allows\n+selecting for it, which is from 2 to its number of options.\n+\n+The longest answer string of an Election has 1 character for each single-choice\n+question, and the maximum number of selections plus 2 for each multiple-choice\n+question. Its length MUST NOT exceed 489, so that every Miner Ballot fits in a\n+512-byte Coinbase Memo.\n+\n+The Election Identifier MUST have 1 to 16 characters, each a lowercase ASCII\n+letter `a` to `z`, an ASCII digit `0` to `9`, or a hyphen `-`. It MUST NOT equal\n+the Election Identifier of any earlier Poll Definition on the same network.\n+\n+A published Poll Definition MUST NOT be changed. To change its questions,\n+options, Voting Period, or Participation Threshold, the Poll Organizer publishes a\n+new Poll Definition, which supersedes it and is subject to the requirements in\n+[Voting Period]. A Poll Definition can only be superseded before the block at its\n+Voting Start Height is mined, and a superseded Poll Definition is not tallied.\n+Once the Voting Period of a Poll Definition has started, its Miner Poll MUST be\n+tallied and its results published, even if a new Poll Definition is published\n+afterward.\n+\n+## Voting Period\n+\n+The Voting Period consists of the block heights from the Voting Start Height to\n+the Voting End Height inclusive. The Voting End Height MUST be greater than or\n+equal to the Voting Start Height. The Voting Period of each Election is agreed\n+upon as follows:\n+\n+1. The Poll Organizer proposes the Voting Period, together with the questions, in\n+   the announcement of the ZCAP Poll and invites feedback on the forum.\n+2. The proposed Voting Period SHOULD coincide with the voting window of the ZCAP\n+   Poll. To convert times to block heights, the Poll Organizer SHOULD use the\n+   block target spacing in effect, accounting for any network upgrade scheduled\n+   to activate before the Voting End Height.\n+3. After considering the feedback, the Poll Organizer publishes the final Poll\n+   Definition. Its Voting Period MUST have been proposed on the forum, as in\n+   step 1, at least 7 days earlier, so a Voting Period changed in response to\n+   feedback is proposed again. With the final Poll Definition, the Poll\n+   Organizer MUST answer on the forum each objection to the Voting Period raised\n+   during the feedback period, stating how it was addressed or why it was not.\n+   The final Poll Definition MUST be published at least 14 days before the\n+   block at the Voting Start Height is expected to be mined.\n+\n+## Miner Ballots\n+\n+A Miner Ballot is a string of ASCII bytes with the following ABNF syntax\n+[^RFC5234]. The prefix is given as bytes because quoted ABNF strings are\n+case-insensitive.\n+\n+```abnf\n+miner-ballot  = %x7A.70.6F.6C.6C.3A election-id \":\" answer-string  ; \"zpoll:\"\n+election-id   = 1*16( %x61-7A / DIGIT / \"-\" )\n+answer-string = 1*answer\n+answer        = \"-\" / option / \"(\" 1*option \")\"\n+option        = %x31-39 / %x41-5A\n+```\n+\n+An Option Character selects an option: a digit `1` to `9` selects options 1 to 9\n+respectively, and a letter `A` to `Z` selects options 10 to 35 respectively. The\n+$i$-th answer of the answer string answers question $i$:\n+\n+* a single Option Character selects its option;\n+* Option Characters in parentheses select each of their options;\n+* a hyphen `-` leaves the question unanswered.\n+\n+The answer string is the longest run of Answer Characters immediately following\n+`zpoll:` ‖ election-id ‖ `:`. It ends at the first byte that is not an Answer\n+Character, or at the end of the containing data.\n+\n+A Miner Ballot is valid for an Election $E$ if and only if:\n+\n+* its election-id equals the Election Identifier of $E$;\n+* its answer string matches `answer-string`, with exactly one answer per\n+  question of $E$;\n+* each answer to a single-choice question is a hyphen or a single Option\n+  Character, and each answer to a multiple-choice question is a hyphen or\n+  Option Characters in parentheses;\n+* each answer selects only options that exist for its question;\n+* the Option Characters in parentheses select options in strictly ascending\n+  order, and no more options than the maximum number of selections of the\n+  question;\n+* at least one answer is not a hyphen.\n+\n+## Casting Miner Ballots\n+\n+A miner casts a Miner Ballot by including it in the coinbase transaction of a\n+block it mines at a height within the Voting Period: in the Miner Data, in the\n+Coinbase Memo of one or more shielded outputs, or in both. A Coinbase Memo that\n+carries a Miner Ballot MUST be a text memo as defined in ZIP 302 [^zip-0302]: its\n+first byte MUST be `0xF4` or smaller, and it MUST be valid UTF-8 after trailing\n+zero bytes are stripped.\n+\n+The bytes of a Miner Ballot MUST be consecutive, and the byte immediately\n+following it, if any, MUST NOT be an Answer Character. In the Miner Data, both\n+requirements apply to the raw bytes, including script opcodes. Placing the whole\n+Miner Ballot in a single data push satisfies the first requirement, and ending\n+the Miner Data, or the text of a text memo before its zero padding, with the\n+Miner Ballot satisfies the second.\n+\n+A miner SHOULD include its Miner Ballot in every block it mines during the Voting\n+Period. Since each block counts separately, a miner MAY change its Miner Ballot\n+during the Voting Period. A mining pool that casts Miner Ballots SHOULD publish\n+its Miner Ballot to its hashers before the Voting Start Height.\n+\n+## Determining the Miner Ballot of a Block\n+\n+For an Election $E$ and a block $B$ at a height within the Voting Period of $E$,\n+the Miner Ballot of $B$ for $E$ is determined as follows:\n+\n+1. Search the raw bytes of the Miner Data of $B$, without parsing them as script,\n+   for occurrences of `zpoll:` ‖ Election Identifier of $E$ ‖ `:`. The first\n+   occurrence that, together with its answer string, forms a Miner Ballot valid\n+   for $E$ gives the Miner Ballot of $B$.\n+2. Otherwise, search the Coinbase Memos of the shielded outputs of the coinbase\n+   transaction of $B$ in the same way, in the order in which the outputs appear\n+   in the transaction encoding. Skip each Coinbase Memo that is not a ZIP 302\n+   text memo: one whose first byte is greater than `0xF4`, or that is not valid\n+   UTF-8 after trailing zero bytes are stripped. The first Miner Ballot valid for\n+   $E$ found in this order gives the Miner Ballot of $B$.\n+3. Otherwise, $B$ has no Miner Ballot for $E$.\n+\n+Any other Miner Ballots for $E$ in $B$ have no effect.\n+\n+## Tallying\n+\n+The tally of a Miner Poll is computed over the best valid block chain of the\n+network given in its Poll Definition, once that chain contains a block at height\n+Voting End Height + 99, giving the block at the Voting End Height 100\n+confirmations.\n+\n+Each block in the Voting Period counts as a single vote. Its Miner Ballot, as\n+determined in [Determining the Miner Ballot of a Block], counts exactly once,\n+regardless of who mined it or the value of its coinbase outputs. A block without\n+a Miner Ballot for the Election counts as answering no question.\n+\n+Let:\n+\n+* $N$ be the number of blocks in the Voting Period, i.e. Voting End Height −\n+  Voting Start Height + 1;\n+* $P$ be the number of blocks in the Voting Period that have a Miner Ballot for\n+  the Election;\n+* $c_{q,o}$ be the number of those blocks whose Miner Ballot selects option $o$\n+  for question $q$;\n+* $A_q$ be the number of those blocks whose Miner Ballot answers question $q$.\n+\n+For a single-choice question, $A_q = \\sum_o c_{q,o}$. A Miner Ballot can select\n+several options of a multiple-choice question, so for such a question the counts\n+$c_{q,o}$ can sum to more than $A_q$, and the percentages of its options to more\n+than 100%.\n+\n+The results of the Miner Poll consist of:\n+\n+* for each question $q$ and option $o$, the count $c_{q,o}$ and the percentage\n+  $100 \\cdot c_{q,o} / N$;\n+* for each question $q$, the count $N - A_q$ of blocks that do not answer $q$,\n+  and the percentage $100 \\cdot (N - A_q) / N$;\n+* $N$, $P$, and the overall participation $100 \\cdot P / N$ as a percentage;\n+* for each question $q$, whether $100 \\cdot A_q / N$ is at least the\n+  Participation Threshold;\n+* the height and hash of the block at the Voting End Height in the block chain\n+  that was tallied.\n+\n+Percentages in the results, and in any summary of them by the Poll Organizer,\n+MUST be relative to $N$. The counts are authoritative.\n+\n+The results on question $q$ are representative of miners if and only if\n+$100 \\cdot A_q / N$ is at least the Participation Threshold.\n+\n+## Publication of Results\n+\n+The Poll Organizer MUST publish the Miner Poll results in the same forum thread\n+as the ZCAP Poll results, no later than 7 days after the block at height\n+Voting End Height + 99 is mined.\n+\n+A ZIP that cites an Election's ZCAP Poll results, as ZIP 259 does for NU7\n+[^zip-0259], SHOULD also cite that Election's Miner Poll results.\n+\n+This ZIP does not specify what weight Miner Poll results carry relative to ZCAP\n+Poll results, or how the two are combined. Coinholder polls are outside its\n+scope.\n+\n+## Testnet-specific considerations\n+\n+Miner Polls SHOULD be rehearsed on Testnet before the first Miner Poll on\n+Mainnet. Miner Polls on Testnet are rehearsals only: they are not the Miner Poll\n+of any Election and have no significance for Zcash governance.\n+\n+Miner Ballots have the same encoding on both networks, and Election Identifiers\n+are unique only per network. A Poll Definition on Testnet that rehearses a Miner\n+Poll on Mainnet SHOULD use the Election Identifier, questions, and options of\n+that Mainnet Poll Definition, so that miners rehearse exactly the Miner Ballots\n+they will cast on Mainnet. The 7-day and 14-day requirements in [Voting Period]\n+do not apply on Testnet.\n+\n+## Example\n+\n+This subsection is non-normative.\n+\n+Consider a hypothetical Mainnet Election with Election Identifier `nu8` and five\n+single-choice questions, which have 4, 4, 4, 3, and 4 options.\n+\n+The 15-byte Miner Ballot `zpoll:nu8:2311-` selects option 2 for question 1,\n+option 3 for question 2, and option 1 for questions 3 and 4; it leaves question 5\n+unanswered. A miner placing it in the Miner Data of a block at height 3,600,000\n+can use the 20-byte coinbase script `0380ee360f7a706f6c6c3a6e75383a323331312d`:\n+the height encoding `0380ee36`, the push opcode `0f`, and the Miner Ballot. As a\n+Coinbase Memo, the Miner Ballot is its 15 bytes followed by 497 zero bytes. The\n+same 15 bytes followed by `0xFF` and 496 zero bytes are not valid UTF-8, so that\n+memo is not a text memo and carries no Miner Ballot.\n+\n+If the Voting Period has 100,000 blocks and only 10, all mined by one pool, carry\n+`zpoll:nu8:2311-`, option 2 of question 1 has 10 votes (0.01%) and 99,990 blocks\n+(99.99%) do not answer question 1. Question 1 is answered in only 0.01% of the\n+Voting Period, so its results are not representative of miners unless the\n+Participation Threshold is at most 0.01%.\n+\n+If instead 99,990 blocks carry `zpoll:nu8:2311-` and 10 carry `zpoll:nu8:23113`,\n+overall participation is 100% and questions 1 to 4 are each answered in 100% of\n+the Voting Period. Question 5, however, is answered in only 0.01%, so its results\n+are not representative of miners unless the Participation Threshold is at most\n+0.01%.\n+\n+The following table shows how Miner Data is interpreted for this Election:\n+\n+| Data | Result |\n+|------|--------|\n+| `zpoll:nu8:2311-` | Valid, as above. |\n+| `/ExamplePool/ zpoll:nu8:2311-` | Valid. |\n+| `zpoll:nu8:2311-/ExamplePool/` | Valid; `/` ends the answer string. |\n+| `zpoll:nu8:9 zpoll:nu8:2311-` | Valid; the first occurrence is invalid, so the second counts. |\n+| `zpoll:nu8:2311` | Invalid: four answers for five questions. |\n+| `zpoll:nu8:2311-X` | Invalid: the answer string has six answers. |\n+| `zpoll:nu8:2311-(ExamplePool)` | Invalid: `(` and `E` are Answer Characters, so the answer string is `2311-(E`. |\n+| `zpoll:nu8:2314-` | Invalid: question 4 has no option 4. |\n+| `zpoll:nu8:-----` | Invalid: no question is answered. |\n+| `ZPOLL:nu8:2311-` | Not a Miner Ballot: the prefix is case-sensitive. |\n+| `zpoll:nu7:2311-` | Not a Miner Ballot for `nu8`. |\n+\n+Consider also a hypothetical Election with Election Identifier `devfund` and two\n+questions: a multiple-choice question with 6 options and at most 3 selections,\n+and a single-choice question with 3 options. Its longest answer string has 5 + 1\n+= 6 characters.\n+\n+| Data | Result |\n+|------|--------|\n+| `zpoll:devfund:(135)2` | Valid: options 1, 3, and 5 for question 1, and option 2 for question 2. |\n+| `zpoll:devfund:(3)-` | Valid: option 3 for question 1; question 2 is unanswered. |\n+| `zpoll:devfund:3-` | Invalid: an answer to a multiple-choice question needs parentheses. |\n+| `zpoll:devfund:(1)(2)` | Invalid: question 2 is single-choice. |\n+| `zpoll:devfund:()2` | Invalid: parentheses enclose no option. |\n+| `zpoll:devfund:(1356)2` | Invalid: four selections for question 1. |\n+| `zpoll:devfund:(31)2` | Invalid: options not in ascending order. |\n+| `zpoll:devfund:(33)2` | Invalid: option 3 is selected twice. |\n+| `zpoll:devfund:(17)2` | Invalid: question 1 has no option 7. |\n+\n+If a Voting Period of 100 blocks has 30 blocks that carry `zpoll:devfund:(135)2`\n+and 20 that carry `zpoll:devfund:(3)-`, option 3 of question 1 has 50 votes (50%),\n+and options 1 and 5 have 30 votes (30%) each. Question 1 is answered in 50 blocks\n+(50%), although its options have 110 votes in total.\n+\n+\n+# Rationale\n+\n+## Rationale for counting blocks\n+\n+The number of blocks mined approximates a miner's share of hashrate, and proof of\n+work makes Sybil voting pointless, since more Miner Ballots require more work.\n+Mining pools represent their hashers, who can switch pools if they disagree with\n+their pool's Miner Ballot.\n+\n+Blocks are not weighted by coinbase output value or timestamp, which keeps the\n+tally independent of transaction fees, the block subsidy, and timestamps chosen\n+by miners. A change to the block target spacing within a Voting Period changes\n+the number of blocks per unit of time for all miners alike, so counting blocks\n+still approximates share of hashrate.\n+\n+Rented hashrate could buy an outcome at the cost of that hashrate, and public\n+Miner Ballots make bribes verifiable; both are reasons why the results indicate\n+sentiment and are not binding.\n+\n+## Rationale for counting blocks without Miner Ballots\n+\n+Percentages are relative to all blocks in the Voting Period, as the version bits\n+mechanism of BIP 9 counts a block that does not signal against activation.\n+[^bip-0009] Relative to Miner Ballots cast, the 10 Miner Ballots from one pool in\n+the [Example] would show 100% support for their options; relative to the Voting\n+Period, they show 0.01%. A miner's influence on any percentage is thus bounded by\n+its share of the blocks in the Voting Period, however few other miners vote.\n+\n+This is possible because the electorate of a Miner Poll, the blocks in the Voting\n+Period, is fully observable, as the membership of the ZCAP is. Shares among the\n+blocks that answer a question follow from the published counts $c_{q,o}$ and\n+$A_q$.\n+\n+The Participation Threshold flags low turnout. It applies to each question:\n+since a Miner Ballot can leave questions unanswered, a heavily answered question\n+would otherwise make the results on an unanswered question appear\n+representative.\n+\n+The tally does not group blocks by miner, because coinbase outputs and pool tags\n+are chosen by miners and do not reliably identify them.\n+\n+## Rationale for using coinbase transactions\n+\n+Only the miner of a block controls its coinbase transaction, so proof of work\n+authenticates the Miner Ballot. No consensus change is needed: consensus\n+constrains the Miner Data only by the height encoding, the length limit\n+[^protocol-txnconsensus], and the block-level limit on signature operations\n+inherited from Bitcoin, to which the ASCII bytes of a Miner Ballot contribute\n+nothing; and shielded coinbase outputs are publicly decryptable [^zip-0213]. The\n+Zcash Stratum protocol keeps the extra nonce in the block header nonce\n+[^zip-0301], leaving room in the Miner Data.\n+\n+Two alternatives were rejected:\n+\n+* Block header version bits: consensus only requires the block version number to\n+  be at least 4 [^protocol-blockheader], but a bit expresses only yes or no.\n+* Separate transactions: anyone can create them, and they are not tied to block\n+  production.\n+\n+## Rationale for the Miner Ballot encoding\n+\n+ASCII suits string-valued node configuration, ZIP 302 text memos, and block\n+explorers. Compactness matters because the 100-byte coinbase script is shared\n+with the height encoding and pool tags; the Miner Ballot `zpoll:nu8:2311-` is 15\n+bytes. Positional answers need no question numbers, and a hyphen lets a miner\n+skip a question that has no \"Abstain\" option. Parentheses let a miner answer\n+multiple-choice questions, such as the candidate and Dev Fund proposal questions\n+of past ZCAP Polls, while answers to single-choice questions stay one character\n+long. Requiring ascending order gives each set of selected options a single\n+encoding. The maximum number of selections follows the ZCAP Poll, so that the\n+results of both polls remain comparable.\n+\n+The Election Identifier and the exact-length, option-range, and selection checks\n+keep stale configurations from counting toward a later Election. Ending the\n+answer string at the first byte that is not an Answer Character makes a length\n+field unnecessary. The case-sensitive prefix gives the encoding a single\n+canonical form; an incompatible future encoding would use a different prefix.\n+\n+The Miner Ballot has no network field, and Election Identifiers are unique only\n+per network, so that a rehearsal on Testnet can use exactly the Miner Ballots of\n+the Miner Poll on Mainnet, keeping Testnet as close to Mainnet as possible. A\n+Miner Ballot counts on the network whose block carries it: a miner that copies a\n+rehearsal configuration to Mainnet votes with it on Mainnet.\n+\n+The Miner Data is searched as raw bytes because consensus does not require it to\n+be a well-formed script [^protocol-txnconsensus], and a tally that depended on\n+parsing could differ between implementations that handle malformed scripts\n+differently.\n+\n+## Rationale for the precedence of Miner Ballots\n+\n+The rule is deterministic, and duplicate Miner Ballots carry no penalty. The\n+Miner Data is checked first because it is always present and needs no\n+decryption.\n+\n+## Rationale for block-height Voting Periods\n+\n+Block heights are deterministic, whereas block timestamps are chosen by miners.\n+Changes to the block target spacing are accounted for when the Voting Period is\n+chosen. The 7-day feedback period and the public answers to objections make\n+agreement on the Voting Period reviewable. As for the questions of the ZCAP Poll,\n+that agreement is reached through public review; this ZIP does not define a\n+separate vote on the Voting Period. The 14-day notice period gives mining pools\n+time to reconfigure.\n+\n+\n+# Reference implementation\n+\n+Miners can cast Miner Ballots with Zebra: its `mining.extra_coinbase_data` option\n+places text in the Miner Data, and its `mining.miner_memo` option sets the\n+Coinbase Memo of the miner's output when `mining.miner_address` has a shielded\n+receiver. [^zebra-mining-config]\n+\n+\n+# Open issues\n+\n+* A reference implementation of tallying.\n+* ZCAP Poll questions that rank or score options, which Miner Ballots cannot\n+  answer.\n+* The interaction with memo bundles [^zip-0231], if they are deployed.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3] or later](protocol/protocol.pdf)\n+\n+[^protocol-blockchain]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.3: The Block Chain](protocol/protocol.pdf#blockchain)\n+\n+[^protocol-coinbasetransactions]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.11: Coinbase Transactions](protocol/protocol.pdf#coinbasetransactions)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^protocol-decryptovk]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.20.3: Decryption using an Outgoing Viewing Key (Sapling and Orchard)](protocol/protocol.pdf#decryptovk)\n+\n+[^protocol-txnconsensus]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.1.2: Transaction Consensus Rules](protocol/protocol.pdf#txnconsensus)\n+\n+[^protocol-blockheader]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.6: Block Header Encoding and Consensus](protocol/protocol.pdf#blockheader)\n+\n+[^bip-0009]: [BIP 9: Version bits with timeout and delay](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki)\n+\n+[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)\n+\n+[^zip-0213]: [ZIP 213: Shielded Coinbase](zip-0213.rst)\n+\n+[^zip-0218]: [ZIP 218: 25-second Block Target Spacing](zip-0218.md)\n+\n+[^zip-0231]: [ZIP 231: Memo Bundles](zip-0231.md)\n+\n+[^zip-0235]: [ZIP 235: Remove 60% of Transaction Fees From Circulation](zip-0235.md)\n+\n+[^zip-0259]: [ZIP 259: Deployment of the NU7 Network Upgrade](zip-0259.md)\n+\n+[^zip-0301]: [ZIP 301: Zcash Stratum Protocol](zip-0301.rst)\n+\n+[^zip-0302]: [ZIP 302: Standardized Memo Field Format](zip-0302.rst)\n+\n+[^zip-1015]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding](zip-1015.rst)\n+\n+[^nu7-poll-questions]: [NU7 Coinholder Vote: poll questions and scope](https://forum.zcashcommunity.com/t/nu7-coinholder-vote/56912)\n+\n+[^nu7-poll-results]: [NU7 Sentiment Polls Results](https://forum.zcashcommunity.com/t/nu7-sentiment-polls-results/57590)\n+\n+[^RFC5234]: [RFC 5234: Augmented BNF for Syntax Specifications: ABNF](https://www.rfc-editor.org/rfc/rfc5234.html)\n+\n+[^zebra-mining-config]: [Zebra: mining configuration](https://github.com/ZcashFoundation/zebra/blob/main/zebra-rpc/src/config/mining.rs)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-marek-miner-sentiment-polling.md","title":"Miner Sentiment Polling","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"poll-definitions","text":"Poll Definitions"},{"level":2,"id":"voting-period","text":"Voting Period"},{"level":2,"id":"miner-ballots","text":"Miner Ballots"},{"level":2,"id":"casting-miner-ballots","text":"Casting Miner Ballots"},{"level":2,"id":"determining-the-miner-ballot-of-a-block","text":"Determining the Miner Ballot of a Block"},{"level":2,"id":"tallying","text":"Tallying"},{"level":2,"id":"publication-of-results","text":"Publication of Results"},{"level":2,"id":"testnet-specific-considerations","text":"Testnet-specific considerations"},{"level":2,"id":"example","text":"Example"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"rationale-for-counting-blocks","text":"Rationale for counting blocks"},{"level":2,"id":"rationale-for-counting-blocks-without-miner-ballots","text":"Rationale for counting blocks without Miner Ballots"},{"level":2,"id":"rationale-for-using-coinbase-transactions","text":"Rationale for using coinbase transactions"},{"level":2,"id":"rationale-for-the-miner-ballot-encoding","text":"Rationale for the Miner Ballot encoding"},{"level":2,"id":"rationale-for-the-precedence-of-miner-ballots","text":"Rationale for the precedence of Miner Ballots"},{"level":2,"id":"rationale-for-block-height-voting-periods","text":"Rationale for block-height Voting Periods"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1346,"title":"Bulk synchronization: v2 protocol primitives and Block Chain Synchronization ZIP","author":"arya2","createdAt":"2026-08-06T19:03:32Z","updatedAt":"2026-11-04T01:44:15Z","url":"https://github.com/zcash/zips/pull/1346","diffUrl":"https://github.com/zcash/zips/pull/1346.diff","headRef":"agent/checkpointed-sync-zip","headRepo":"zcash/zips","headSha":"8c15df4fc23046d13aa430a1af14e86eafd9d089","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Recommends how nodes synchronize over the version 2 P2P protocol. The v2 protocol draft (#1344) now defines the primitives (`get-headers`/`get-blocks`/`get-hashes`) and the normative synchronization rules for both headers-first and checkpointed synchronization; this ZIP recommends concrete strategie","zipRefs":[],"draftRefs":["draft-arya-block-chain-sync"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-arya-block-chain-sync.md","status":"added","additions":634,"deletions":0,"patch":"@@ -0,0 +1,634 @@\n+    ZIP: XXX\n+    Title: Block Chain Synchronization\n+    Owners: Arya <arya@zfnd.org>\n+    Status: Draft\n+    Category: Network\n+    Created: 2026-08-06\n+    License: MIT\n+    Discussions-To: <https://github.com/zcash/zips/issues/352>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"MAY\", and\n+\"RECOMMENDED\" in this document are to be interpreted as described in BCP 14\n+[^BCP14] when, and only when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in\n+section 3.12 of the Zcash Protocol Specification. [^protocol-networks]\n+\n+The term \"block chain\" in this document is to be interpreted as described in\n+section 3.3 of the Zcash Protocol Specification. [^protocol-blockchain]\n+\n+The term \"v2 protocol\" in this document refers to the version 2 Zcash P2P\n+network protocol [^draft-p2p-v2]. The terms \"peer\" and \"request stream\", the\n+`get-headers`, `get-blocks`, `get-hashes`, `get-block-range`,\n+`get-tree-roots`, and `get-object` request stream types, the service flags,\n+and the synchronization rules of that protocol, are to be interpreted as\n+defined there.\n+\n+best chain\n+:   The best valid block chain, as described in section 3.3 of the Zcash\n+    Protocol Specification [^protocol-blockchain]; its *tip* is its highest\n+    block.\n+\n+validated tip\n+:   The highest block that a node has accepted into its best chain under the\n+    v2 protocol's synchronization rules.\n+\n+synchronization strategy\n+:   A procedure by which a node advances its validated tip toward the network\n+    chain tip, using the request streams of the v2 protocol.\n+\n+checkpoint\n+:   A (height, block hash) pair that a synchronizing node treats as\n+    authoritative for the block at that height.\n+\n+trusted commitment\n+:   As defined in the v2 protocol's checkpointed synchronization rules\n+    [^draft-p2p-v2]; extended in this ZIP to also bind, for snapshot\n+    synchronization, a state snapshot manifest.\n+\n+synchronization artifact\n+:   The immutable, content-addressed byte object served via `get-object`, as\n+    defined in the v2 protocol [^draft-p2p-v2] — for example, a known-hash\n+    chunk file or a snapshot piece.\n+\n+known-hash list\n+:   The block hashes of every height of a span of the chain, together with\n+    per-block sync-cost metadata, bound by a trusted commitment (for\n+    example, as the pinned hashes of a list of chunk artifacts).\n+\n+anchor\n+:   The anchor of a `get-block-range` stream, as defined in the v2 protocol\n+    [^draft-p2p-v2]; in this ZIP, always a block hash verified against a\n+    trusted commitment (directly, or via a verified artifact).\n+\n+work unit\n+:   A contiguous range of blocks (or a byte range of an artifact) scheduled\n+    for download as one request stream.\n+\n+snapshot\n+:   A serialization of a node's chain state at a *snapshot height* `H`,\n+    divided into a *manifest* and content-addressed *pieces*, allowing a new\n+    node to begin operating at height `H` without downloading or validating\n+    the chain below it.\n+\n+\n+# Abstract\n+\n+This ZIP recommends how a node synchronizes the block chain over the version\n+2 Zcash P2P network protocol. The v2 protocol defines the synchronization\n+primitives and the rules that any use of them must satisfy; this ZIP\n+recommends concrete *synchronization strategies* satisfying those rules —\n+headers-first synchronization, *checkpointed synchronization* over anchored\n+block-range streams, optionally accelerated by verified note commitment\n+tree roots, and *snapshot synchronization*, which makes a new node\n+operational in minutes — together with a download scheduling discipline and\n+the policy for selecting between the strategies.\n+\n+\n+# Motivation\n+\n+The v2 protocol [^draft-p2p-v2] deliberately specifies synchronization\n+primitives, the rules constraining their use, and only one baseline\n+procedure — headers-first synchronization. This ZIP recommends the concrete\n+procedures, because the difference between a naive procedure and a good one\n+is measured in days.\n+\n+Three costs dominate initial synchronization, in ascending order of what\n+removing them is worth:\n+\n+1. **Round trips and scheduling.** The legacy protocol transferred blocks in\n+   small per-request batches against individually requested hashes; at\n+   typical peer round-trip times this bounds throughput at a few hundred\n+   small blocks per second regardless of bandwidth. The v2 protocol's\n+   `get-block-range` removes the round trip from the inner loop: blocks\n+   stream continuously against a single verified anchor, and per-block\n+   download handles are unnecessary.\n+\n+2. **Validation CPU.** Under checkpointed synchronization, proofs and\n+   signatures below the final checkpoint are vouched for by the trusted\n+   commitment, and the dominant remaining CPU cost — measured at roughly\n+   70% of block-commit time in the Zebra implementation — is recomputing\n+   the note commitment trees, leaf by leaf, only to learn per-block roots\n+   that are already bound by header commitments. Verified tree roots\n+   (`get-tree-roots`) eliminate that recomputation without adding trust.\n+\n+3. **The historical data itself.** A new node operator's goal is a node at\n+   the tip; the chain's full history — hundreds of gigabytes — is the\n+   overwhelming share of bytes and time, and most operators need it only\n+   eventually (to serve peers) or never (pruned operation). Snapshot\n+   synchronization removes it from the critical path entirely: the node\n+   installs a verified state snapshot at a recent height and is operational\n+   in minutes, backfilling history in the background if it chooses.\n+\n+Checkpoints themselves also motivate this machinery. The Zebra\n+implementation compiles in known-hash lists covering every block — over 100\n+MB of hashes that grow with the chain — because the legacy protocol offered\n+no way to fetch and verify them. Under this ZIP, a binary compiles in only\n+the expected hashes of the list itself, chunked by height range (a few\n+kilobytes); the full known-hash list is then obtained verifiably from the\n+P2P network with `get-hashes`, as specified in\n+[Synchronization Data](#synchronizationdata).\n+\n+\n+# Specification\n+\n+The synchronization rules of the v2 protocol [^draft-p2p-v2] apply to every\n+strategy below and are not restated here.\n+\n+## Synchronization Data\n+\n+A node's trusted commitment binds the data its strategies rely on:\n+\n+- **Known-hash lists**, as pinned SHA-256 hashes of the list's *chunks*:\n+  each chunk is the canonical serialization of the `get-hashes` entries\n+  for a fixed height range. A node obtains the entries from its peers with\n+  `get-hashes` and verifies them by reassembling each chunk and comparing\n+  its hash against the commitment. The same bytes MAY instead be obtained\n+  as chunk artifacts from any source — bundled with the release,\n+  `get-object` requests to `NODE_SYNC_ARTIFACTS` peers, or out-of-band\n+  mirrors — accepted only if their hashes match the commitment. A\n+  verified known-hash list makes every listed hash an anchor, and its span\n+  metadata gives per-height sizes and costs for scheduling.\n+\n+  The commitment MAY additionally bind a single SHA-256 hash of the\n+  *spentness hints*: the hint bits for every transparent output created\n+  at or below the final checkpoint (see\n+  [Spentness Hints](#spentnesshints)), serialized in canonical order,\n+  obtained like any artifact and verified whole against this hash. The\n+  hints are committed separately from the entry chunks — one hash rather\n+  than one per range — because they are a function of the final\n+  checkpoint height as well as of the chain: extending the commitment's\n+  coverage would change every per-range hint hash anyway, while the\n+  entry-chunk hashes stay stable. A commitment from an older release\n+  simply covers a lower final checkpoint: its hints remain valid for that\n+  shorter span, and the node synchronizes above it normally.\n+- **A snapshot manifest hash**, for snapshot synchronization (see\n+  [Snapshot Synchronization](#snapshotsynchronization)).\n+\n+Chunk artifacts are deterministic functions of the chain, so any node that\n+has synchronized the covered span can regenerate them byte-identically and\n+serve them; artifact pieces SHOULD be no larger than the `get-object`\n+per-request maximum [^draft-p2p-v2] so each piece is independently fetchable\n+and verifiable.\n+\n+## Recommended Strategies\n+\n+A node implements headers-first synchronization (the v2 protocol's fallback\n+rule requires it) and MAY implement the others.\n+\n+### Headers-First Synchronization\n+\n+Headers-first synchronization is specified in the v2 protocol\n+[^draft-p2p-v2], where it is characterized as the baseline, full-validation\n+method. It is also the strategy for the chain near the tip, above the reach\n+of commitments, whatever strategy covered the history below.\n+\n+### Checkpointed Synchronization\n+\n+A node MAY synchronize the span of the block chain covered by a verified\n+known-hash list as follows:\n+\n+1. **Obtain and verify the known-hash list** for the target span (see\n+   [Synchronization Data](#synchronizationdata)).\n+\n+2. **Partition the span into work units**: contiguous block ranges, sized\n+   using the span metadata toward a byte target (see\n+   [Download Scheduling](#downloadscheduling)), each ending at a listed\n+   height. Any listed hash can end a unit, so unit boundaries are chosen\n+   freely — by bytes, not by a fixed block count — and each unit's\n+   request carries the byte target as its `max_bytes` bound.\n+\n+3. **Stream each unit with `get-block-range`**, anchored at the unit's\n+   final hash. Each delivered block is verified on arrival under the v2\n+   protocol's `get-block-range` delivery rules [^draft-p2p-v2] and — as a\n+   cross-check — matched against the known-hash list at its height. A\n+   truncated or cancelled unit keeps its verified prefix, and the\n+   remainder is re-requested from any peer, anchored at the next expected\n+   hash, as the v2 protocol's `get-block-range` truncation rules provide.\n+\n+4. **Commit** each completed range in height order, applying whatever\n+   abbreviated validation local policy permits for\n+   checkpoint-authenticated blocks under the v2 protocol's validated\n+   advancement rule — with tree work eliminated where\n+   [Verified Tree Roots](#verifiedtreeroots) are available.\n+\n+A node with a commitment that binds only spaced checkpoint hashes (rather\n+than a full known-hash list) applies the same procedure with units\n+anchored at checkpoint heights; interior per-block cross-checks and\n+advance sizing are then unavailable, but neither is needed — steps 2 and 3\n+already bound each unit in bytes and authenticate every delivered block\n+against its anchor.\n+\n+### Verified Tree Roots\n+\n+A node MAY avoid the note commitment tree recomputation that dominates the\n+CPU cost of committing checkpoint-authenticated blocks (see\n+[Motivation](#motivation)) by obtaining each historical block's tree roots\n+with `get-tree-roots` from `NODE_TREE_ROOTS` peers and verifying them\n+against header commitments:\n+\n+1. For each block in the span, obtain the entry (Sapling, Orchard, and\n+   Ironwood roots, shielded transaction counts, and authorizing data\n+   commitment), anchoring each request's `final_hash` at a hash the node\n+   has already authenticated, so that entries describe known blocks\n+   rather than the responder's view of a best chain.\n+2. Verify the entries against headers that checkpointed synchronization has\n+   authenticated. Which check applies depends on what the block's header\n+   commits to at that height, which changes at Heartwood — *not* at NU5:\n+\n+   - **Between Sapling activation and Heartwood activation**, the fourth\n+     header field (`hashFinalSaplingRoot`) is the block's Sapling note\n+     commitment tree root. Each supplied Sapling root is checked directly\n+     against it.\n+   - **From Heartwood activation**, ZIP 221 [^zip-0221] repurposes that\n+     field to carry the chain history root (`hashChainHistoryRoot`, and\n+     from NU5 `hashBlockCommitments`, which binds the chain history root\n+     together with the authorizing data commitment of ZIP 244\n+     [^zip-0244]). A direct comparison against a note commitment tree root\n+     is therefore *not* a valid check anywhere above Heartwood. The node\n+     instead rebuilds the chain history tree from the supplied roots and\n+     counts and checks the rebuilt values against each header. Each\n+     block's header commits to the history tree containing its *parent's*\n+     data, so an entry is verified only once the *following* block's\n+     header has been checked against it.\n+   - **Before a pool's activation** at the entry's height, the entry's\n+     root field is 32 zero bytes and its count is 0, as the v2 protocol\n+     requires [^draft-p2p-v2]; an entry that deviates fails verification.\n+   - **A root that no header commits to** at the entry's height is not\n+     verifiable by this procedure. The chain history tree leaf of ZIP 221\n+     binds the Sapling and (from NU5) Orchard roots, but defines no\n+     Ironwood field, so no header authenticates an Ironwood root.\n+\n+3. A **verified** root replaces the recomputation: the node adopts the\n+   root, maintains tree *frontiers* only at strategy boundaries (where\n+   full per-block validation resumes), and stores what its serving\n+   obligations require. A node MUST NOT adopt a root that the procedure of\n+   step 2 has not verified against an authenticated header, and MUST\n+   recompute that pool's tree from the blocks' note commitments instead.\n+   Two cases in particular are not verified by an entry's own arrival:\n+\n+   - Because verification above Heartwood runs one block behind, the\n+     highest entry of a response is unverified until the entry for the\n+     following block is obtained and checked against its header. A node\n+     either extends the requested range by one entry or holds the highest\n+     entry unadopted.\n+   - Entries MAY be obtained and verified in any order, but each entry is\n+     adopted only when its own check passes; an entry at the boundary of a\n+     range is not adopted merely because its neighbours were.\n+\n+4. An entry that fails verification is discarded with a misbehavior penalty\n+   (per the v2 protocol); the node falls back to recomputing that span's\n+   trees locally.\n+\n+No additional trust is involved: every adopted root is authenticated by a\n+header that is itself authenticated by the trusted commitment, and a root\n+that no header authenticates is never adopted.\n+\n+**Frontier artifacts.** Adopting verified roots leaves the node without\n+the tree *frontiers* it needs to resume appending note commitments where\n+full validation begins. The trusted commitment MAY bind the hash of a\n+*frontier artifact* — the serialized Sapling, Orchard, and Ironwood\n+frontiers at the final checkpoint — obtained like any artifact. A\n+frontier whose recomputed root matches the root verified for that height\n+by the procedure above is adopted, so the binding itself carries no\n+trust: a wrong frontier fails the root comparison and the node falls\n+back to rebuilding that pool's tree from the span's note commitments.\n+The Ironwood frontier cannot be verified this way (no header\n+authenticates an Ironwood root); it is authenticated only by the\n+commitment's hash, or rebuilt from the pool's note commitments — a\n+bounded cost, since the pool postdates NU6.3 activation.\n+\n+### Spentness Hints\n+\n+After tree work is eliminated, the dominant remaining state cost of\n+committing historical blocks is random access: looking up and deleting each\n+transparent input's spent output, and inserting (and, under full\n+validation, membership-testing) each revealed nullifier. Spentness hints —\n+adapted from Bitcoin's SwiftSync [^swiftsync] — eliminate the random access\n+and make historical commits order-independent, at the cost of a small hint\n+bitmap bound by the trusted commitment, which vouches for its correctness\n+exactly as it vouches for the known block hashes: the bitmap is a\n+deterministic function of the chain, regenerable and auditable by any\n+synchronized node, and its pinned hash is updated by each release that\n+updates the checkpoints.\n+Hints apply only within the reach of the trusted commitment: a node MUST\n+NOT apply them above the final checkpoint.\n+\n+**Transparent outputs.** The spentness hints (see\n+[Synchronization Data](#synchronizationdata)) carry one bit per\n+transparent output created at or below the *terminal height* — the final\n+checkpoint — in canonical order (by block height, then transaction index,\n+then output index): whether the output is still unspent at that height.\n+The `txouts` metadata of the verified entry chunks gives each height's bit\n+count, so a node can check the bitmap's length before downloading any\n+block bodies, and a work unit anywhere in the span can locate its first\n+bit without processing the blocks below it. During the span:\n+\n+- An output hinted *unspent* is written to the transparent UTXO set as it\n+  is created — and, during the span, never looked up or deleted.\n+- An output hinted *spent* is never stored.\n+- Transparent inputs perform no lookup at all.\n+\n+The constructed UTXO set is then exactly the set of outputs unspent at\n+the terminal height, without a single random read: every output hinted\n+spent is one the committed chain spends within the span, on the\n+authority of the commitment.\n+\n+**Nullifiers.** The nullifier sets never shrink, so no hint is needed at\n+all: revealed nullifiers are simply inserted as they are encountered —\n+insertion is sequential-write work, not the random reads the hints exist\n+to remove — and per-spend membership tests below the final checkpoint\n+are vouched for by the trusted commitment. When backfilling below a\n+snapshot, the reconstructed sets are compared with the snapshot's at `H`\n+(see [Snapshot Synchronization](#snapshotsynchronization)).\n+\n+**Value pools.** No lookups are needed for value tracking either: shielded\n+pool balances are sums of the blocks' value balance fields, which commute,\n+and the transparent pool balance at the terminal height is the sum of the\n+constructed UTXO set's amounts. These are consensus quantities: they MUST\n+be accumulated with checked arithmetic that detects overflow.\n+\n+**What order-independence does and does not cover.** The state construction\n+above reads no state written by an earlier block, so the *construction* —\n+the UTXO set, the nullifier sets, and the value pool\n+totals — is order-independent, and blocks and ranges MAY be verified and\n+committed in any order within the span. The per-input reads and per-output\n+deletes disappear entirely.\n+\n+Order-independence does not extend to consensus rules whose outcome is not\n+a function of the terminal totals. A node applying hints MUST account for\n+each of the following, either by checking it in height order or by\n+recording it as covered by the trusted commitment under the v2 protocol's\n+validated advancement rule:\n+\n+- **Non-negative pool balances at every block.** ZIP 209 [^zip-0209]\n+  requires rejecting a block at which any chain value pool balance would\n+  become negative. A terminal total says nothing about a running balance\n+  that dips negative at an interior height and recovers; below the final\n+  checkpoint this rule is among those vouched for by the trusted\n+  commitment, and above it, blocks are committed in height order under\n+  full validation.\n+- **Coinbase maturity and the pre-NU5 shielded-coinbase rule**, which\n+  depend on the creation height and coinbase status of the *spent* output\n+  — exactly the lookup that hints remove. This is why hints are confined\n+  to the reach of the trusted commitment, where these per-spend checks\n+  are vouched for; above the final checkpoint, blocks are committed in\n+  height order with the lookups performed.\n+\n+A node SHOULD obtain and verify the hint bitmap — its hash against the\n+commitment, and its bit count against the span's transparent output\n+total, summed from the verified entry chunks' `txouts` metadata — before\n+processing any block bodies with hints, so that an unavailable or\n+malformed bitmap costs nothing but its fetch; a bitmap that fails either\n+check is discarded and the span is synchronized without hints. A\n+published bitmap that is wrong despite matching its pinned hash is a\n+defect of the commitment itself, exactly as a wrong checkpoint hash would\n+be; because the bitmap is deterministic, publishers and third parties can\n+regenerate and cross-check it, and release processes SHOULD do so.\n+\n+### Snapshot Synchronization\n+\n+A node with no chain state MAY become operational at a *snapshot height*\n+`H` in minutes, deferring or omitting the history below it:\n+\n+1. **Obtain the manifest** whose SHA-256 hash is bound by the trusted\n+   commitment, from any source (`get-object`, mirrors, bundled). The\n+   manifest lists the snapshot's content-addressed pieces and their sizes.\n+2. **Fetch and verify the pieces** from\n+   `NODE_SYNC_ARTIFACTS` peers via `get-object` and/or out-of-band mirrors,\n+   scheduling them like any other work units (see\n+   [Download Scheduling](#downloadscheduling)); a piece is accepted only if\n+   its hash matches the manifest.\n+3. **Verify the state against the chain where possible.** The snapshot\n+   contains, at minimum: the transparent UTXO set; the Sprout, Sapling,\n+   Orchard, and Ironwood nullifier sets; the note commitment tree frontiers\n+   of each pool; the chain history tree; and the chain value pool\n+   balances, which the node needs as the starting point for the\n+   pool-balance consensus rules above `H`.\n+   The parts that the chain commits to — the Sapling, Orchard, and Ironwood\n+   frontiers (via their header-committed roots) and the chain history tree\n+   (via `hashBlockCommitments` of the following block, ZIP 221 [^zip-0221])\n+   — MUST be verified against headers authenticated by the trusted\n+   commitment. Because the chain history tree at `H` is committed by the\n+   *following* block's header, the trusted commitment MUST authenticate\n+   the header at height `H + 1` for that verification to be possible; a\n+   commitment that stops at `H` leaves the chain history tree unverified,\n+   and the node MUST treat it as an uncommitted component. The parts the\n+   chain does not commit to (the transparent UTXO set, the nullifier sets,\n+   the Sprout tree, and the chain value pool balances) are authenticated\n+   solely by the manifest commitment, which therefore carries the same\n+   trust as the node's binary — the trust model of the checkpoints\n+   themselves.\n+4. **Begin operating at `H`**: validate blocks above `H` under the\n+   consensus rules (or checkpointed synchronization, where commitments\n+   reach above `H`), participate fully in relay, and serve the blocks it\n+   accumulates, advertising service flags as the v2 protocol requires of a\n+   node that lacks the complete chain [^draft-p2p-v2].\n+5. **Backfill in the background.** The node SHOULD backfill the history\n+   below `H` using checkpointed synchronization, at low priority, and\n+   advertise `NODE_NETWORK` when complete. On completion, backfill\n+   reconciles the snapshot's uncommitted components against the chain the\n+   node has just replayed. (The hint bitmap's bits are relative to the\n+   final checkpoint rather than `H`, so backfill below a snapshot\n+   proceeds without transparent hints; as background work, it is not on\n+   the critical path that hints exist to shorten.)\n+\n+   The reconciliation discharges the snapshot's trust only to the extent\n+   that each component is actually compared, so each MUST be covered\n+   explicitly, and over complete entries: a comparison over outpoints\n+   alone would let a fabricated or amount-inflated UTXO entry pass\n+   unnoticed. Backfill therefore verifies, at `H`:\n+\n+   - **The transparent UTXO set**, compared as complete entries — the\n+     outpoint, the value, the `scriptPubKey`, the creation height, and\n+     the coinbase flag, in a canonical encoding — between the set the\n+     replay constructs and the set the snapshot supplied, so that a\n+     phantom outpoint, an inflated value, or an altered script is caught.\n+   - **The nullifier sets**, by the same whole-value comparison per pool.\n+   - **The Sprout note commitment tree**, which no header commits to, by\n+     recomputing it from the JoinSplit note commitments of the replayed\n+     blocks and comparing the resulting root and frontier. Sprout is a\n+     closed pool of bounded size, so this is inexpensive.\n+   - **The chain value pool balances**, by recomputing them over the\n+     replayed span (with checked arithmetic, see\n+     [Spentness Hints](#spentnesshints)) and comparing them to the\n+     snapshot's.\n+\n+   When all four check out, the snapshot's uncommitted components have been\n+   verified against the chain and the node's trust base is that of a fully\n+   synchronized node. Until they do, they remain trusted on the strength of\n+   the commitment. A mismatch in any of them means the snapshot was wrong:\n+   the node discards it and falls back to checkpointed synchronization from\n+   genesis. A node MAY instead operate indefinitely without deep history\n+   (pruned operation), never advertising `NODE_NETWORK`; such a node never\n+   discharges the snapshot's trust.\n+\n+Snapshot heights SHOULD lie at least the v2 protocol's reorganization\n+margin below the network tip at the time the commitment is published, and\n+snapshot artifacts SHOULD be deterministic functions of the chain state at\n+`H`, so that any node of the same implementation that reaches `H` can\n+regenerate and serve them. A wrong or corrupted snapshot cannot cause\n+acceptance of a false state — a piece that does not match the manifest, or\n+a frontier that fails header verification, is rejected and the node falls\n+back to checkpointed synchronization; the commitment's only unverifiable\n+claims are those with no on-chain commitment (see step 3). (This is the trust\n+model of Bitcoin's assumeutxo [^assumeutxo] and of Ethereum's era archives\n+[^era1], with the difference that Zcash's header commitments make the tree\n+components independently verifiable.)\n+\n+## Download Scheduling\n+\n+Bulk download is a scheduling problem: divide known work across untrusted\n+peers of unknown and changing capacity, so that aggregate throughput\n+approaches the client's link capacity and no single peer can stall\n+progress. A node SHOULD apply the following discipline to bulk transfers\n+(`get-block-range` units and `get-object` pieces):\n+\n+- **Work units of 16–64 MiB**, realized for block ranges by the request's\n+  `max_bytes` bound (with the known-hash span metadata, where available,\n+  used to plan unit boundaries in advance), and for artifacts by manifest\n+  piece sizes. Units near the commit frontier SHOULD be smaller, so the\n+  frontier advances promptly; deep lookahead units MAY be larger.\n+- **Pull-based assignment with per-peer budgets.** Maintain an estimate of\n+  each peer's delivery rate (for example, an exponentially weighted moving\n+  average); let each peer's connection pull the next unassigned unit\n+  whenever its in-flight bytes fall below roughly 2–3 seconds of its\n+  estimated rate. Fast peers thereby draw more work with no explicit\n+  balancing step.\n+- **Concurrency across peers.** Per-connection throughput is typically\n+  bounded by a single CPU core (see the v2 protocol's bulk transfer\n+  guidance [^draft-p2p-v2]); a node SHOULD download from at least 4–8\n+  peers concurrently, preferring `NODE_NETWORK` peers for deep history and\n+  respecting the flow control window guidance of the v2 protocol.\n+- **Bounded lookahead.** In-flight plus downloaded-but-uncommitted data\n+  SHOULD be bounded in bytes (a budget on the order of 256 MiB to 1 GiB),\n+  not in blocks; block counts calibrated to near-tip synchronization are\n+  meaningless across eras whose block sizes differ by three orders of\n+  magnitude.\n+- **Stall detection, twice over.** Cancel (with `CANCELLED`) and reassign a\n+  unit whose peer delivers no bytes for a few seconds, falls below an\n+  absolute configured rate floor, or sustains a rate far below the current\n+  peer median (for example, a quarter). Cancelled block-range units keep\n+  their verified prefix and are resumed per step 3 of\n+  [Checkpointed Synchronization](#checkpointedsynchronization).\n+- **End-game duplication.** When unassigned work runs out before the\n+  fastest peers do, assign remaining units redundantly to the fastest\n+  peers and cancel the losers; this bounds the tail of the download at the\n+  cost of at most one duplicate unit per peer.\n+- **Exact blame.** A block failing its arrival check is attributable with\n+  certainty to the delivering peer, as is an artifact piece that fails\n+  its hash after being delivered entirely by one peer; both are handled\n+  per the v2 protocol's error and misbehavior rules, and the failed unit\n+  is reassigned elsewhere. A piece assembled from ranges delivered by\n+  several peers is *not* attributable when it fails its hash — the v2\n+  protocol forbids assigning a penalty in that case — so the node\n+  re-fetches the piece with each candidate peer serving it whole, which\n+  isolates a misbehaving peer; a scheduler that assigns each piece to a\n+  single peer (as the unit sizes above make natural) keeps blame exact\n+  from the start.\n+\n+## Strategy Selection\n+\n+A synchronizing node SHOULD proceed as follows:\n+\n+1. A new node with no chain state, holding a snapshot commitment, SHOULD\n+   use snapshot synchronization: it is operational after transferring the\n+   snapshot (gigabytes) rather than the history (hundreds of gigabytes),\n+   and no other strategy changes what the node ultimately trusts.\n+2. For spans covered by known-hash commitments — backfill below a\n+   snapshot, or the whole chain when no snapshot is used — it SHOULD use\n+   checkpointed synchronization, with verified tree roots where peers\n+   serve them and spentness hints where its commitment (or snapshot)\n+   provides them.\n+3. Above the reach of its commitments, and within the reorganization\n+   margin of the tip, it uses headers-first synchronization.\n+4. Every strategy degrades toward headers-first synchronization when its\n+   prerequisites are unavailable (no artifacts obtainable, no serving\n+   peers, verification failures), per the v2 protocol's fallback rule;\n+   scarcity of `NODE_TREE_ROOTS` or `NODE_SYNC_ARTIFACTS` peers slows\n+   synchronization but never blocks it.\n+\n+\n+# Security and Privacy Considerations\n+\n+The security properties of the strategies are analyzed in the v2 protocol's\n+security considerations [^draft-p2p-v2]. All strategies here satisfy its\n+synchronization rules, so the choice between them — including every\n+fallback — affects performance, not the integrity of the resulting chain.\n+The trust base is unchanged by acceleration: every accelerating input is\n+either authenticated against the chain or bound by the trusted commitment,\n+and the only data trusted without chain verification\n+(the spentness hints and the snapshot's uncommitted state components)\n+carries exactly the trust of\n+the binary that shipped the commitment, as the checkpoints already do.\n+The snapshot components' trust is discharged, for each component the\n+reconciliation of\n+[Snapshot Synchronization](#snapshotsynchronization) step 5 actually\n+compares, when backfill completes; a node that never backfills never\n+discharges it. The spentness hints stand or fall with the commitment\n+itself, exactly as the checkpoint hashes do, and are auditable in the\n+same way: the bitmap is a deterministic function of the chain that any\n+synchronized node can regenerate and cross-check. Malicious\n+synchronization data from peers produces failure, not\n+fraud: every verification failure is detected and routed around, and is\n+attributed and penalized where blame is provable under the v2 protocol's\n+misbehavior rules. (One failure is deliberately *not* attributed: an\n+assembled artifact\n+piece whose ranges came from several peers — see\n+[Download Scheduling](#downloadscheduling).)\n+\n+**Serving capacity.** Snapshot-synchronized and pruned nodes cannot serve\n+deep history; if they became the majority, historical blocks and artifacts\n+could become scarce. The service flags let nodes advertise what they serve,\n+operators of full nodes are encouraged to retain `NODE_NETWORK` and\n+`NODE_SYNC_ARTIFACTS`, and artifacts remain regenerable by any backfilled\n+node and servable from out-of-band mirrors.\n+\n+**Fingerprinting.** The heights, ranges, and units a node requests reveal\n+its implementation, configuration, and synchronization progress. This is\n+comparable to the fingerprinting surface of headers-first synchronization\n+and of the `user_agent` field, and carries the same mitigation: nodes\n+concerned about it can align their request patterns with common\n+implementations.\n+\n+\n+# Deployment\n+\n+This ZIP depends on the v2 protocol [^draft-p2p-v2], which specifies the\n+request streams, service flags, and synchronization rules used here. No\n+version gating is required beyond the v2 protocol's own deployment: each\n+primitive is refused by nodes that do not serve it, and every strategy\n+degrades gracefully (see [Strategy Selection](#strategyselection)).\n+Distribution can be purely in-band: the known-hash list is reassembled\n+via `get-hashes`, and hint, frontier, and snapshot artifacts are served\n+by `NODE_SYNC_ARTIFACTS` peers via `get-object` — which requires that\n+seeders and any configured initial peers include such nodes.\n+Implementations MAY additionally bundle current artifacts with releases,\n+or serve them from out-of-band HTTPS mirrors interchangeably with\n+`get-object` peers.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^protocol-blockchain]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.3: The Block Chain](protocol/protocol.pdf#blockchain)\n+\n+[^draft-p2p-v2]: [Draft ZIP: Version 2 Zcash P2P Network Protocol](draft-arya-jvff-p2p-quic-transport.md)\n+\n+[^zip-0209]: [ZIP 209: Prohibit Negative Shielded Chain Value Pool Balances](zip-0209.rst)\n+\n+[^zip-0221]: [ZIP 221: FlyClient - Consensus-Layer Changes](zip-0221.rst)\n+\n+[^zip-0244]: [ZIP 244: Transaction Identifier Non-Malleability](zip-0244.rst)\n+\n+[^assumeutxo]: [Bitcoin Core: assumeutxo design and usage](https://github.com/bitcoin/bitcoin/blob/master/doc/assumeutxo.md)\n+\n+[^swiftsync]: [Ruben Somsen: SwiftSync — speeding up IBD with pre-generated hints](https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd)\n+\n+[^era1]: [era1 archival file format specification](https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era1.md)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-arya-block-chain-sync.md","title":"Block Chain Synchronization","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"synchronization-data","text":"Synchronization Data"},{"level":2,"id":"recommended-strategies","text":"Recommended Strategies"},{"level":3,"id":"headers-first-synchronization","text":"Headers-First Synchronization"},{"level":3,"id":"checkpointed-synchronization","text":"Checkpointed Synchronization"},{"level":3,"id":"verified-tree-roots","text":"Verified Tree Roots"},{"level":3,"id":"spentness-hints","text":"Spentness Hints"},{"level":3,"id":"snapshot-synchronization","text":"Snapshot Synchronization"},{"level":2,"id":"download-scheduling","text":"Download Scheduling"},{"level":2,"id":"strategy-selection","text":"Strategy Selection"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1431,"title":"Add ZIP draft \"Zcash Adoption Pool (ZAP)\"","author":"daira","createdAt":"2026-10-31T17:52:35Z","updatedAt":"2026-11-04T00:24:16Z","url":"https://github.com/zcash/zips/pull/1431","diffUrl":"https://github.com/zcash/zips/pull/1431.diff","headRef":"draft-dairaemma-governance-zap","headRepo":"daira/zips","headSha":"e0677267f46c4de9dec28146132cba4668161cb2","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Reaffirm the portion of Protocol-defined Ecosystem Funding allocated to Zcash Community Grants (ZCG), and focus on its original goal described [in ZIP 1015](https://zips.z.cash/zip-1015#zcash-community-grants-zcg) — to accelerate Zcash adoption as broadly as possible. To better reflect that focus an","zipRefs":[1015],"draftRefs":["draft-dairaemma-governance-zap"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -231,6 +231,7 @@ be deleted.\n     <tr> <th>Draft name</th> <th>Title</th> <th>Discussions-To</th> </tr>\n     <tr> <td class=\"left\">draft-arya-dairaemma-disable-addition-of-transparent-chain-value</td> <td class=\"left\"><a href=\"zips/draft-arya-dairaemma-disable-addition-of-transparent-chain-value.md\">Disabling Addition of New Value to the Transparent Chain Value Pool</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1115\">zips#1115</a></td>\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n+    <tr> <td class=\"left\">draft-dairaemma-governance-zap</td> <td class=\"left\"><a href=\"zips/draft-dairaemma-governance-zap.md\">Zcash Adoption Pool (ZAP)</a></td> <td class=\"left\"><a href=\"https://forum.zcashcommunity.com/t/zcash-adoption-pool-zap/58656\">https://forum.zcashcommunity.com/t/zcash-adoption-pool-zap/58656</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>"},{"filename":"zips/draft-dairaemma-governance-zap.md","status":"added","additions":198,"deletions":0,"patch":"@@ -0,0 +1,198 @@\n+\n+    ZIP: Unassigned\n+    Title: Zcash Adoption Pool (ZAP)\n+    Owners: Daira-Emma Hopwood <daira@jacaranda.org>\n+    Credits: Violet Rollergirl\n+             Blake Benthall (emersonian)\n+             Mylo Bennett\n+             Alison Stevenson\n+    Status: Draft\n+    Category: Consensus Process\n+    Created: 2026-10-30\n+    License: MIT\n+    Discussions-To: <https://forum.zcashcommunity.com/t/zcash-adoption-pool-zap/58656>\n+    Pull-Request: <https://github.com/zcash/zips/pull/1431>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\" and \"SHALL\" in this document are to be interpreted as\n+described in BCP 14 [^BCP14] when, and only when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as\n+defined in the Zcash protocol specification [^protocol-networks].\n+\n+The terms \"Zcash Community Grants\" (or \"ZCG\"), \"ZCG Committee\", and \"Deferred\n+Dev Fund Lockbox\" are to be interpreted as defined in ZIP 1015 [^zip-1015].\n+\n+The terms \"Protocol-defined Ecosystem Funding\" and \"Coinholder-Controlled Fund\"\n+are to be interpreted as defined in ZIP 1016 [^zip-1016].\n+\n+“ZEC” refers to the native currency of Zcash on Mainnet.\n+\n+\n+# Abstract\n+\n+Reaffirm the portion of Protocol-defined Ecosystem Funding allocated to\n+Zcash Community Grants (ZCG), and focus on its original goal described in\n+ZIP 1015 [^zip-1015-zcg] — to accelerate Zcash adoption as broadly as\n+possible. To better reflect that focus and to clarify the original intent\n+of ZIP 1015, this portion of funds will be called the \"Zcash Adoption Pool\".\n+\n+\n+# Motivation\n+\n+As specified in ZIP 1015:\n+\n+> The stream allocated to Zcash Community Grants (ZCG) is intended to fund\n+> independent teams entering the Zcash ecosystem, to perform major ongoing\n+> development (or other work) for the public good of the Zcash ecosystem, to the\n+> extent that such teams are available and effective. The ZCG Committee is given\n+> the discretion to allocate funds not only to major grants, but also to a\n+> diverse range of projects that advance the usability, security, privacy, and\n+> adoption of Zcash, including community programs, dedicated resources, and other\n+> projects of varying sizes.\n+\n+There is no shortage of projects that fulfil these criteria and that are in\n+desperate need of funding.\n+\n+## Background\n+\n+Throughout the history of the Zcash project, there have been periodic calls to\n+abandon support for independent teams entering the Zcash ecosystem. This has\n+presented itself in a variety of forms with numerous arguments. At no point in\n+Zcash's history, however, has there ever been a lack of use cases for programmes\n+and initiatives that utilize shielded ZEC.\n+\n+Most recently, calls for the abolition of the ZCG have made absurd arguments,\n+including that there is more money in the Zcash Community Grants portion of the\n+\"dev\" fund than is required for \"development\". This argument presupposes that\n+\"development\" should be limited to technical protocol development, which is a\n+clear misinterpretation of both the letter of ZIP 1015 (as quoted above), and\n+its original intent. That intent was not changed by ZIP 1016. Notably, these\n+proposals have not stipulated any change to the Coinholder-Controlled Fund —\n+which in practice would result in a substantial shift of power toward large\n+holders.\n+\n+Another such argument is that development work can be funded via other means,\n+such as private equity, and therefore \"should\" be funded by private equity. The\n+authors of this ZIP do not object to funding from private equity. Rather, this\n+ZIP reaffirms the utility of funding programmes that private equity has\n+evidently *not* funded, and for which no other reliable source of funding\n+currently exists.\n+\n+There are many programmes that may apply for ZCG grants whose source for funds\n+is at risk, but for whom support will undeniably increase the adoption rate of\n+Zcash. That is precisely what the \"dev\" fund was designed to fund.\n+\n+To clarify this in literal terms, this ZIP therefore renames the pool of funds\n+and the grant programme funded via the `FS_FPF_ZCG_H3` slice of Protocol-defined\n+Ecosystem Funding to explicitly include the word \"adoption\", in order to avoid\n+further misinterpretation or the possibility of future misappropriation of the\n+funds previously allocated for purposes of broader Zcash and ZEC adoption.\n+\n+\n+# Requirements\n+\n+All requirements of ZIP 1015 continue to apply; in particular:\n+\n+* There is a well-defined, publically agreed, process for evaluation and\n+  community feedback on grant proposals.\n+* Any use of Deferred Dev Fund Lockbox funds is consistent with the purpose\n+  specified in [^zip-1015-lockbox] of \"funding grants to ecosystem participants\".\n+\n+In addition:\n+\n+* Funds disbursed through the Zcash Adoption Pool are to be further disbursed for\n+  the explicit purpose of increasing the number of human individuals who have\n+  direct holdings in ZEC or who directly transact in ZEC on the Zcash network.\n+  The purpose of this is to further broaden the holding of ZEC and increase the\n+  velocity of its use as a currency. This is what *adoption* means.\n+\n+\n+# Non-requirements\n+\n+* Pleasing everyone.\n+\n+\n+# Specification\n+\n+For avoidance of doubt, the `FS_FPF_ZCG_H3` funding stream SHALL continue at its\n+existing value and for the duration specified in ZIP 214 Revision 3 [^zip-0214].\n+This applies to Mainnet and Testnet.\n+\n+The pool of funds associated with this stream SHALL be renamed to\n+\"Zcash Adoption Pool\" (\"ZAP\"), and the grant programme by which it is disbursed\n+to \"Zcash Adoption Pool Grants\" (\"ZAP Grants\"). Assets associated with Zcash\n+Community Grants (past, present, or future) therefore become assets associated\n+with the Zcash Adoption Pool; requirements on their administration specified in\n+ZIPs 1015 and 1016 [^zip-1015] [^zip-1016] are unchanged. The ZCG Committee SHALL\n+be referred to as the ZAP Committee.\n+\n+The ZAP Grant assessment process MUST obtain reasonable confidence, on the evidence\n+in the application, that funds disbursed through Zcash Adoption Pool Grants will\n+be further disbursed for the explicit purpose of increasing the number of human\n+individuals who have direct holdings in ZEC or who directly transact in ZEC on the\n+Zcash network. The purpose of this is to further broaden the holding of ZEC and\n+increase the velocity of its use as a currency.\n+\n+## Examples\n+\n+Examples of end-recipients who might receive ZEC as a result of grants approved\n+under this programme include:\n+\n+* sex workers who are debanked or require electronic payment means otherwise\n+  unavailable to them, and their clients that require payment discretion;\n+* civil society organizations who struggle to maintain funding;\n+* privacy projects and contributors in need of legal defence;\n+* technical development projects that would similarly struggle to\n+  receive funding from private investments (a past example would be Arti, an\n+  implementation of Tor in Rust);\n+* people protesting surveillance or restrictions on their speech, movement, and\n+  freedom of association;\n+* abortion funds;\n+* people in need of humanitarian aid and disaster relief, e.g. across borders;\n+* communities threatened by genocide or forced displacement;\n+* debanked individuals;\n+* remittance recipients;\n+* victims of abuse;\n+* homeless people;\n+* trans people attempting to access hormones;\n+* refugees;\n+* people unable to obtain food or medical care due to discrimination;\n+* people in need of education in underfunded nations;\n+* publishers catering to underserved groups or to political views opposing\n+  discrimination;\n+* projects developing accessibility features, or localization to minority\n+  languages;\n+* small businesses choked by excessive payment processing fees;\n+* people and businesses unable to access the financial system due to payment\n+  processor discrimination and censorship.\n+\n+Some of these examples are intentionally provocative. All can be funded\n+entirely legally in most jurisdictions. They are presented here to highlight\n+the requirement that Zcash remain **for everyone**.\n+\n+\n+# Acknowledgements\n+\n+The authors would like to thank the attendees of the Zcon7 conference for\n+discussions leading to this proposal, and the organizers of the conference\n+for the space to discuss it in person.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^zip-0214]: [ZIP 214: Consensus rules for a Zcash Development Fund](zip-0214.rst)\n+\n+[^zip-1015]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding](zip-1015.rst)\n+\n+[^zip-1015-zcg]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding — Zcash Community Grants](zip-1015#zcash-community-grants-zcg)\n+\n+[^zip-1015-lockbox]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding — Lockbox](zip-1015#lockbox)\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](zip-1016.md)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-dairaemma-governance-zap.md","title":"Zcash Adoption Pool (ZAP)","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":2,"id":"background","text":"Background"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"examples","text":"Examples"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1423,"title":"Add draft ZIP: Winding Down Zcash Community Grants","author":"thowar2","createdAt":"2026-10-29T07:00:25Z","updatedAt":"2026-11-03T23:48:55Z","url":"https://github.com/zcash/zips/pull/1423","diffUrl":"https://github.com/zcash/zips/pull/1423.diff","headRef":"codex/wind-down-zcg","headRepo":"thowar2/zips","headSha":"425f849ff7008b55cfc7a4441215d51167535ab6","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Adds an unnumbered Consensus Process ZIP to wind down Zcash Community Grants at a designated Mainnet network upgrade: stop new awards, complete existing commitments, and contribute residual funds to the NSM. The draft specifies amendments to ZIPs 1015 and 1016, legal prerequisites for FPF, reserve a","zipRefs":[],"draftRefs":["draft-howard-wind-down-zcg"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-howard-wind-down-zcg.md","status":"added","additions":571,"deletions":0,"patch":"@@ -0,0 +1,571 @@\n+    ZIP: Unassigned\n+    Title: Winding Down Zcash Community Grants\n+    Owners: Tom Howard <tom@zcashlabs.org>\n+    Credits: Maxime Desalle\n+    Status: Draft\n+    Category: Consensus Process\n+    Created: 2026-10-28\n+    License: MIT\n+    Updates: 1015, 1016\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to\n+be interpreted as described in BCP 14 [^BCP14] when, and only when, they appear\n+in all capitals.\n+\n+\"Zcash Community Grants\" (\"ZCG\"), \"ZCG Committee\", and \"Financial Privacy\n+Foundation\" (\"FPF\") have the meanings given in ZIP 1015. [^zip-1015]\n+\n+\"Coinholder-Controlled Fund\" (\"CCF\") has the meaning given in ZIP 1016.\n+[^zip-1016]\n+\n+\"Network Sustainability Mechanism\" (\"NSM\") and \"NSM Value Balance\" refer to\n+the mechanism and balance specified in ZIP 237. [^zip-0237] A contribution to\n+the NSM removes ZEC from circulation for subsequent reissuance under the\n+protocol; it is not a payment to an organization or a treasury address.\n+\n+\"Wind-down Date\" means the UTC calendar day of Mainnet activation of the\n+network upgrade that deploys this ZIP.\n+\n+\"ZCG funds\" means assets held or administered by FPF for the ZCG program,\n+including its grant and Discretionary Budget balances, assets subsequently\n+received for that program, grant refunds, and proceeds attributable to those\n+assets. This includes any remaining assets transferred from ZCG's previous\n+administrator. It excludes the CCF and assets administered by FPF for other\n+programs.\n+\n+\"Existing commitments\" means grants approved by the ZCG Committee before\n+activation of the designated upgrade on the Wind-down Date, including their\n+approved but unpaid milestones, and legally binding ZCG administrative\n+obligations entered into before that activation. An application, request for\n+proposals, or expression of intent that has not resulted in an approved grant\n+or binding obligation is not a commitment.\n+\n+\"Residual funds\" means ZCG funds remaining after provision for existing\n+commitments and the wind-down expenses permitted by this ZIP.\n+\n+\n+# Abstract\n+\n+This ZIP proposes an orderly wind-down of Zcash Community Grants. At activation\n+of the designated Mainnet network upgrade, ZCG would stop making new grants and\n+begin winding down its operations. Existing commitments would be completed\n+under their existing terms. FPF would retain sufficient assets to meet those\n+commitments and reasonable wind-down expenses, then contribute all residual\n+funds to the Network Sustainability Mechanism.\n+\n+ZIPs 1015 and 1016 currently restrict the purposes for which FPF may use ZCG\n+funds. This ZIP specifies amendments that expressly authorize completing\n+existing commitments, paying limited closure costs, and contributing the\n+remainder to the NSM. It also calls for corresponding legal instruments and\n+approvals so that the change can be executed consistently with FPF's legal\n+obligations, rather than assuming a ZIP alone changes existing agreements.\n+\n+This proposal complements the separate proposal to end ZCG's future funding\n+stream. That proposal changes future block subsidy allocations; this proposal\n+addresses the existing treasury, commitments, and program governance. It does\n+not itself change consensus rules or the CCF.\n+\n+The wind-down can begin before a voluntary NSM contribution mechanism is\n+deployed. Until such a mechanism is available, FPF would safeguard the residual\n+funds solely for that purpose. Public reporting would establish the disposition\n+of the treasury while protecting confidential grantee and financial information.\n+\n+\n+# Motivation\n+\n+ZCG has carried the torch through the evolution of Zcash's development reward\n+program. From the Major Grants structure established by ZIP 1014 to the\n+community grants program administered by FPF under ZIP 1015, it has provided\n+continuity for independent teams and a way to direct protocol-funded resources\n+toward work that benefits the Zcash ecosystem. [^zip-1014] [^zip-1015] That\n+contribution provides the foundation for the next stage of the funding model.\n+\n+Coinholder-directed funding has been part of that model since ZIP 1016,\n+\"Community and Coinholder Funding Model\", was created on 2025-02-19.\n+[^zip-1016] The voting technology developed to support it has since matured\n+into an operational system. The Coinholder-Directed Retroactive Grants Program\n+(CDRGP) completed its latest round on September 29, 2026, with approximately\n+2.18 million ZEC of voting power participating on individual proposals.\n+[^cdrgp-q3-results] [^cdrgp-q3-tallies] The separate NU7 scope vote closed on\n+September 14, 2026, with approximately 2.40 million ZEC participating on its\n+issuance-schedule question. [^nu7-poll-results] Circulating supply was\n+approximately 16.96 million ZEC on September 29 and 16.93 million ZEC on\n+September 14. Both processes therefore exceeded two million ZEC of\n+participation, or over 12% of the circulating supply in each case.\n+[^supply-vote-dates]\n+\n+The CDRGP has demonstrated that coinholder-directed grants are operational.\n+Its latest results approve funding for core protocol work by ZODL and Valar\n+Group, Zec.rocks wallet infrastructure, Ironwood external audits and formal\n+verification, and critical vulnerability bounties for Orchard and Zebra.\n+[^cdrgp-q3-results] Earlier rounds have also progressed from approval to\n+completed disbursements, including payments for Unstoppable Wallet, BitcoinVN,\n+and ZcashCommunity website hosting. [^cdrgp-disbursements] These are concrete\n+examples of a functioning funding channel for core development, infrastructure,\n+and security.\n+\n+ZIP 1016 provides for open proposal submission and permissionless participation\n+by eligible coinholders in grant approval, rather than requiring an award from\n+the ZCG Committee. [^zip-1016] The program retains published eligibility\n+conditions, legal vetoes, and administration and disbursement by FPF and the\n+Key-Holder Organizations. Its operation demonstrates that a separate ZCG\n+grant-selection committee is no longer needed to provide that funding channel.\n+\n+This proposal therefore treats ZCG's off-chain committee and legal arrangements\n+as a transitional bridge that has served its purpose. The development-funding\n+system now has an operational coinholder-directed approval process, supported\n+by on-chain voting infrastructure. [^coinholder-voting-chain] Continuing a\n+parallel ZCG grant-making body is no longer necessary in the proposed model.\n+Winding it down recognizes its contribution and completes that transition,\n+while preserving the legal administration still needed to execute grants.\n+\n+Ending ZCG's automatic funding stream and winding down its grant-making\n+program are distinct decisions. The proposal \"Ending the ZCG Funding Stream in\n+Favor of the NSM\" leaves ZCG's existing assets and the rules governing them\n+unchanged, including its authority to make further grants. [^end-zcg-stream]\n+An additional decision is therefore needed to close the program and determine\n+the disposition of its remaining treasury.\n+\n+This ZIP builds on Maxime Desalle's forum proposal, \"End ZCG and send its\n+funds to the NSM\". [^maxime-proposal]\n+\n+The policy proposed here is to honor work already approved, stop creating new\n+commitments, and direct the unused balance toward the long-term sustainability\n+of the Zcash network. Completing existing commitments gives grantees a defined\n+transition and avoids treating program closure as a reason to abandon approved\n+work. Returning the remainder through the NSM avoids creating another\n+discretionary grant-making body to receive it.\n+\n+The present fund restrictions need explicit attention. ZIP 1015 requires FPF\n+to use the ZCG allocation exclusively for grants and related administration,\n+and to recognize it contractually as a Restricted Fund donation. ZIP 1016\n+carries those obligations forward. [^zip-1015-zcg] [^zip-1016-admin] An NSM\n+contribution is neither a grant to an external party nor ordinary grant\n+administration. Express amendments are needed to authorize it within the ZIP\n+framework.\n+\n+FPF is an independent Cayman Islands foundation company. [^fpf] Its actual\n+authority also depends on applicable law, its constitutional documents, and\n+the agreements governing the assets. Foundation companies have their own\n+memorandum and articles of association. [^cayman-foundation] This proposal\n+provides a community mandate and a process for putting the necessary legal\n+authority in place; it does not assume those documents have already been\n+reviewed or amended.\n+\n+\n+# Privacy Implications\n+\n+This proposal introduces no changes to transaction privacy or wallet behavior.\n+Publishing the wind-down accounts and NSM contribution evidence may associate\n+transactions with FPF or the ZCG treasury. Reports can establish amounts and\n+completion without publishing grantees' private identities, payment addresses,\n+KYC records, or privileged legal advice. A future voluntary NSM contribution\n+mechanism may have additional privacy implications, as discussed in ZIP 233.\n+[^zip-0233]\n+\n+\n+# Requirements\n+\n+* Stop new ZCG grant-making at a clearly defined cutoff.\n+* Complete existing commitments under their approved terms, including normal\n+  milestone verification and remedies for non-performance.\n+* Provide FPF with an explicit mandate, within the ZIP framework and the\n+  applicable legal instruments, to close ZCG and contribute residual funds to\n+  the NSM.\n+* Limit and disclose the costs of administering the wind-down.\n+* Account for the full ZCG treasury, including later receipts and recoveries,\n+  without diverting funds belonging to other programs.\n+* Safeguard commitments and residual funds until their disposition is complete.\n+* End the committee's operational role when its remaining duties are complete,\n+  while preserving necessary accounting, custody, and reporting.\n+* Keep the decision about existing assets separate from changes to future\n+  subsidy allocations and CCF governance.\n+\n+\n+# Specification\n+\n+## Activation\n+\n+The wind-down and the amendments to ZIPs 1015 and 1016 MUST take effect at the\n+designated Mainnet network upgrade activation, regardless of NSM contribution\n+mechanism availability or ZCG funding-stream status. This ZIP MUST already\n+be Active under ZIP 0. [^zip-0000]\n+\n+The deployment ZIP MUST identify this ZIP and specify the upgrade's Mainnet\n+activation height under ZIP 200. [^zip-0200] Existing rules governing ZCG funds\n+continue to apply until that activation.\n+\n+At activation, ZCG MUST stop accepting applications and approving new grants.\n+Pending applications that were not approved before activation MUST be closed\n+without an award.\n+\n+## Administration during the wind-down\n+\n+From activation, FPF and the ZCG Committee MUST administer ZCG funds solely to\n+complete existing commitments, meet the permitted wind-down expenses, and\n+contribute residual funds to the NSM as specified below.\n+\n+FPF MUST notify affected applicants and existing grantees of the wind-down\n+and publish the designated upgrade, its Mainnet activation height, the\n+Wind-down Date when known, and a contact for outstanding commitments.\n+\n+This ZIP does not alter block validity, funding-stream amounts or end heights,\n+or ownership of funds already paid to grantees. Any ZCG funding-stream receipts\n+that continue under the consensus rules MUST be included in the wind-down\n+accounts and disposed of under this ZIP. Ending those receipts requires a\n+separate consensus change, such as the complementary proposal.\n+[^end-zcg-stream]\n+\n+The process MAY be rehearsed on Testnet using test funds. Testnet activation\n+or rehearsal MUST NOT trigger the Mainnet wind-down or authorize disposal of\n+Mainnet ZCG funds. Dates and reporting deadlines in this ZIP MUST be\n+interpreted using UTC.\n+\n+## Inventory, reserves, and wind-down plan\n+\n+Within 30 days after the Wind-down Date, FPF, with the ZCG Committee, MUST\n+publish an initial wind-down plan containing:\n+\n+* ZCG balances by asset and currency, reconciled to the accounting records;\n+* each existing commitment, its remaining amount and currency, outstanding\n+  conditions, and expected completion or lawful termination date;\n+* a reserve sufficient to meet those commitments, including a documented\n+  allowance for contingent liabilities and currency risk;\n+* an itemized wind-down budget, with a spending cap, responsible parties, and\n+  an expected timetable for completing the administrative work; and\n+* the legal and technical prerequisites for the NSM contribution, identifying\n+  which have been satisfied and which remain outstanding.\n+\n+The plan MUST distinguish an approved grant from an unconditional payment\n+liability. Its reserve MUST provide for unpaid approved milestones without\n+waiving their conditions. FPF MUST NOT contribute assets needed to satisfy\n+creditors, existing commitments, or other legal obligations to the NSM.\n+If assets are insufficient to meet these obligations, FPF MUST report the\n+shortfall and address it under the applicable agreements and law before\n+determining that any residual funds are available.\n+\n+FPF MUST keep ZCG funds separately accounted for and MUST NOT use them for the\n+CCF, other FPF programs, or general FPF operations unrelated to the wind-down.\n+\n+## Completion of existing commitments\n+\n+FPF and the ZCG Committee MUST continue to administer existing commitments\n+under their approved terms, including milestone review, reporting, and any\n+applicable suspension, termination, or refund provisions. This ZIP does not\n+make conditional milestones payable in advance or excuse non-performance.\n+\n+An existing commitment MAY be amended to enable completion or lawful closure,\n+but an amendment MUST NOT increase its approved funding amount or add a new\n+project or new scope of work. Any change requiring a grantee's consent MUST\n+obtain that consent. An unexercised option for additional funding or a\n+discretionary renewal MUST NOT be exercised.\n+\n+For an indefinite or automatically renewing commitment, FPF MUST use the\n+earliest lawful non-renewal or termination opportunity consistent with its\n+existing obligations. The plan MUST identify the resulting final service and\n+payment dates. Disputed or contingent liabilities MUST remain reserved until\n+resolved or lawfully discharged.\n+\n+Amounts released by completion, cancellation, refunds, or a reduction in a\n+required reserve MUST become residual funds; they MUST NOT be reassigned to\n+new grants.\n+\n+## Wind-down expenses and committee closure\n+\n+ZCG funds MAY cover reasonable, documented expenses necessary to complete the\n+wind-down, including grant administration, legal and accounting work, asset\n+conversion, custody, transaction fees, final reporting, and required record\n+retention. FPF MUST limit charges to work attributable to ZCG's wind-down and\n+MUST NOT charge unrelated programs' costs to ZCG.\n+\n+The ZCG Committee MUST approve the initial wind-down budget and any increase\n+in its spending cap, subject to FPF's existing legal veto. Existing conflict\n+of interest and compensation rules MUST continue to apply. Committee and FPF\n+compensation MUST reflect the reduced duties and actual work required.\n+\n+Until its remaining decisions are complete, the committee MUST retain the\n+membership and decision-making arrangements required by ZIP 1015. A vacancy\n+or expiring term MUST be addressed under the existing selection process if\n+needed to perform those duties. The wind-down does not authorize FPF to take\n+over discretionary grant decisions.\n+\n+Once existing commitments have been completed or lawfully discharged and the\n+committee has approved the final administrative budget, the committee MUST\n+conclude its operational role. No further ZCG elections are required after\n+that point. FPF MUST retain responsibility for any remaining custody, NSM\n+contributions, reporting, and legal record retention. An expense above the\n+final approved cap MUST NOT be paid from ZCG funds without further community\n+authorization under an amendment to this ZIP.\n+\n+## Legal authority and implementation\n+\n+Before making an NSM contribution, FPF MUST obtain advice from qualified\n+Cayman Islands counsel concerning its authority to execute this wind-down,\n+including the restricted-fund terms, its constitutional documents, existing\n+contracts, and any required consents or approvals.\n+\n+FPF MUST arrange for the commitments required by ZIPs 1015 and 1016 to be\n+amended, in suitable legal language, to authorize the uses specified here.\n+It MUST obtain any additional consent, corporate approval, or court order\n+that is necessary before taking an action that requires it. A ZIP amendment\n+MUST NOT be treated as automatically amending an agreement, extinguishing a\n+third party's rights, or overriding applicable law.\n+\n+FPF MUST publish confirmation of the authority and approvals on which it\n+relies, without disclosing privileged advice or confidential information. If\n+a required authorization cannot be obtained, FPF MUST report the obstacle,\n+safeguard the affected funds, and seek a further community decision. Such an\n+obstacle MUST NOT authorize new grants or an alternative recipient.\n+\n+## Contribution of residual funds to the NSM\n+\n+Subject to the reserve and legal requirements above, FPF MUST contribute all\n+residual funds to the NSM. Residual assets held in currencies or assets other\n+than ZEC MUST be converted to ZEC on commercially reasonable terms, with\n+conversion costs included in the approved budget and disclosed in the accounts.\n+\n+The contribution MUST use a deployed consensus mechanism that removes the\n+contributed ZEC from circulation and credits the corresponding amount to the\n+NSM Value Balance under ZIP 237. [^zip-0237] That mechanism MUST be active on\n+Mainnet when the contribution is made; publication of a ZIP, implementation\n+in software, or activation on Testnet alone is insufficient.\n+\n+ZIP 237 defines the NSM balance and reissuance rules, but does not by itself\n+provide a transaction mechanism for contributing existing treasury ZEC.\n+ZIP 233 proposes such a voluntary mechanism, but is not deployed by NU7 as\n+specified in ZIP 259. [^zip-0233] [^zip-0259] A different deployed Mainnet\n+mechanism meeting the contribution requirements above MAY be used. This ZIP\n+neither deploys ZIP 233 nor supplies a new consensus mechanism.\n+\n+If a suitable mechanism is not yet available, FPF MUST hold the residual\n+funds in segregated custody solely for a future NSM contribution and report\n+their balance and custody costs quarterly. FPF MUST NOT represent a payment\n+to an unspendable address, an excessive transaction fee, or a transfer to\n+another organization as fulfilling this obligation.\n+\n+FPF MUST publish a contribution schedule in the wind-down plan and update it\n+as reserves are released. Each residual amount MUST be contributed within\n+90 days after all three conditions are satisfied for that amount: it is\n+available without impairing the required reserves, the necessary legal\n+authority has been obtained, and the technical mechanism is deployed.\n+FPF MAY combine amounts within that period to reduce transaction costs,\n+provided the full remaining balance is accounted for and ultimately\n+contributed.\n+\n+For each contribution, FPF MUST publish its amount, transaction identifier,\n+confirmation block, and evidence that the amount was credited under the\n+deployed NSM mechanism. It MUST verify confirmation on the canonical chain\n+before recording the contribution as complete. The same rules apply to\n+later receipts and recoveries, including funding-stream payments received\n+after the initial treasury has been contributed.\n+\n+## Transparency and final accounting\n+\n+The existing transparency, security, and responsible disclosure obligations\n+in ZIPs 1015 and 1016 MUST continue during the wind-down. Reports MUST protect\n+confidential grantee information, private payment details, and key material;\n+redactions MUST NOT prevent reconciliation of the aggregate financial amounts.\n+\n+FPF MUST publish quarterly progress reports reconciling opening balances,\n+receipts, commitment payments, expenses, conversion proceeds, NSM\n+contributions, reserves, and closing balances. Any missed timetable or\n+change in reserve or budget MUST be explained.\n+\n+After existing commitments and wind-down expenses have been settled and all\n+residual funds then held have been contributed, FPF MUST publish a final\n+reconciliation and closure report. It MUST identify the custodian and retention\n+arrangements for legally required records and provide a contact for later\n+receipts, recoveries, or claims. Public reports and grant records SHOULD remain\n+available as a historical archive. Any later receipt or recovery, including\n+funding-stream receipts, MUST be accounted for and contributed under this ZIP\n+after provision for lawful related expenses. Custody and quarterly reporting\n+obligations MUST continue for any such funds pending contribution.\n+\n+## Changes to ZIP 1015\n+\n+In the Specification, under \"Zcash Community Grants (ZCG)\", insert a new\n+subsection \"Wind-down of Zcash Community Grants\" immediately before\n+\"Furthering Decentralization\", with the following text:\n+\n+> At Mainnet activation on the Wind-down Date defined in the ZIP \"Winding Down\n+> Zcash Community Grants\", FPF and the ZCG Committee MUST begin winding down\n+> the ZCG program in accordance with that ZIP. For this limited purpose, its\n+> provisions take precedence over conflicting provisions of this ZIP, including\n+> the exclusive grant-use restriction, committee and election requirements,\n+> Discretionary Budget provisions, and obligations to explore an independent\n+> ZCG entity.\n+>\n+> The permitted purposes of the ZCG Restricted Fund donation expressly include\n+> completing existing commitments, paying the reasonable and limited wind-down\n+> expenses authorized by that ZIP, and contributing all residual funds to the\n+> NSM. FPF MUST arrange the corresponding amendments to the restricted-fund\n+> agreements and obtain the legal authority required by that ZIP before\n+> executing an action that requires it. These purposes do not authorize new\n+> grants, unrelated FPF expenditure, or diversion to another fund.\n+>\n+> An NSM contribution mandated by that ZIP is an authorized disposition of the\n+> Restricted Fund, rather than a grant requiring a new grant award. Compliance\n+> with that ZIP does not constitute a breach for the purposes of Enforcement.\n+> Security, separate accounting, and transparency obligations continue until\n+> their completion under that ZIP, with required record retention continuing\n+> thereafter.\n+\n+Add a reference to this ZIP in that subsection and in the References section,\n+using its assigned identifier when available. The existing funding-stream\n+tables and historical allocations are unchanged by this amendment.\n+\n+## Changes to ZIP 1016\n+\n+In \"Zcash Community Grants\", append to the first paragraph:\n+\n+> At Mainnet activation on the Wind-down Date defined in the ZIP \"Winding Down\n+> Zcash Community Grants\", the administration and disposition of ZCG funds are\n+> governed by that ZIP, including its amendments to ZIP 1015. This applies to both\n+> accumulated funds and any subsequent receipts for ZCG.\n+\n+In \"Administrative obligations and constraints\", append to the first paragraph:\n+\n+> These provisions are subject to the ZCG wind-down amendments specified in\n+> the ZIP \"Winding Down Zcash Community Grants\". FPF MUST arrange the\n+> corresponding amendments to its contractual commitments governing ZCG funds.\n+\n+After the third paragraph of that section, insert:\n+\n+> The ZCG wind-down amendments to ZIP 1015 and the ZIP \"Winding Down Zcash\n+> Community Grants\" apply only to ZCG funds. They are not incorporated into\n+> the requirements governing the Coinholder-Controlled Fund or its Key-Holder\n+> Organizations. For that Fund, the otherwise applicable ZIP 1015 obligations\n+> continue without those amendments. Neither FPF nor the ZCG Committee is\n+> authorized by the wind-down to dispose of Coinholder-Controlled Fund assets.\n+\n+Add references to this ZIP at those insertions and in the References section,\n+using its assigned identifier when available. If the complementary\n+funding-stream proposal is also adopted, its changes to the duration and\n+destination of future allocations apply independently of these amendments.\n+\n+\n+# Rationale\n+\n+## Completing approved work\n+\n+Paying the entire treasury into the NSM immediately would leave approved work\n+and potential liabilities unfunded. Keeping a documented reserve permits\n+existing projects to finish while preserving their milestone conditions.\n+Closing applications and discretionary renewals provides a definite end to\n+grant-making. Contractual termination provisions address ongoing arrangements\n+without assuming that all grants end on the same date.\n+\n+## Choosing the NSM for the remainder\n+\n+The proposal directs the remainder toward protocol-defined network\n+sustainability. Transferring it to another discretionary organization or\n+creating a replacement grants committee would preserve the allocation role\n+that this proposal seeks to close. The NSM's subsequent reissuance remains\n+governed by its own consensus specification.\n+\n+## Expressly amending the fund restrictions\n+\n+Relying on an interpretation that an NSM contribution is an ordinary ZCG grant\n+would leave FPF's mandate unclear. Express permission in both ZIPs, together\n+with corresponding legal instruments, addresses the restricted-fund purpose\n+and the incorporation of ZIP 1015's rules by ZIP 1016. The CCF exception avoids\n+accidentally authorizing its wind-down through that incorporation.\n+\n+## Coordinating the wind-down with a network upgrade\n+\n+Mainnet activation provides a shared cutoff for ending new grant-making and,\n+if the complementary proposal is adopted for the same upgrade, ending future\n+funding-stream receipts. The proposals remain separate decisions about future\n+allocations and existing assets. This ZIP uses the upgrade's activation as its\n+process trigger without introducing its own block-validation changes.\n+\n+If a voluntary NSM contribution mechanism is not available when the wind-down\n+begins, safe custody bridges the interval until it is deployed. Custody may\n+continue after the committee concludes its operational role. It introduces\n+continuing costs and dependence on a future deployment, which the community\n+should weigh when considering this ZIP.\n+\n+## Community consideration\n+\n+This is an initial proposal for discussion; it does not claim community\n+consensus. Review should consider the effect on teams that depend on future\n+ZCG grants, the availability of other funding channels, the proposed cutoff,\n+and the costs and risks of retaining funds pending an NSM contribution.\n+\n+\n+# Deployment\n+\n+This ZIP is not included in NU7 as currently specified by ZIP 259. [^zip-0259]\n+The target upgrade and its Mainnet activation height are unassigned. A\n+deployment ZIP will specify them under ZIP 200. [^zip-0200]\n+\n+# Reference implementation\n+\n+None yet.\n+\n+# Open issues\n+\n+* Obtain FPF's legal review of the restricted-fund arrangements, constitutional\n+  documents, and required consents; identify any assets with additional\n+  restrictions that prevent the proposed disposition.\n+* Designate the target network upgrade and its Mainnet activation height in\n+  the deployment ZIP.\n+* Confirm the 30-day initial reporting period and the 90-day contribution\n+  period after legal and technical readiness.\n+* Reconcile the outstanding grant and administrative commitments with FPF and\n+  agree the wind-down budget and reserve methodology before implementation.\n+* Identify the network upgrade and operational tooling for voluntary NSM\n+  contributions. Decide whether to retain the proposed custody arrangement if\n+  deployment is substantially delayed.\n+* Obtain independent review of the treasury custody, contribution verification,\n+  and privacy arrangements before this ZIP becomes Released under ZIP 0.\n+* Record community feedback and any substantiated objections before seeking\n+  advancement beyond Draft.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — RFC 2119 and RFC 8174](https://www.rfc-editor.org/info/bcp14/)\n+\n+[^zip-0000]: [ZIP 0: ZIP Process](zip-0000.rst)\n+\n+[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)\n+\n+[^zip-0233]: [ZIP 233: Network Sustainability Mechanism: Removing Funds From Circulation](zip-0233.md)\n+\n+[^zip-0237]: [ZIP 237: Network Sustainability Mechanism: Halving-Preserving Issuance](zip-0237.md)\n+\n+[^zip-0259]: [ZIP 259: Deployment of the NU7 Network Upgrade](zip-0259.md)\n+\n+[^zip-1014]: [ZIP 1014: Establishing a Dev Fund for ECC, ZF, and Major Grants](zip-1014.rst)\n+\n+[^zip-1015]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding](zip-1015.rst)\n+\n+[^zip-1015-zcg]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding — Zcash Community Grants](zip-1015.rst#zcash-community-grants-zcg)\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](zip-1016.md)\n+\n+[^zip-1016-admin]: [ZIP 1016: Community and Coinholder Funding Model — Administrative obligations and constraints](zip-1016.md#administrativeobligationsandconstraints)\n+\n+[^end-zcg-stream]: [Draft: Ending the ZCG Funding Stream in Favor of the NSM](https://github.com/zcash/zips/blob/end-zcg-stream/zips/draft-bowe-end-zcg-stream.md)\n+\n+[^maxime-proposal]: [Maxime Desalle: End ZCG and send its funds to the NSM](https://forum.zcashcommunity.com/t/end-zcg-and-send-its-funds-to-the-nsm/58515)\n+\n+[^cdrgp-q3-results]: [FPF: Q3 Coinholder-Directed Retroactive Grants Program Results, September 29, 2026](https://forum.zcashcommunity.com/t/q3-coinholder-directed-retroactive-grants-program-results/57909)\n+\n+[^cdrgp-q3-tallies]: [Q3 2026 Coinholder-Directed Retroactive Grants Program: results and participation, with links to the finalized vote-chain tallies](https://openzcash.org/coinholders/results?round=q3-2026)\n+\n+[^nu7-poll-results]: [NU7 Coinholder Vote: results published by Sean Bowe, September 14, 2026](https://forum.zcashcommunity.com/t/nu7-coinholder-vote/56912/42)\n+\n+[^supply-vote-dates]: [ZEC Stats: node-derived daily Zcash supply data](https://zecstats.com/api/daily-stats). The `end_supply_zat` values for 2026-09-14 and 2026-09-29 are 1,693,284,391,554,480 and 1,695,970,641,554,480 zatoshis, respectively: 16,932,843.91554480 and 16,959,706.41554480 ZEC. These are end-of-day UTC figures; the comparison uses total issued ZEC as circulating supply. See the [supply methodology](https://zecstats.com/chart/supply).\n+\n+[^cdrgp-disbursements]: [FPF: completed Q4 2025 Coinholder-Directed Retroactive Grants Program disbursements, February 4, 2026](https://forum.zcashcommunity.com/t/coinholder-directed-retroactive-grants-program-results-nov-2025/53487/19)\n+\n+[^coinholder-voting-chain]: [NU7 Coinholder Vote: voting infrastructure and participation](https://forum.zcashcommunity.com/t/nu7-coinholder-vote/56912)\n+\n+[^fpf]: [Financial Privacy Foundation — About Us](https://www.financialprivacyfoundation.org/)\n+\n+[^cayman-foundation]: [Cayman Islands General Registry — Foundation Company](https://www.ciregistry.ky/companies-register/types-of-companies/foundation-company/)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-howard-wind-down-zcg.md","title":"Winding Down Zcash Community Grants","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"activation","text":"Activation"},{"level":2,"id":"administration-during-the-wind-down","text":"Administration during the wind-down"},{"level":2,"id":"inventory-reserves-and-wind-down-plan","text":"Inventory, reserves, and wind-down plan"},{"level":2,"id":"completion-of-existing-commitments","text":"Completion of existing commitments"},{"level":2,"id":"wind-down-expenses-and-committee-closure","text":"Wind-down expenses and committee closure"},{"level":2,"id":"legal-authority-and-implementation","text":"Legal authority and implementation"},{"level":2,"id":"contribution-of-residual-funds-to-the-nsm","text":"Contribution of residual funds to the NSM"},{"level":2,"id":"transparency-and-final-accounting","text":"Transparency and final accounting"},{"level":2,"id":"changes-to-zip-1015","text":"Changes to ZIP 1015"},{"level":2,"id":"changes-to-zip-1016","text":"Changes to ZIP 1016"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"completing-approved-work","text":"Completing approved work"},{"level":2,"id":"choosing-the-nsm-for-the-remainder","text":"Choosing the NSM for the remainder"},{"level":2,"id":"expressly-amending-the-fund-restrictions","text":"Expressly amending the fund restrictions"},{"level":2,"id":"coordinating-the-wind-down-with-a-network-upgrade","text":"Coordinating the wind-down with a network upgrade"},{"level":2,"id":"community-consideration","text":"Community consideration"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1428,"title":"Draft ZIP: Future Dev and Legal Defense Fund Lockbox, and a 50,000 ZEC donation to the NSM","author":"aquietinvestor","createdAt":"2026-10-30T14:44:33Z","updatedAt":"2026-11-03T22:55:21Z","url":"https://github.com/zcash/zips/pull/1428","diffUrl":"https://github.com/zcash/zips/pull/1428.diff","headRef":"future-dev-legal-defense-fund","headRepo":"aquietinvestor/zips","headSha":"e2c7c3d5f83d60a468b5291759262cfa4c456df4","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Add draft ZIP for Future Dev and Legal Defense Fund Lockbox, Its authors are Sean Bowe (Tachyon Foundation), Zooko Wilcox (Shielded Labs), and Jason McGee (Shielded Labs). It makes two changes: 1. At its activation, Zcash Community Grants' 8% funding stream ends, and until the third halving the same","zipRefs":[],"draftRefs":["draft-mcgee-future-dev-legal-defense-fund"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -233,6 +233,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-mcgee-future-dev-legal-defense-fund</td> <td class=\"left\"><a href=\"zips/draft-mcgee-future-dev-legal-defense-fund.md\">Future Dev and Legal Defense Fund Lockbox, and a ZCG Donation to the Network Sustainability Mechanism</a></td> <td class=\"left\"><a href=\"https://forum.zcashcommunity.com/t/future-dev-and-legal-defense-fund-zip/58611\">https://forum.zcashcommunity.com/t/future-dev-and-legal-defense-fund-zip/58611</a></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-mcgee-future-dev-legal-defense-fund.md","status":"added","additions":276,"deletions":0,"patch":"@@ -0,0 +1,276 @@\n+    ZIP: Unassigned\n+    Title: Future Dev and Legal Defense Fund Lockbox, and a ZCG Donation to the Network Sustainability Mechanism\n+    Owners: Sean Bowe (Tachyon Group) <ewillbefull@gmail.com>\n+            Zooko Wilcox (Shielded Labs) <zooko@shieldedlabs.net>\n+            Jason McGee (Shielded Labs) <jason@shieldedlabs.net>\n+    Status: Draft\n+    Category: Consensus / Process\n+    Created: 2026-10-29\n+    License: MIT\n+    Discussions-To: <https://forum.zcashcommunity.com/t/future-dev-and-legal-defense-fund-zip/58611>\n+    Pull-Request: <https://github.com/zcash/zips/pull/1428>\n+    Updates: 207, 209, 214, 237, 1015, 1016\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHALL\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in\n+this document are to be interpreted as described in BCP 14 [^BCP14] when, and\n+only when, they appear in all capitals.\n+\n+The terms \"Mainnet\", \"Testnet\", \"block subsidy\", \"funding stream\", \"coinbase\n+transaction\", \"chain value pool balance\", and \"Issued Supply\" have the meanings\n+given in the Zcash Protocol Specification [^protocol]; the character § refers\n+to its sections.\n+\n+\"Zcash Community Grants\" (\"ZCG\"), the \"ZCG Committee\", the \"Financial Privacy\n+Foundation\" (\"FPF\"), and the \"Deferred Dev Fund Lockbox\" have the meanings given\n+in ZIP 1015 [^zip-1015].\n+\n+The \"Network Sustainability Mechanism\" (\"NSM\") and the \"NSM Value Balance\" have\n+the meanings given in ZIP 237 [^zip-0237]: ZEC removed from circulation is added\n+to the NSM Value Balance, and returns to circulation through future block\n+subsidies.\n+\n+\n+# Abstract\n+\n+This ZIP makes two changes.\n+\n+1. ZCG's 8% funding stream ends when this ZIP activates. For the rest of that\n+   stream's term, the same 8% of the block subsidy goes into a new lockbox, the\n+   **Future Dev and Legal Defense Fund Lockbox**, which holds it for future\n+   Zcash development and legal defense.\n+\n+2. The ZCG Committee donates 50,000 ZEC to the NSM within 30 days of this ZIP's\n+   acceptance, by sending it to an address that no one can spend from. When\n+   this ZIP activates, everything sent to that address is removed from\n+   circulation and added to the NSM Value Balance.\n+\n+\n+# Motivation\n+\n+The rapid rise in the price of ZEC has enlarged the monetary value of ZCG's\n+treasury to a much greater amount than ever before. This ZIP reduces the risks\n+and pressure that come with large amounts of money, donates to the Network\n+Sustainability Mechanism (providing increased long-term issuance), and accrues\n+funds in a lockbox for future use by the Zcash community.\n+\n+ZCG's dashboard reports holdings of 91,452.98 ZEC and \\$8,796,224, against\n+\\$2,417,165 of outstanding grant liabilities [^zcg-dashboard]. FPF reported\n+\\$4,718,359 of ZCG expenses in the first half of 2026, about \\$9.4 million a\n+year [^fpf-q2-2026]. The 8% stream adds another 144 ZEC a day, 52,560 ZEC a\n+year; at \\$1,300 per ZEC, that is about \\$68 million a year, more than seven\n+times ZCG's spending [^stream-scenarios].\n+\n+ZCG's treasury before and after the donation, at \\$1,300 per ZEC\n+(details [^donation-scenarios]):\n+\n+| | ZEC | Dollars | Value, less grant liabilities | Years of spending at 2026 levels |\n+| :-- | --: | --: | --: | --: |\n+| Before | 91,452.98 | \\$8,796,224 | \\$125.3 million | 13.3 |\n+| After | 41,452.98 | \\$8,796,224 | \\$60.3 million | 6.4 |\n+\n+\n+# Privacy Implications\n+\n+The lockbox adds no addresses, keys, or notes. The donation is a public\n+transparent payment to a published address. This ZIP leaves the privacy of\n+every transaction unchanged.\n+\n+\n+# Specification\n+\n+## Activation\n+\n+Let $A$ be this ZIP's activation height on a network, and $N$ that network's\n+NU7 activation height; $A$ MUST be greater than $N$. Let $E$ be the end height\n+of `FS_FPF_ZCG_H3` under Revision 3 of ZIP 214 [^zip-0214]: the third halving\n+under ZIP 218's 25-second blocks [^zip-0218], $E = N + 3 \\cdot (H_3 - N)$, where\n+$H_3$ is the pre-NU7 third-halving height (4406400 on Mainnet, 4476000 on\n+Testnet). With Mainnet's $N = 3543405$ [^zip-0259], $E = 6132390$. End heights\n+are exclusive, as in ZIP 207 [^zip-0207].\n+\n+The lockbox rules below apply on a network where $A < E$; where $A \\geq E$, the\n+ZCG stream has already ended, and they change nothing. The NSM donation address\n+rules apply from $A$ on every network.\n+\n+## The Future Dev and Legal Defense Fund Lockbox\n+\n+The Future Dev and Legal Defense Fund Lockbox holds funds for future Zcash\n+development and legal defense. No one can spend from it until a future ZIP\n+specifies how.\n+\n+**Funding streams.** A new revision of ZIP 214 makes these changes at $A$:\n+\n+* The end height of `FS_FPF_ZCG_H3` becomes $A$. Its recipient list keeps the\n+  addresses for the address periods before $A$, as ZIP 207 computes them,\n+  including ZIP 2008's change [^zip-2008].\n+* A new funding stream, `FS_FDLDF_H3`, pays 8/100 of the block subsidy from\n+  height $A$ to height $E$, to the recipient $\\mathsf{FDLDF\\_POOL}$ in every\n+  address period.\n+\n+`FS_CCF_H3` and the Deferred Dev Fund Lockbox are unchanged.\n+\n+**The recipient.** A new revision of ZIP 207 adds the recipient identifier\n+$\\mathsf{FDLDF\\_POOL}$, which works as $\\mathsf{DEFERRED\\_POOL}$ does [^zip-2001]:\n+a stream paid to it produces no coinbase output, and its value goes into the\n+lockbox's balance instead. In § 7.8 and § 7.1.2, define\n+$\\mathsf{totalFDLDFOutput}(h)$ as the sum of $\\mathsf{fs.Value}(h)$ over the\n+streams paid to $\\mathsf{FDLDF\\_POOL}$ at height $h$, and add it to the coinbase\n+transaction's $\\mathsf{totalOutputValue}$, beside\n+$\\mathsf{totalDeferredOutput}(h)$.\n+\n+**The balance.** In § 4.17, add the lockbox's chain value pool balance,\n+\n+$$\\mathsf{FDLDFBalance}(h) := \\sum_{i=0}^{h} \\mathsf{totalFDLDFOutput}(i),$$\n+\n+and include it in $\\mathsf{IssuedSupply}(h)$. Add it to the pools of ZIP 209\n+[^zip-0209], whose balances MUST be nonnegative and whose sum MUST NOT exceed\n+$\\mathsf{MAX\\_MONEY}$. No consensus rule moves value out of it.\n+\n+## The NSM donation address\n+\n+The NSM donation address is the Mainnet P2SH address\n+\n+    t3QZUHYkaEQEuWosAc9HgMPKiQRdvuBPCQZ\n+\n+Its script hash, `41c98a140039816273e50db317422c11c2bfcc88`, is HASH160 of the\n+one-byte script `6a`, `OP_RETURN`, which fails whatever a spender supplies. To\n+check this, decode the address with Base58Check, confirm its version prefix\n+(`1cbd`) and checksum, and compare its script hash with HASH160 of the byte\n+`0x6a`. Spending from it would require a second script with the same HASH160.\n+\n+Let $\\mathsf{DonatedValue}(h)$ be the total value of the transparent outputs in\n+block $h$, in any transaction including the coinbase transaction, whose script\n+is exactly the P2SH script `a91441c98a140039816273e50db317422c11c2bfcc8887`.\n+From $A$ on:\n+\n+* A transaction MUST NOT spend an output with that script.\n+* At height $A$, the value of every such output up to and including block $A$,\n+  $\\sum_{i=0}^{A} \\mathsf{DonatedValue}(i)$, is removed from circulation; at\n+  each later height $h$, $\\mathsf{DonatedValue}(h)$ is. Call this amount\n+  $\\mathsf{NSMDonations}(h)$. In § 4.17, the transparent chain value pool\n+  balance decreases by $\\mathsf{NSMDonations}(h)$, and ZIP 237's list of\n+  mechanisms for removal from circulation gains the source\n+  $\\mathsf{NSMDonations}(h)$, so that it is added to the NSM Value Balance.\n+\n+Anyone MAY send ZEC to the NSM donation address, and nothing sent there can be\n+returned. Wallets SHOULD NOT offer it as an ordinary payment destination.\n+Counts of circulating supply SHOULD exclude everything sent to it, before $A$\n+as well as after.\n+\n+## The donation\n+\n+The ZCG Committee SHALL cause 50,000 ZEC of the funds it administers to be sent\n+to the NSM donation address, in one or more Mainnet transactions. It SHALL do so\n+as soon as possible, and no later than 30 days after the date on which this ZIP\n+is accepted by the Zcash community.\n+\n+Since the funds ZCG administers are held by FPF, the ZCG Committee fulfills this\n+obligation by directing FPF to create the transactions, as it directs FPF to\n+make grant payments under ZIP 1015. Only FPF's payments count toward the\n+donation.\n+\n+The ZCG Committee SHALL publish the transaction identifier of each such\n+transaction to the Zcash community promptly after it is mined.\n+\n+## Changes to ZIPs 1015 and 1016\n+\n+In ZIP 1015's section on ZCG [^zip-1015-zcg], add: \"ZCG funds MAY also be used\n+for the donation to the Network Sustainability Mechanism that [this ZIP]\n+specifies. That donation is not a grant.\"\n+\n+In ZIP 1016's section on ZCG [^zip-1016], end ZCG's funding stream at this\n+ZIP's activation height, and state that from then until the third halving the\n+same 8% goes into the Future Dev and Legal Defense Fund Lockbox, as this ZIP\n+specifies. In its section on the Coinholder-Controlled Fund, give the 12%\n+stream's duration by `FS_CCF_H3`'s own start and end heights in ZIP 214, which\n+this ZIP leaves unchanged.\n+\n+\n+# Rationale\n+\n+**A lockbox of its own.** The 8% is set aside, issued but locked, for future\n+development and legal defense, and a future ZIP decides how it is spent. A\n+separate balance keeps it apart from the Deferred Dev Fund Lockbox's purpose\n+and disbursement rules, and like that lockbox it needs no custodians or keys.\n+\n+**A donation by address.** NU7 deploys ZIP 237's NSM but no way for a\n+transaction to remove ZEC from circulation directly: it deploys neither ZIP 233\n+nor ZIP 234 [^zip-0233] [^zip-0259]. Sending to an address that no one can spend\n+from lets the donation happen now; at $A$, it moves into the NSM, with every\n+earlier payment to the address. The consensus change is small: no\n+new transaction fields, address formats, or wallet changes. Payments from anyone\n+count, so the address serves every donor. If ZIP 233 is deployed later, both\n+mechanisms add to the same NSM Value Balance.\n+\n+**The donation's size.** 50,000 ZEC is about 55% of ZCG's ZEC. Anyone can check\n+its payment on the chain, and ZCG keeps the rest of its treasury and its grants\n+program.\n+\n+**Accounting.** The lockbox's balance stays in the Issued Supply. A donation\n+moves value from the transparent pool to the NSM Value Balance in the same\n+block. Both keep ZIP 237's invariant: in each block from NU7 on, the Issued\n+Supply plus the NSM Value Balance grows by exactly the scheduled block subsidy.\n+\n+**Other proposals.** ZIP draft #1414 sends the 8% stream to the NSM\n+[^draft-end-zcg-stream], and draft #1423 winds ZCG down [^draft-wind-down-zcg].\n+Where they overlap with this ZIP, they are alternatives to it.\n+\n+\n+# Deployment\n+\n+The lockbox and the NSM donation address are consensus changes, proposed for a\n+network upgrade after NU7 that activates before height $E$; that upgrade's\n+deployment ZIP sets $A$ for each network [^zip-0200]. The donation needs no\n+network upgrade: it is due within 30 days of this ZIP's acceptance.\n+\n+\n+# Reference implementation\n+\n+None yet.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification](https://zips.z.cash/protocol/protocol.pdf)\n+\n+[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](https://zips.z.cash/zip-0200)\n+\n+[^zip-0207]: [ZIP 207: Funding Streams](https://zips.z.cash/zip-0207)\n+\n+[^zip-0209]: [ZIP 209: Prohibit Out-of-Range Chain Value Pool Balances](https://zips.z.cash/zip-0209)\n+\n+[^zip-0214]: [ZIP 214: Consensus rules for a Zcash Development Fund](https://zips.z.cash/zip-0214)\n+\n+[^zip-0218]: [ZIP 218: 25-second Block Target Spacing](https://zips.z.cash/zip-0218)\n+\n+[^zip-0233]: [ZIP 233: Network Sustainability Mechanism: Removing Funds From Circulation](https://zips.z.cash/zip-0233)\n+\n+[^zip-0237]: [ZIP 237: Network Sustainability Mechanism: Halving-Preserving Issuance](https://zips.z.cash/zip-0237)\n+\n+[^zip-0259]: [ZIP 259: Deployment of the NU7 Network Upgrade](https://zips.z.cash/zip-0259)\n+\n+[^zip-1015]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding](https://zips.z.cash/zip-1015)\n+\n+[^zip-1015-zcg]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding — Zcash Community Grants](https://zips.z.cash/zip-1015#zcash-community-grants-zcg)\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](https://zips.z.cash/zip-1016)\n+\n+[^zip-2001]: [ZIP 2001: Lockbox Funding Streams](https://zips.z.cash/zip-2001)\n+\n+[^zip-2008]: [ZIP 2008: Update to `FS_FPF_ZCG_H3` address list](https://zips.z.cash/zip-2008)\n+\n+[^draft-end-zcg-stream]: [Draft: Ending the ZCG Funding Stream in Favor of the NSM (zcash/zips#1414)](https://github.com/zcash/zips/pull/1414)\n+\n+[^draft-wind-down-zcg]: [Draft: Winding Down Zcash Community Grants (zcash/zips#1423)](https://github.com/zcash/zips/pull/1423)\n+\n+[^fpf-q2-2026]: [Financial Privacy Foundation: Q2 2026 Report, p. 5 (ZCG statistics, with total expenses for Q1 and Q2 2026)](https://9ba4718c-5c73-47c3-a024-4fc4e5278803.usrfiles.com/ugd/9ba471_0836532577dd4f0eab3cf5080820e622.pdf). Q2's total includes \\$1,216,687 of Discretionary Fund usage, versus \\$54,108 in Q1, so neither quarter alone indicates recurring spending.\n+\n+[^zcg-dashboard]: [Zcash Community Grants website](https://zcashcommunitygrants.org/) and its linked [Zcash Community Grants + Coinholder Grants Dashboard](https://docs.google.com/spreadsheets/d/1FQ28rDCyRW0TiNxrm3rgD8ai2KGUsXAjPieQmI1kKKg/edit?usp=sharing), accessed October 26, 2026. The recorded inputs are 91,452.98 ZEC, USD 8,796,224, and USD 2,417,165 in grant liabilities. The dashboard's block reference is 3,526,011 (October 21, 2026, 22:37:53 UTC), and the worksheet is marked “Updates required”. The figures are reported dashboard inputs and have not been independently reconciled.\n+\n+[^stream-scenarios]: The 8% of a 1.5625 ZEC block subsidy, at 1,152 blocks of 75 seconds a day: `0.08 * 1.5625 * 1,152 = 144` ZEC a day, `144 * 365 = 52,560` ZEC a year; at USD 1,300 per ZEC, USD 68,328,000 a year. ZIP 218's 25-second blocks, each with a third of the subsidy, leave the daily amount the same, apart from rounding. Annual spending: `(1,530,844 + 3,187,515) * 2 = 9,436,718` USD, from the Q2 2026 report.\n+\n+[^donation-scenarios]: From the dashboard figures above, at USD 1,300 per ZEC: before the donation, `91,452.98 * 1,300 + 8,796,224 - 2,417,165 = 125,267,933` USD; after it, `41,452.98 * 1,300 + 8,796,224 - 2,417,165 = 60,267,933` USD. Years of spending divide these by the 2026 rate of USD 9,436,718 a year. The figures assume no conversion costs and no further receipts, returns, or obligations beyond the dashboard's grant liabilities."}],"filesTruncated":false,"preview":{"filename":"zips/draft-mcgee-future-dev-legal-defense-fund.md","title":"Future Dev and Legal Defense Fund Lockbox, and a ZCG Donation to the Network Sustainability Mechanism","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"activation","text":"Activation"},{"level":2,"id":"the-future-dev-and-legal-defense-fund-lockbox","text":"The Future Dev and Legal Defense Fund Lockbox"},{"level":2,"id":"the-nsm-donation-address","text":"The NSM donation address"},{"level":2,"id":"the-donation","text":"The donation"},{"level":2,"id":"changes-to-zips-1015-and-1016","text":"Changes to ZIPs 1015 and 1016"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1435,"title":"Add draft ZIP: Wallet-to-Wallet Messaging","author":"arya2","createdAt":"2026-11-03T21:56:12Z","updatedAt":"2026-11-03T21:56:12Z","url":"https://github.com/zcash/zips/pull/1435","diffUrl":"https://github.com/zcash/zips/pull/1435.diff","headRef":"draft-wallet-to-wallet-messaging","headRepo":"zcash/zips","headSha":"748171908d06861c5d0051a4f1310e43d03c71cc","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Adds `zips/draft-arya-wallet-to-wallet-messaging.md`, a Standards / Wallet draft specifying an off-chain, mailbox-based channel between wallets over an anonymity network: - Envelopes are encrypted to the Orchard Receiver of the recipient's Unified Address, reusing the Orchard key agreement with a do","zipRefs":[316],"draftRefs":["draft-arya-wallet-to-wallet-messaging"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-arya-wallet-to-wallet-messaging.md","status":"added","additions":677,"deletions":0,"patch":"@@ -0,0 +1,677 @@\n+    ZIP: Unassigned\n+    Title: Wallet-to-Wallet Messaging\n+    Owners: Arya <arya@zfnd.org>\n+    Status: Draft\n+    Category: Standards / Wallet\n+    Created: 2026-11-03\n+    License: MIT\n+    Pull-Request: <https://github.com/zcash/zips/pull/???>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and only\n+when, they appear in all capitals.\n+\n+The terms \"Unified Address\", \"Unified Viewing Key\", \"Receiver\", \"Metadata\n+Item\", \"Typecode\", \"Producer\", and \"Consumer\" are to be interpreted as defined\n+in ZIP 316. [^zip-0316] An \"Orchard Receiver\" is a Receiver with Typecode\n+$\\mathtt{0x03}$.\n+\n+compactSize\n+\n+: A variable-length integer as used in the Bitcoin protocol,\n+  [^Bitcoin-CompactSize] in minimal encoding, with a value of at most\n+  $\\mathtt{0x02000000}$.\n+\n+Sender wallet\n+\n+: The wallet that creates and deposits an Envelope.\n+\n+Recipient wallet\n+\n+: The wallet an Envelope is encrypted to.\n+\n+Envelope\n+\n+: An encrypted message, padded to a size class, as deposited in a Mailbox.\n+\n+Mailbox\n+\n+: A service that holds Envelopes for a Recipient wallet until it fetches them.\n+\n+Mailbox Descriptor\n+\n+: The data a Sender wallet needs to deposit an Envelope in a Mailbox.\n+\n+Transport Binding\n+\n+: A specification of the Mailbox operations over one anonymity network,\n+  identified by a `binding_id`.\n+\n+Payload\n+\n+: The typed content carried inside an Envelope.\n+\n+\n+# Abstract\n+\n+This ZIP specifies an off-chain channel between Zcash wallets. A Sender wallet\n+that holds a Recipient wallet's Unified Address deposits an encrypted Envelope\n+in a Mailbox over an anonymity network, and the Recipient wallet fetches it\n+later. Neither wallet reveals its network identity to the other, and the\n+Mailbox operator does not learn who sent an Envelope.\n+\n+Envelopes are encrypted to the Orchard Receiver of the Unified Address, using\n+the Orchard key agreement with a domain-separated key derivation, so no new\n+keys have to be exchanged. A new ZIP 316 Metadata Item carries the Mailbox\n+Descriptor in the Unified Address. Payload types are defined for\n+acknowledgements, text, ZIP 321 payment requests, and PCZTs, with codes\n+reserved for FROST signing rounds and off-chain note delivery. Mailbox\n+credentials are derived from the wallet seed with ZIP 32 registered key\n+derivation.\n+\n+Anyone who holds the recipient's Orchard incoming viewing key can read its\n+Envelopes, and a Mailbox Descriptor links every Unified Address that carries\n+it. Transport Bindings are specified separately; Appendix A sketches one for\n+the Nym mixnet. [^nym]\n+\n+\n+# Motivation\n+\n+Several wallet protocols need a direct channel between wallets. Each wallet\n+that wants one currently builds its own, so wallets from different vendors\n+cannot talk to each other:\n+\n+- Multisig and hardware-signer flows pass PCZTs [^pczt] between parties.\n+- FROST signing (ZIP 312 [^zip-0312]) runs over a confidential, authenticated\n+  channel between the Coordinator and the signers whose mechanism ZIP 312\n+  leaves out of scope. This ZIP supplies the confidential, anonymous\n+  transport; a FROST payload would have to carry its own authentication.\n+- Payment requests (ZIP 321 [^zip-0321]) and acknowledgements have no\n+  wallet-to-wallet transport.\n+- Project Tachyon [^tachyon] [^tachyon-post] proposes removing in-band secret\n+  distribution from transactions, which makes an off-chain delivery channel a\n+  requirement rather than an add-on.\n+\n+On-chain memos are the only standard channel today. ZIP 231 [^zip-0231]\n+proposes to lift their 512-byte size cap, but memos would still cost fees, be\n+stored by every full validator forever, and need a block confirmation per\n+round, which rules out interactive protocols such as PCZT and FROST\n+exchanges.\n+\n+ZIP 324 [^zip-0324] delegates delivery to a channel the parties already\n+share, such as a secure messenger or a QR code, for the case where the sender\n+has no address for the recipient. It does not help when the sender does hold\n+a Unified Address, which is the case this ZIP covers.\n+\n+The idea is not new. Hornby's 2020 light wallet privacy review [^hornby-2020]\n+lists out-of-band transaction delivery over a mixnet as an option and notes\n+that scalable Zcash will need a mixnet or similar technology anyway. zips#629\n+[^zips-629] suggests a mixnet plus a \"private mailbox\" to keep offline\n+reception. The Nym × Zcash integration guide [^nym-zcash] covers routing a\n+wallet's chain-sync and transaction-submission traffic through the mixnet,\n+not wallet-to-wallet delivery. This ZIP fills that gap.\n+\n+\n+# Privacy Implications\n+\n+**Who can read Envelopes.** Envelopes are encrypted to an Orchard Receiver, so\n+anyone holding the account's Orchard incoming viewing key can decrypt them.\n+That is the trust model of incoming notes, but Envelopes can carry more: a\n+PCZT reveals a transaction the recipient is about to sign, including its\n+outputs. Users who share incoming viewing keys with auditors or watch-only\n+services should not expect Envelopes to be private from them.\n+\n+**Linkability.** A Mailbox Descriptor is a stable identifier. Two Unified\n+Addresses that carry the same descriptor are linked to one recipient by anyone\n+who sees both, which defeats diversified addresses. Unlinkable addresses\n+therefore need separate mailboxes, at the cost of more credentials to derive\n+and more mailboxes to fetch from. A `reply_to` address inside an Envelope has\n+the same property for the sender.\n+\n+**Network observers and the Mailbox operator.** The anonymity network hides\n+the sender's network identity from the operator and the recipient, and the\n+recipient's from the sender. The operator still sees how many Envelopes a\n+mailbox receives, their size classes, and when they are deposited and\n+fetched. In bindings where the recipient connects to its own mailbox\n+directly, as in the Nym sketch of Appendix A, the operator also learns the\n+recipient's network identity unless the connection is tunnelled, for example\n+over Tor. Network privacy is only as good as the anonymity set: while few\n+wallets use a binding, cover traffic carries most of the weight.\n+\n+**Timing.** A deposit made at the same moment as a related on-chain\n+broadcast, a fetch triggered by opening the app, or an immediate\n+acknowledgement each leak timing that an observer can correlate with chain\n+activity. The wallet behaviour rules in the specification reduce but do not\n+remove this.\n+\n+**No sender authentication and no forward secrecy.** Envelopes do not\n+authenticate their sender, so a Text payload can impersonate anyone; the\n+specification forbids presenting it as coming from a named party. Envelope\n+keys derive from long-lived keys, so compromise of an incoming viewing key\n+exposes every Envelope the adversary has recorded, as for on-chain notes.\n+\n+**Denial of service.** Anyone holding a Unified Address can fill its mailbox.\n+The cost falls on the mailbox's storage and the recipient's bandwidth; trial\n+decryption itself is cheap. Bindings are expected to document quotas, and the\n+unencrypted Envelope header lets a wallet see size classes before fetching\n+bodies where a binding allows that.\n+\n+\n+# Requirements\n+\n+- A sender holding only a recipient's Unified Address can deliver a message\n+  with no prior contact.\n+- The recipient need not be online when the message is sent.\n+- No long-term key material is needed beyond what the wallet derives from its\n+  seed, and a wallet restored from seed can recover its mailboxes on a\n+  best-effort basis.\n+- Neither the Mailbox operator nor a network observer learns the sender's\n+  network identity or links a sender to a recipient. The sender does not learn\n+  the recipient's network identity.\n+- An Envelope reveals nothing about its content beyond its size class.\n+- Wallets that implement ZIP 316 but not this ZIP can still parse and pay a\n+  Unified Address that carries a Mailbox Descriptor, provided the Typecode\n+  registration requested in the Specification is granted.\n+- The core protocol does not depend on any one network or token.\n+\n+\n+# Non-requirements\n+\n+- Sender authentication at the Envelope layer. Payload types that need it\n+  carry their own.\n+- Guaranteed delivery. Delivery is best-effort; protocols built on top have to\n+  handle loss.\n+- Forward secrecy.\n+- Group messaging.\n+- Hiding the recipient's network identity from its own Mailbox operator. A\n+  binding may provide this, and a wallet can add Tor, but the core does not\n+  require it.\n+\n+\n+# Specification\n+\n+## Overview\n+\n+    Sender wallet                                        Recipient wallet\n+         |                                                      ^\n+         | 1. read Orchard Receiver and Mailbox                 |\n+         |    Descriptor from the recipient's UA                |\n+         | 2. build and encrypt the Envelope                    | 5. trial-decrypt\n+         | 3. Deposit over the anonymity network                |    with ivk\n+         v                                                      |\n+      [ Mailbox ] -------------- 4. Fetch over the anonymity network\n+\n+## Mailbox Descriptors [mailbox-descriptors]\n+\n+A Mailbox Descriptor is encoded as:\n+\n+| Field            | Type                   | Description                      |\n+|------------------|------------------------|----------------------------------|\n+| `binding_id`     | `u16` (LE)             | Identifies the Transport Binding |\n+| `descriptor_len` | `compactSize`          | Length of `descriptor`           |\n+| `descriptor`     | `byte[descriptor_len]` | Binding-specific bytes           |\n+\n+Registered `binding_id` values:\n+\n+| `binding_id`                        | Binding                                      |\n+|-------------------------------------|----------------------------------------------|\n+| $\\mathtt{0x0000}$                   | Reserved                                     |\n+| $\\mathtt{0x0001}$                   | Nym mixnet (Appendix A, non-normative)       |\n+| $\\mathtt{0xFF00}$–$\\mathtt{0xFFFF}$ | Experimental; MUST NOT be used in production |\n+\n+## Unified Address Metadata Item [metadata-item]\n+\n+This ZIP defines a ZIP 316 Metadata Item, **Mailbox Descriptors**, with a\n+Typecode in the Non-MUST-understand Metadata range $\\mathtt{0xC0}$ to\n+$\\mathtt{0xDF}$, to be assigned by the ZIP Editors. [^zip-0316-metadata]\n+\n+The Item's value MUST parse as one or more Mailbox Descriptors, concatenated\n+with no trailing bytes, in the recipient's order of preference. Descriptors\n+with an unrecognised `binding_id` MUST be skipped, not rejected. A Sender\n+wallet SHOULD use the first descriptor whose binding it supports.\n+\n+A Unified Address that carries this Item MUST also carry an Orchard Receiver;\n+Envelopes are encrypted to that Receiver. The Item MUST NOT appear in a\n+Unified Viewing Key.\n+\n+A Mailbox Descriptor is a stable identifier. A wallet MUST NOT put the same\n+descriptor into two Unified Addresses that the user expects to be unlinkable.\n+Wallets SHOULD include the Item only in addresses generated for a purpose\n+that calls for messaging, and SHOULD NOT include it by default.\n+\n+If a binding's descriptors can go stale (for example in Appendix A, when the\n+recipient changes gateway), the wallet SHOULD also set an Address Expiry Time\n+Metadata Item [^zip-0316-expiry] on the address. Address Expiry Typecodes are\n+MUST-understand, so such an address can only be paid by Consumers that\n+implement ZIP 316 Revision 2; see [Open issues](#open-issues).\n+\n+### Typecode registration [typecode-registration]\n+\n+The ZIP 316 Typecode Registry [^zip-0316-registry] does not currently permit\n+Producers to include Non-MUST-understand Metadata in any address type, and\n+additions to the registry are made by 2xxx-series ZIPs. This ZIP requests the\n+following row, to be applied when it reaches Proposed status:\n+\n+| Typecode | Description         | R0 | R2 `zu` | R2 `tu` |\n+|----------|---------------------|----|---------|---------|\n+| TBD      | Mailbox Descriptors | ✅ | ✅      | ✅      |\n+\n+The Item has no effect on transaction construction, so it meets ZIP 316's\n+condition for `zu` addresses. Permitting it in Revision 0 addresses is what\n+lets wallets that predate this ZIP pay the address, because Consumers ignore\n+Items with Typecodes they do not recognise. If the ZIP Editors permit it only\n+in Revision 2 addresses, the compatibility requirement holds only for\n+Consumers that implement ZIP 316 Revision 2.\n+\n+## Envelope encryption [envelope-encryption]\n+\n+Let $(\\mathsf{d}, \\mathsf{pk_d})$ be the recipient's Orchard Receiver. Let\n+$\\mathsf{KA^{Orchard}}$ [^protocol-orchardkeyagreement],\n+$\\mathsf{DiversifyHash^{Orchard}}$ [^protocol-diversifyhash],\n+$\\mathsf{repr}_{\\mathbb{P}}$ and $\\mathsf{abst}_{\\mathbb{P}}$\n+[^protocol-pallasandvesta], and $\\mathsf{ivk}$\n+[^protocol-orchardkeycomponents] be as defined in the Zcash Protocol\n+Specification, and let $\\mathsf{LEBS2OSP}_{256}$ be used to encode Pallas\n+points as 32-byte strings as in $\\mathsf{KDF^{Orchard}}$.\n+[^protocol-orchardkdf]\n+\n+The sender:\n+\n+1. Computes $\\mathsf{g_d} = \\mathsf{DiversifyHash^{Orchard}}(\\mathsf{d})$.\n+2. Chooses $\\mathsf{esk}$ uniformly at random from\n+   $\\mathsf{KA^{Orchard}.Private}$. $\\mathsf{esk}$ MUST NOT be reused across\n+   Envelopes.\n+3. Computes $\\mathsf{epk} = \\mathsf{KA^{Orchard}.DerivePublic}(\\mathsf{esk}, \\mathsf{g_d})$,\n+   $\\mathsf{sharedSecret} = \\mathsf{KA^{Orchard}.Agree}(\\mathsf{esk}, \\mathsf{pk_d})$,\n+   and $\\mathsf{ephemeralKey} = \\mathsf{LEBS2OSP}_{256}(\\mathsf{repr}_{\\mathbb{P}}(\\mathsf{epk}))$.\n+4. Derives the 32-byte key\n+\n+   $\\hspace{2em}\\mathsf{K} = \\mathsf{BLAKE2b\\text{-}256}(\\texttt{\"Zcash\\_WalletMsg\\_\"}, \\mathsf{LEBS2OSP}_{256}(\\mathsf{repr}_{\\mathbb{P}}(\\mathsf{sharedSecret})) \\,||\\, \\mathsf{ephemeralKey})$\n+\n+   This is $\\mathsf{KDF^{Orchard}}$ with a different personalization, so\n+   Envelope keys are domain-separated from note encryption keys even though\n+   they share the key agreement.\n+5. Chooses the smallest size class (see [Envelope format](#envelope-format))\n+   whose padded size $N$ fits the plaintext, and pads the plaintext with zero\n+   bytes to exactly $N$ bytes.\n+6. Computes\n+\n+   $\\hspace{2em}\\mathsf{C} = \\mathsf{IETF\\_AEAD\\_CHACHA20\\_POLY1305.Encrypt}(\\mathsf{K}, \\mathsf{nonce}, \\mathsf{aad}, \\mathsf{plaintext})$\n+   [^RFC8439]\n+\n+   with $\\mathsf{nonce} = [\\mathtt{0x00}]^{12}$ and\n+   $\\mathsf{aad} = \\mathtt{version} \\,||\\, \\mathtt{size\\_class} \\,||\\, \\mathsf{ephemeralKey}$.\n+   The zero nonce is safe because $\\mathsf{K}$ is unique to each\n+   $\\mathsf{esk}$.\n+\n+The recipient, for each Orchard incoming viewing key $\\mathsf{ivk}$ it holds:\n+\n+1. Computes $\\mathsf{epk} = \\mathsf{abst}_{\\mathbb{P}}(\\mathsf{ephemeralKey})$\n+   and rejects the Envelope if the result is $\\bot$ or the identity\n+   $\\mathcal{O}_{\\mathbb{P}}$.\n+2. Computes $\\mathsf{sharedSecret} = \\mathsf{KA^{Orchard}.Agree}(\\mathsf{ivk}, \\mathsf{epk})$.\n+   Since $\\mathsf{pk_d} = \\mathsf{KA^{Orchard}.DerivePublic}(\\mathsf{ivk}, \\mathsf{g_d})$,\n+   this equals the sender's value.\n+3. Derives $\\mathsf{K}$ as above and attempts decryption with the same\n+   $\\mathsf{aad}$. On failure, the Envelope is not for this key.\n+4. Rejects the plaintext if it does not parse as described in\n+   [Plaintext format](#plaintext-format), or if any padding byte is nonzero.\n+5. Rejects the Envelope unless the plaintext's $\\mathsf{d}$ is the\n+   diversifier of an address of this account that this wallet issued either\n+   with a Mailbox Descriptors Item or as a `reply_to`.\n+\n+A wallet processes an Envelope under the first $\\mathsf{ivk}$ for which\n+decryption succeeds. Steps 1 and 2 cost one point decompression and one\n+scalar multiplication per Envelope and account.\n+\n+## Envelope format [envelope-format]\n+\n+| Field          | Type           | Description                                                             |\n+|----------------|----------------|-------------------------------------------------------------------------|\n+| `version`      | `byte`         | $\\mathtt{0x01}$                                                         |\n+| `size_class`   | `byte`         | See table below                                                         |\n+| `ephemeralKey` | `byte[32]`     | $\\mathsf{LEBS2OSP}_{256}(\\mathsf{repr}_{\\mathbb{P}}(\\mathsf{epk}))$     |\n+| `ciphertext`   | `byte[N + 16]` | AEAD output; $N$ is set by `size_class`                                 |\n+\n+| `size_class`    | Padded plaintext size $N$ | Intended use              |\n+|-----------------|---------------------------|---------------------------|\n+| $\\mathtt{0x00}$ | 2048 bytes                | Acks, URIs, short text    |\n+| $\\mathtt{0x01}$ | 32768 bytes               | Small PCZTs, FROST rounds |\n+| $\\mathtt{0x02}$ | 262144 bytes              | Large PCZTs               |\n+\n+Other values are reserved. Recipients MUST reject Envelopes with an unknown\n+`version` or `size_class`, or whose length does not match `size_class`.\n+\n+## Plaintext format [plaintext-format]\n+\n+| Field          | Type                | Description                                                   |\n+|----------------|---------------------|---------------------------------------------------------------|\n+| `payload_type` | `u16` (LE)          | See [Payload types](#payload-types)                           |\n+| `flags`        | `byte`              | Bit 0: `ack_requested`; see below                             |\n+| `d`            | `byte[11]`          | Diversifier of the Orchard Receiver the Envelope was sent to  |\n+| `message_id`   | `byte[32]`          | Uniformly random                                              |\n+| `in_reply_to`  | `byte[32]`          | `message_id` of the Envelope this one answers, or all zero    |\n+| `expiry_time`  | `u64` (LE)          | Unix seconds; $0$ means no expiry                             |\n+| `reply_len`    | `compactSize`       | Length of `reply_to`                                          |\n+| `reply_to`     | `byte[reply_len]`   | A Unified Address, or empty                                   |\n+| `payload_len`  | `compactSize`       | Length of `payload`                                           |\n+| `payload`      | `byte[payload_len]` | Type-specific                                                 |\n+| `padding`      | `byte[]`            | Zero bytes up to $N$                                          |\n+\n+Senders MUST set bits of `flags` other than bit 0 to $0$; recipients MUST\n+ignore bits they do not recognise.\n+\n+`d` lets the recipient attribute the Envelope to one of its addresses without\n+trusting `reply_to`, and `in_reply_to` lets protocols with several rounds\n+match answers to requests.\n+\n+`reply_to`, if present, is the UTF-8 string encoding of a Unified Address\n+[^zip-0316-encoding] whose Human-Readable Part matches the network of the\n+Envelope's recipient. It MUST carry an Orchard Receiver and MAY carry a\n+Mailbox Descriptors Item. A reply is encrypted to\n+that Receiver. It is deposited in the mailbox named by the Item if there is\n+one, or sent by a transport-level anonymous reply if the binding provides one\n+(see [Mailbox operations](#mailbox-operations)); if neither is available, no\n+reply is possible. A sender that wants replies without linking them to its\n+other addresses SHOULD use a Unified Address and mailbox dedicated to replies.\n+\n+Senders SHOULD round `expiry_time` to the hour, to avoid leaking clock skew.\n+\n+Recipients:\n+\n+- MUST drop Envelopes whose `expiry_time` is nonzero and in the past.\n+- MUST drop Envelopes whose `message_id` they have already processed, and\n+  SHOULD remember processed `message_id`s at least until their expiry, or for\n+  a wallet-chosen period when `expiry_time` is $0$.\n+- MUST ignore the payload of an Envelope with an unknown `payload_type`, but\n+  still record its `message_id`.\n+\n+## Payload types [payload-types]\n+\n+| `payload_type`                      | Name            | Payload                                                |\n+|-------------------------------------|-----------------|--------------------------------------------------------|\n+| $\\mathtt{0x0000}$                   | Reserved        | —                                                      |\n+| $\\mathtt{0x0001}$                   | Ack             | Empty; `in_reply_to` names the acknowledged Envelope   |\n+| $\\mathtt{0x0002}$                   | Text            | UTF-8 text for display to the user                     |\n+| $\\mathtt{0x0003}$                   | Payment request | UTF-8 ZIP 321 URI [^zip-0321]                          |\n+| $\\mathtt{0x0004}$                   | PCZT            | Serialized PCZT [^pczt]; format reference TBD          |\n+| $\\mathtt{0x0005}$                   | Reserved        | FROST round messages (ZIP 312 [^zip-0312]); format TBD |\n+| $\\mathtt{0x0006}$                   | Reserved        | Off-chain note delivery [^tachyon]; format TBD         |\n+| $\\mathtt{0xF000}$–$\\mathtt{0xFFFF}$ | Experimental    | MUST NOT be used in production                         |\n+\n+An Ack MUST NOT set `ack_requested`.\n+\n+Payloads MUST NOT carry spend authority, such as spending keys or ZIP 324 URIs\n+[^zip-0324], because every holder of the recipient's incoming viewing key can\n+read them.\n+\n+Payload types that cause side effects SHOULD carry their own replay\n+protection; `message_id` deduplication and `expiry_time` only bound replay.\n+\n+## Mailbox operations [mailbox-operations]\n+\n+A Transport Binding MUST provide these operations:\n+\n+- **Deposit**(descriptor, envelope): deliver an Envelope to the Mailbox named\n+  by the descriptor.\n+- **Fetch**(credential) → envelopes: retrieve held Envelopes.\n+- **Delete**(credential, handles): remove fetched Envelopes, unless the\n+  binding documents that Fetch removes them implicitly.\n+\n+A Transport Binding MUST hide the sender's network identity from the Mailbox\n+operator and from the recipient, and MUST hide the recipient's network\n+identity from the sender. It SHOULD hide the recipient's network identity from\n+the Mailbox operator, and SHOULD resist timing correlation by a global\n+passive adversary. Bindings SHOULD rate-limit Deposit and SHOULD bound the\n+storage a mailbox can consume.\n+\n+A Transport Binding specification MUST state:\n+\n+- the descriptor encoding;\n+- how the 64-byte mailbox key from\n+  [Mailbox key derivation](#mailbox-key-derivation) is turned into the\n+  binding's credentials;\n+- the retention period, or how a wallet learns it;\n+- per-mailbox storage quotas, if any;\n+- which size classes it supports;\n+- how replies work: by depositing to a `reply_to` mailbox, by a\n+  transport-level anonymous reply, or both, and how long each path stays\n+  usable;\n+- rate limiting and cost, if any;\n+- what network identity, timing, and size information each party can\n+  observe.\n+\n+## Mailbox key derivation [mailbox-key-derivation]\n+\n+Mailbox credentials are derived from the wallet seed $S$ with ZIP 32\n+registered key derivation [^zip-0032-registered], using\n+$\\mathsf{ContextString} = \\texttt{\"Zcash\\_WalletMessaging\"}$ and\n+$\\mathsf{ZipNumber}$ equal to the number assigned to this ZIP:\n+\n+- $(\\mathsf{sk}_{subtree}, \\mathsf{c}_{subtree}) = \\mathsf{RegKD}(\\mathsf{ContextString}, S, \\mathsf{ZipNumber})$\n+- $(\\mathsf{sk}_{n}, \\mathsf{c}_{n}) = \\mathsf{CKDreg}((\\mathsf{sk}_{subtree}, \\mathsf{c}_{subtree}), \\mathsf{coin\\_type} + 2^{31}, [\\,])$\n+- $(\\mathsf{sk}_{b}, \\mathsf{c}_{b}) = \\mathsf{CKDreg}((\\mathsf{sk}_{n}, \\mathsf{c}_{n}), \\mathtt{binding\\_id} + 2^{31}, [\\,])$\n+- $(\\mathsf{sk}_{a}, \\mathsf{c}_{a}) = \\mathsf{CKDreg}((\\mathsf{sk}_{b}, \\mathsf{c}_{b}), \\mathsf{account} + 2^{31}, [\\,])$\n+- $\\mathsf{K}_{i} = \\mathsf{derive\\_child\\_cryptovalue}((\\mathsf{sk}_{a}, \\mathsf{c}_{a}), i + 2^{31}, [\\,])$\n+\n+where $\\mathsf{coin\\_type}$ is the ZIP 32 coin type of the network\n+[^zip-0032-paths], so that Mainnet and Testnet mailboxes are unlinkable;\n+$\\mathsf{account}$ is the ZIP 32 account index of the Orchard key whose\n+addresses carry the descriptor; and $i$ is a per-binding, per-account counter\n+that the wallet increments for each new mailbox. $\\mathsf{K}_{i}$ is 64\n+bytes; each binding states how it is split into credentials.\n+\n+A wallet restored from seed can rebuild its credentials by scanning $i$ from\n+$0$ with a gap limit, subject to the binding's own limits (for example,\n+knowing which gateway each mailbox used).\n+\n+## Wallet behaviour [wallet-behaviour]\n+\n+Sending:\n+\n+- Wallets SHOULD NOT deposit an Envelope at the same moment as a related\n+  on-chain broadcast, and SHOULD add a random delay when the use is not\n+  interactive.\n+- Wallets MUST NOT fall back to a direct connection to a Mailbox when the\n+  binding's anonymity network is unavailable.\n+\n+Fetching:\n+\n+- Wallets SHOULD fetch on a randomized schedule that does not depend on user\n+  actions, including when no message is expected.\n+- Wallets SHOULD cap the bytes they fetch and trial-decrypt per Fetch.\n+\n+Acknowledgements:\n+\n+- Wallets SHOULD send an Ack only when `ack_requested` is set, and SHOULD\n+  delay it by a random amount.\n+\n+Display:\n+\n+- Envelopes are not authenticated. Wallets MUST NOT present a message as\n+  coming from a named party unless the payload type itself authenticates the\n+  sender, and SHOULD NOT open links in Text payloads without user\n+  confirmation.\n+\n+Viewing keys:\n+\n+- Wallets that export an account's incoming viewing key to a third party\n+  SHOULD NOT advertise mailboxes on that account's addresses.\n+\n+\n+# Rationale\n+\n+**Why reuse Orchard key agreement?** It needs no new keys and no prior\n+contact: any Unified Address with an Orchard Receiver is already a public\n+encryption key. The cost is that incoming viewing key holders can read\n+Envelopes; a dedicated messaging key in the descriptor would avoid that at\n+the cost of a larger Item.\n+\n+**Why fixed size classes?** Exact lengths would leak payload types and\n+content. Three classes keep the anonymity set per class large while still\n+allowing PCZTs.\n+\n+**Why no ZIP 324 payload?** A sender that can use this channel already holds\n+the recipient's Unified Address and can pay it on chain, and a ZIP 324 URI\n+carries spend authority that every incoming viewing key holder could use.\n+\n+**Why a transport-agnostic core?** Tying a standard to one network and its\n+token is a long-term risk. Nym is sketched first because it is live,\n+packet-based, uses cover traffic, stores messages for offline clients, and\n+offers sender-anonymous replies.\n+\n+\n+# Reference implementation\n+\n+None yet.\n+\n+\n+# Open issues [open-issues]\n+\n+- Assignment of the Metadata Item Typecode, whether it is permitted in\n+  Revision 0 addresses, and this ZIP's number, which the key derivation\n+  depends on.\n+- Whether to encrypt to a dedicated messaging key carried in the descriptor\n+  instead of to the Orchard Receiver, so that incoming viewing key holders\n+  cannot read Envelopes.\n+- Address Expiry Items make an address unpayable by ZIP 316 Revision 0\n+  Consumers, while the compatibility requirement assumes they can pay it.\n+  Whether to recommend expiry, shorter-lived descriptors, or neither.\n+- The cost of running one mailbox per unlinkable address, and whether wallets\n+  should offer a deliberately linkable \"contact\" mode.\n+- A stable reference for the PCZT serialization.\n+- The FROST round message format and its authentication layer.\n+- The off-chain note delivery format, to be aligned with Tachyon.\n+- Support for Sapling-only recipients.\n+- A Tor onion-service binding.\n+- Whether the Nym binding in Appendix A becomes normative in this ZIP or in a\n+  separate one, once its TBDs are resolved.\n+\n+\n+# Appendix A: Nym transport binding (non-normative) [nym-binding]\n+\n+This appendix sketches a binding for the Nym mixnet so that the core protocol\n+can be evaluated against a real network. It is not a complete Transport\n+Binding specification: the items marked TBD have to be resolved before it can\n+be made normative. `binding_id` $\\mathtt{0x0001}$ is reserved for it.\n+\n+**Descriptor.** The 96-byte binary form of a Nym client address,\n+[^nym-addressing] [^nym-recipient] laid out as follows:\n+\n+| Field                 | Type       | Description                       |\n+|-----------------------|------------|-----------------------------------|\n+| client identity key   | `byte[32]` | Ed25519 public key                |\n+| client encryption key | `byte[32]` | X25519 public key                 |\n+| gateway identity key  | `byte[32]` | Ed25519 public key of the gateway |\n+\n+**Credentials.** The first 32 bytes of $\\mathsf{K}_{i}$ would be the Ed25519\n+seed of the client identity key, and the last 32 bytes the X25519 secret key\n+[^RFC7748] of the client encryption key. The Nym Rust SDK accepts externally\n+supplied client keys through a custom key store. [^nym-sdk-keys]\n+\n+**Deposit.** The sender sends the Envelope as a Nym message to the\n+descriptor's address through the mixnet, from an ephemeral Nym client with\n+fresh keys rather than from its own mailbox client, attaching reply SURBs\n+[^nym-surbs] if it expects a prompt reply. Each ephemeral client has to\n+register with an entry gateway and hold bandwidth credentials; the cost and\n+latency of doing so per message are unverified (TBD).\n+\n+**Fetch.** The recipient runs a Nym client with the derived keys, connected to\n+the gateway named in the descriptor, with cover traffic on. The gateway stores\n+messages while the client is offline and delivers them when it connects.\n+[^nym-addressing]\n+\n+**Delete.** Implicit on delivery by the gateway (TBD: confirm).\n+\n+**Retention.** Not documented by Nym (TBD).\n+\n+**Size classes.** All. Nym splits a message into fixed-size Sphinx packets,\n+so larger classes take more packets and leave a larger timing footprint.\n+\n+**Replies.** SURB reply keys expire after 24 hours, [^nym-surbs] so SURBs\n+serve interactive exchanges only. A recipient that fetches later can reply\n+only to a `reply_to` address that carries a Nym descriptor.\n+\n+**Rate limiting and cost.** Sending through Nym consumes bandwidth\n+credentials. Who pays, and whether they can be bought with shielded ZEC, is\n+TBD.\n+\n+**Gateway changes.** The gateway is part of the descriptor. If the recipient\n+moves gateway, old descriptors stop working and a new Unified Address has to\n+be issued. How a wallet restored from seed learns which gateway each mailbox\n+index used is TBD.\n+\n+**Observability.** The recipient's gateway, which is the Mailbox operator,\n+sees the recipient's connection, including its IP address unless the\n+connection is tunnelled over Tor, and the count and size of stored messages.\n+The sender's entry gateway sees the sender's IP address but not the\n+destination. Mix nodes see packets only. A global passive adversary faces\n+Nym's mixing delays and cover traffic.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol-orchardkeycomponents]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.2.3: Orchard Key Components](protocol/protocol.pdf#orchardkeycomponents)\n+\n+[^protocol-diversifyhash]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions](protocol/protocol.pdf#concretediversifyhash)\n+\n+[^protocol-orchardkeyagreement]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.5.5: Orchard Key Agreement](protocol/protocol.pdf#concreteorchardkeyagreement)\n+\n+[^protocol-orchardkdf]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.5.6: Orchard Key Derivation](protocol/protocol.pdf#concreteorchardkdf)\n+\n+[^protocol-pallasandvesta]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.9.6: Pallas and Vesta](protocol/protocol.pdf#pallasandvesta)\n+\n+[^RFC7748]: [RFC 7748: Elliptic Curves for Security](https://www.rfc-editor.org/rfc/rfc7748.html)\n+\n+[^RFC8439]: [RFC 8439: ChaCha20 and Poly1305 for IETF Protocols](https://www.rfc-editor.org/rfc/rfc8439.html)\n+\n+[^Bitcoin-CompactSize]: [Variable length integer. Bitcoin Wiki](https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer)\n+\n+[^zip-0032-registered]: [ZIP 32: Shielded Hierarchical Deterministic Wallets — Specification: Registered key derivation](zip-0032.rst#specification-registered-key-derivation)\n+\n+[^zip-0032-paths]: [ZIP 32: Shielded Hierarchical Deterministic Wallets — Key path levels](zip-0032.rst#key-path-levels)\n+\n+[^zip-0231]: [ZIP 231: Memo Bundles](zip-0231.md)\n+\n+[^zip-0312]: [ZIP 312: FROST for Spend Authorization Multisignatures](zip-0312.rst)\n+\n+[^zip-0316]: [ZIP 316: Unified Addresses and Unified Viewing Keys](zip-0316.rst)\n+\n+[^zip-0316-encoding]: [ZIP 316: Unified Addresses and Unified Viewing Keys — Encoding of Unified Addresses](zip-0316.rst#encoding-of-unified-addresses)\n+\n+[^zip-0316-metadata]: [ZIP 316: Unified Addresses and Unified Viewing Keys — Metadata Items](zip-0316.rst#metadata-items)\n+\n+[^zip-0316-expiry]: [ZIP 316: Unified Addresses and Unified Viewing Keys — Address Expiration Metadata](zip-0316.rst#address-expiration-metadata)\n+\n+[^zip-0316-registry]: [ZIP 316: Unified Addresses and Unified Viewing Keys — Typecode Registry](zip-0316.rst#typecode-registry)\n+\n+[^zip-0321]: [ZIP 321: Payment Request URIs](zip-0321.rst)\n+\n+[^zip-0324]: [ZIP 324: URI-Encapsulated Payments](zip-0324.rst)\n+\n+[^pczt]: [Partially Created Zcash Transactions (librustzcash `pczt` crate)](https://github.com/zcash/librustzcash/tree/main/pczt)\n+\n+[^zips-629]: [zips#629: Scalability Design Explorations around changing Payment Flows](https://github.com/zcash/zips/issues/629)\n+\n+[^hornby-2020]: [Taylor Hornby, \"Fixing Privacy Problems in the Zcash Light Wallet Protocol\" (2020)](https://defuse.ca/downloads/Fixing%20Privacy%20Problems%20in%20the%20Zcash%20Light%20Wallet%20Protocol.pdf)\n+\n+[^tachyon]: [Sean Bowe and Ian Miers, \"A Note on Notes: Towards Scalable Anonymous Payments via Evolving Nullifiers and Oblivious Synchronization\", IACR ePrint 2025/2031](https://eprint.iacr.org/2025/2031)\n+\n+[^tachyon-post]: [Sean Bowe, \"Tachyon: Scaling Zcash with Oblivious Synchronization\" (Zcash Community Forum thread)](https://forum.zcashcommunity.com/t/scaling-zcash-tachyon-ragu/50789)\n+\n+[^nym]: [Nym documentation](https://nym.com/docs)\n+\n+[^nym-addressing]: [Nym documentation: Addressing System](https://nym.com/docs/network/reference/addressing)\n+\n+[^nym-recipient]: [`nym-sphinx-addressing` crate: `Recipient` (source)](https://docs.rs/nym-sphinx-addressing/latest/src/nym_sphinx_addressing/clients.rs.html)\n+\n+[^nym-surbs]: [Nym documentation: Anonymous Replies (SURBs)](https://nym.com/docs/network/mixnet-mode/anonymous-replies)\n+\n+[^nym-sdk-keys]: [`nym-sdk` crate: `shared_identity.rs` example](https://docs.rs/crate/nym-sdk/latest/source/examples/shared_identity.rs)\n+\n+[^nym-zcash]: [Nym × Zcash Integration Guide](https://zcash-sdk.nym.com/)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-arya-wallet-to-wallet-messaging.md","title":"Wallet-to-Wallet Messaging","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"overview","text":"Overview"},{"level":2,"id":"mailbox-descriptors","text":"Mailbox Descriptors"},{"level":2,"id":"metadata-item","text":"Unified Address Metadata Item"},{"level":3,"id":"typecode-registration","text":"Typecode registration"},{"level":2,"id":"envelope-encryption","text":"Envelope encryption"},{"level":2,"id":"envelope-format","text":"Envelope format"},{"level":2,"id":"plaintext-format","text":"Plaintext format"},{"level":2,"id":"payload-types","text":"Payload types"},{"level":2,"id":"mailbox-operations","text":"Mailbox operations"},{"level":2,"id":"mailbox-key-derivation","text":"Mailbox key derivation"},{"level":2,"id":"wallet-behaviour","text":"Wallet behaviour"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"nym-binding","text":"Appendix A: Nym transport binding (non-normative)"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1433,"title":"Add draft ZIP: Lock-weighted Coinholder Polling","author":"arya2","createdAt":"2026-11-03T15:47:38Z","updatedAt":"2026-11-03T15:47:38Z","url":"https://github.com/zcash/zips/pull/1433","diffUrl":"https://github.com/zcash/zips/pull/1433.diff","headRef":"draft-lock-weighted-voting","headRepo":"zcash/zips","headSha":"c921894d8418382a7e9495f4d6f7f4045403702e","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Draft ZIP, Process category, Status: Draft. Proposes that coinholder polls weight each ballot by how long the voter's ZEC is locked past the snapshot, not only by how much ZEC they hold. Weight is linear in lock length up to a cap of one halving interval; unlocked coins keep a floor weight of one te","zipRefs":[1016],"draftRefs":["draft-arya-lock-weighted-voting"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-arya-lock-weighted-voting.md","status":"added","additions":400,"deletions":0,"patch":"@@ -0,0 +1,400 @@\n+    ZIP: Unassigned\n+    Title: Lock-weighted Coinholder Polling\n+    Owners: Arya <arya@zfnd.org>\n+    Status: Draft\n+    Category: Process\n+    Created: 2026-11-03\n+    License: MIT\n+    Discussions-To: <URL TBD>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and only\n+when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as\n+defined in the Zcash protocol specification [^protocol-networks].\n+\n+The term \"*Ironwood pool*\" is to be interpreted as described in ZIP 229\n+[^zip-0229].\n+\n+The terms \"Coinholder-Controlled Fund\" and \"Key-Holder Organizations\" are to be\n+interpreted as described in ZIP 1016 [^zip-1016]. The term \"Deferred Dev Fund\n+Lockbox\" is to be interpreted as described in ZIP 1015 [^zip-1015].\n+\n+Poll\n+\n+: A vote in which each ballot carries a ZEC amount.\n+\n+Poll operator\n+\n+: Whoever announces a poll, verifies ballots, and publishes results.\n+\n+Snapshot height\n+\n+: The block height $S$ at which holdings are measured.\n+\n+Close height\n+\n+: The height $C$ of the last block in which a ballot can be accepted.\n+\n+Lock record\n+\n+: A triple $(v, u, c)$: a value $v$ in zatoshi, an unlock height $u$ at or\n+  before which consensus forbids spending it ($u = 0$ if unlocked), and an\n+  evidence class $c$. Its lock length is $\\ell = \\max(0, u - S)$ blocks.\n+\n+\n+# Abstract\n+\n+Coinholder polls count one ZEC as one vote at a snapshot height, and coins may\n+be moved right after the snapshot. This ZIP changes only the weighting: a\n+ballot's weight rises linearly with how long its coins stay locked past the\n+snapshot, up to a cap of one halving interval; unlocked coins keep a small\n+floor weight. One evidence class works today: transparent outputs locked\n+with `OP_CHECKLOCKTIMEVERIFY`. A second class for shielded locks is reserved\n+for a consensus ZIP. Quorum and the ZIP 1016 participation threshold stay in\n+unweighted ZEC. The transparent class reveals the voter's output, amount, and\n+unlock height on chain, which the current shielded polling path does not; the\n+shielded class is the intended end state.\n+\n+\n+# Motivation\n+\n+Coinholder polling gives holders a voice alongside developers, users, and\n+miners. ZIP 1016 [^zip-1016] makes it the deciding input for the\n+Coinholder-Controlled Fund, and about 2.4 million of 3.6 million eligible ZEC\n+voted in the NU7 scope poll of September 2026 [^coindesk-nu7].\n+\n+The status quo is one ZEC, one vote, at a snapshot, with coins movable the\n+moment the snapshot passes [^forum-nu7-vote]. Someone who buys the week of\n+the vote and sells the week after is not a holder; they are renting a vote.\n+This ZIP favours people credibly in it for the long haul. Someone willing to\n+lock ZEC for years should have a big say, not the only say. A whale who locks\n+for years still gets a big vote; they have put their coins where their vote\n+is. The change removes the cheap vote, not the big one.\n+\n+## Scope decision\n+\n+There is no timelock for shielded notes. Transparent outputs can be locked\n+with `OP_CHECKLOCKTIMEVERIFY` [^bip-0065], which Zebra enforces on every\n+transparent input [^zebra-script] `[VERIFY: enforced by zcashd since\n+genesis]`. Sapling, Orchard, and Ironwood notes have no script and no lock\n+field, and a transaction's `lock_time` is ignored when no transparent input\n+has a non-final sequence number [^zebra-locktime], so it cannot lock a\n+shielded-only spend. Three options were considered:\n+\n+1. A process-only ZIP using transparent locks. It works at the protocol\n+   level, not the tooling level: the live mechanism accepts only notes in the\n+   *Ironwood pool* [^zcashlabs-voting]. It asks holders to unshield for full\n+   weight, which is at odds with the point of Zcash. Not acceptable as the\n+   only option.\n+2. A consensus ZIP adding a lock to shielded notes. The smallest version is a\n+   lock-height field in the Ironwood note commitment, one circuit constraint,\n+   and one consensus rule (see [Class S]). That is a new circuit version for a\n+   pool created, with formal verification, after the Orchard soundness\n+   vulnerability [^zip-0229]. It is not small and needs a network upgrade.\n+3. Two ZIPs: this process ZIP, defining weighting over any lock the protocol\n+   can prove, with the transparent class as an interim, plus a later consensus\n+   ZIP for shielded locks.\n+\n+Option 3 is chosen. Everything below is defined over lock records, so the\n+shielded class can be added without rewriting this ZIP.\n+\n+\n+# Privacy Implications\n+\n+Under the live mechanism, validators learn neither who voted nor how much ZEC\n+a ballot carried; amounts are encrypted and only totals are decrypted\n+[^zcashlabs-voting]. A ballot under [Class T] reveals an output, its value,\n+and its unlock height on chain to everyone, and links the ballot to that\n+output unless the operator encrypts ballot contents. A voter who unshields to\n+lock also reveals the unshielding transaction. That cost is why Class S is the\n+intended end state.\n+\n+The shielded class is required to reveal no more than the current shielded\n+path: the lock length stays a private witness, the multiplier is applied\n+inside the proof, and locked notes are indistinguishable from unlocked notes\n+on chain, including when spent.\n+\n+\n+# Requirements\n+\n+* A ballot's weight depends on both the amount of ZEC and how long it is\n+  locked past the snapshot.\n+* Borrowing, hedging, or splitting to gain weight costs more than under\n+  amount-only weighting.\n+* Weighting is defined over an abstract lock record, so shielded locks can be\n+  added without changing the rules.\n+* The shielded class reveals no more than the current shielded polling path.\n+* Coins with no holder, such as protocol-level lockboxes, cannot be voted.\n+\n+\n+# Non-requirements\n+\n+* This ZIP does not change who is eligible to vote, what polls are held, or\n+  what polls decide. It changes weighting only.\n+* It does not propose on-chain binding governance. Polls remain what ZIP 1016\n+  says they are.\n+* It does not change mining, block subsidy, or funding streams.\n+* It does not verify custody.\n+\n+\n+# Specification\n+\n+## Parameters\n+\n+$S$ and $C$ are chosen by the poll operator per poll. The floor is\n+$f = 1/10$. The cap $L$ is the halving interval in effect at height $S$:\n+$\\mathsf{PostBlossomHalvingInterval} = 1{,}680{,}000$ blocks before NU7\n+activation and $\\mathsf{PostNU7HalvingInterval} = 5{,}040{,}000$ blocks from\n+NU7 activation [^zip-0218]. Both are about four years.\n+\n+## Weight function\n+\n+For a lock record $(v, u, c)$ with lock length $\\ell = \\max(0, u - S)$:\n+\n+$$m(\\ell) = f + (1 - f) \\cdot \\frac{\\min(\\ell, L)}{L}, \\qquad w = v \\cdot m(\\ell)$$\n+\n+If $u \\leq C$, the record MUST be counted as unlocked ($\\ell = 0$). A ballot's\n+weight is the sum of $w$ over its lock records.\n+\n+| $\\ell / L$ | $m(\\ell)$ | at the four-year cap |\n+|-----------:|----------:|----------------------|\n+| 0          | 0.100     | unlocked             |\n+| 1/16       | 0.156     | 3 months             |\n+| 1/8        | 0.213     | 6 months             |\n+| 1/4        | 0.325     | 1 year               |\n+| 1/2        | 0.550     | 2 years              |\n+| 1 or more  | 1.000     | 4 years or more      |\n+\n+## Lock records\n+\n+An operator MUST accept Class H and at least one lock class, and MUST reject\n+any ballot whose evidence fails verification; a failed record MUST NOT be\n+counted under Class H.\n+\n+### Class H\n+\n+Class H evidence is whatever proof of holding at $S$ the operator already\n+accepts, such as a proof of unspent Ironwood notes. It yields $(v, 0,\n+\\mathrm{H})$ and counts at the floor.\n+\n+### Class T\n+\n+Class T evidence is a transparent output whose `scriptPubKey` is a\n+pay-to-script-hash of the redeem script\n+\n+    <u> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG\n+\n+where `<u>` is a minimally encoded script number with\n+$1 \\leq u < 500{,}000{,}000$ (a block height, not a Unix time) [^bip-0065].\n+The ballot carries the outpoint, the redeem script, and an ECDSA signature\n+over secp256k1 by `pubkey`, as for `OP_CHECKSIG`, over a message $M$ defined\n+by the operator. $M$ MUST include a poll identifier and MUST bind the ballot\n+contents or a commitment to them.\n+\n+The operator MUST check that:\n+\n+1. the output exists and is unspent in the chain state at height $S$;\n+2. the `scriptPubKey` equals the P2SH encoding of the supplied redeem script;\n+3. the redeem script matches the template and $u$ is in height form;\n+4. the signature is valid for `pubkey` over $M$;\n+5. the outpoint has not been used in another ballot of this poll.\n+\n+The lock record is $(v, u, \\mathrm{T})$ with $v$ the output's value.\n+Operators MAY also accept an `OP_CHECKMULTISIG` variant of the template with\n+$m$ valid signatures.\n+\n+### Class S\n+\n+Class S, shielded locks, is reserved for a consensus ZIP. That ZIP MUST\n+provide: a way to fix an unlock height at note creation that consensus\n+enforces on spends; a proof usable within the operator's shielded path that\n+yields a lock record for notes unspent at $S$, with the multiplier applied\n+inside the proof; and locked notes indistinguishable from unlocked notes on\n+chain, including when spent.\n+\n+The smallest design known: a lock-height field inside the\n+Ironwood note commitment (zero for none); a circuit constraint that the field\n+is at most a per-bundle minimum height; a consensus rule that a transaction\n+with a non-zero minimum height is invalid in any block below it; and wallets\n+that set that value on every transaction so locked spends do not stand out.\n+`[VERIFY: fit with ZIP 229 and the Ironwood circuit]`\n+\n+## Counting\n+\n+For each option the operator MUST compute the weighted total $W$, the sum of\n+ballot weights, and the unweighted total $V$, the sum of $v$ over the same\n+ballots. The outcome among options MUST be decided on $W$. Any quorum, and the\n+420,000 ZEC participation threshold of ZIP 1016 [^zip-1016], MUST be measured\n+on $V$.\n+\n+## Excluded coins\n+\n+Value in the Deferred Dev Fund Lockbox [^zip-2001] and the NSM value balance\n+[^zip-0237] are chain value pool balances, not outputs or notes. They cannot\n+produce lock records and MUST NOT be counted.\n+\n+## Poll operator obligations\n+\n+Before height $S$ the operator MUST publish $S$, $C$, $L$, $f$, the accepted\n+evidence classes, and the format of $M$. The operator SHOULD NOT verify\n+Class T evidence until the block at height $S$ has at least 100\n+confirmations. After the poll the operator MUST publish $W$ and $V$ for every\n+option and SHOULD publish only totals. Operators MAY rehearse on Testnet,\n+where Mainnet thresholds do not apply.\n+\n+\n+# Security Considerations\n+\n+**Borrow-to-vote.** Borrow ZEC, lock it, vote, repay after unlock. At an\n+annual borrowing cost $r$, a one-week unlocked borrow costs about $r/52$ of\n+$v$ for weight $0.1\\,v$; a lock of $L$ costs about $4r$ of $v$ for weight $v$,\n+roughly twenty times more per unit of weight, and the loan must run until\n+$u$.\n+\n+**Lock-and-hedge.** Lock $v$ and short $v$ elsewhere, voting with no net\n+exposure. This costs funding and margin over $\\ell$ and remains possible; the\n+design raises cost, it does not make this impossible.\n+\n+**Splitting.** Because $m$ is linear below the cap, weight per ZEC-block is\n+constant, so no partition of a holding across lock lengths changes its total\n+weight.\n+\n+**Last-minute locks.** A lock placed the day before $S$ that reaches past $L$\n+counts in full. The forward commitment is the same whenever the lock was\n+placed; what such a voter lacks is past holding, which this ZIP does not\n+weigh. See [Open questions].\n+\n+**Vote buying** is unchanged in kind. A seller must lock to sell full weight,\n+so the price rises, but the trade exists.\n+\n+**Custodians.** A custodian that owes coins on demand cannot lock them without\n+a liquidity mismatch, so voting customer coins at full weight gets harder.\n+That is intended. The limits: a custodian can lock its own float or accept\n+the mismatch, and this ZIP does not verify custody.\n+\n+\n+# Rationale\n+\n+## Alternatives considered\n+\n+*Amount only (status quo).* One ZEC, one vote, movable right after the\n+snapshot [^forum-nu7-vote]. The vote can be rented for a week's exposure.\n+\n+*One person, one vote.* Zcash has no sybil-resistant identity. The Zcash\n+Community Advisory Panel already gives a per-person signal alongside\n+coinholder polls [^zfnd-nu7-results].\n+\n+*Square root or logarithm of amount.* Without identity, any concave function\n+of amount is defeated by splitting a holding across ballots.\n+\n+*Conviction tiers* [^polkadot-opengov]. Multipliers that step with lock\n+length let a holder pick the best point on the curve by splitting. Linear with\n+a cap, as in Curve's vote-escrow [^curve-vecrv], does not.\n+\n+*Age weighting.* Weight by how long coins have already been held. For\n+shielded notes this is provable today without a consensus change: the note\n+commitment tree is append-only, so a note's position bounds its creation\n+height, and the proof-of-balance draft [^draft-str4d-balance-proof] already\n+proves holding at a snapshot. In the *Ironwood pool*, age is bounded by the\n+pool's activation in July 2026 [^zip-0258] and reset by migration\n+[^zip-0318]. It is backward-looking, so it does not express willingness to\n+stay, but it defeats buy-vote-sell and last-minute locks directly. Not\n+adopted here; see [Open questions].\n+\n+*Time-averaged balance.* Several snapshots, several proofs per ballot, still\n+passive. A forward lock supplies its own window.\n+\n+## Rationale for the cap and floor\n+\n+The cap is one halving interval: the longest horizon over which the protocol\n+itself plans issuance, and the four-year maximum of vote-escrow systems\n+[^curve-vecrv]. The floor is non-zero so that this ZIP does not change who\n+can vote; zero would exclude every shielded holder until Class S exists. One\n+tenth is the weight of an unlocked vote in Polkadot's conviction voting\n+[^polkadot-opengov].\n+\n+## Rationale for measuring in blocks\n+\n+`OP_CHECKLOCKTIMEVERIFY` heights are blocks, and NU7 cuts the target spacing\n+from 75 to 25 seconds [^zip-0218], so a lock expressed in time would change\n+meaning at activation. Measuring $\\ell$ and $L$ in blocks at height $S$ keeps\n+the weight well defined.\n+\n+\n+# Open questions\n+\n+* Will any poll operator add a Class T path to a shielded-only mechanism, and\n+  what is $M$ there?\n+* Should past holding (age) count toward $\\ell$, generally or as the shielded\n+  interim before Class S?\n+* Are $f = 1/10$ and $L$ = one halving interval right? Should $f$ drop to\n+  zero once Class S exists?\n+* Should a lock have to reach past a fixed horizon rather than only past $C$?\n+* Should Coinholder-Controlled Fund coins held by the Key-Holder\n+  Organizations [^zip-0271] be excluded? ZIP 1016 restricts no one.\n+* Should Class T accept time-form or `OP_CHECKSEQUENCEVERIFY` locks\n+  (ZIP 112 [^zip-0112])?\n+* ZIP 1016 decides on ZEC cast in favour and, between competing grants, on\n+  ZEC committed in support. Deciding on $W$ changes that; applying this ZIP\n+  to ZIP 1016 votes needs its Owners and an Updates header.\n+* Which network upgrade could carry Class S? `[VERIFY: NU8 planning]`\n+\n+\n+# Deployment\n+\n+This ZIP applies to a poll when the operator announces, before $S$, that the\n+poll follows it. Class T is usable once an operator implements its\n+verification. Class S is usable only after its consensus ZIP activates on\n+Mainnet; until then, shielded coins vote under Class H at the floor weight.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^zip-0112]: [ZIP 112: CHECKSEQUENCEVERIFY](zip-0112.rst)\n+\n+[^zip-0218]: [ZIP 218: 25-second Block Target Spacing](zip-0218.md)\n+\n+[^zip-0229]: [ZIP 229: Version 6 Transaction Format](zip-0229.md)\n+\n+[^zip-0237]: [ZIP 237: Network Sustainability Mechanism](zip-0237.md)\n+\n+[^zip-0258]: [ZIP 258: Deployment of the NU6.3 Network Upgrade](zip-0258.md)\n+\n+[^zip-0271]: [ZIP 271: Deferred Dev Fund Lockbox Disbursement](zip-0271.md)\n+\n+[^zip-0318]: [ZIP 318: Orchard to Ironwood Migration](zip-0318.md)\n+\n+[^zip-1015]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding](zip-1015.rst)\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](zip-1016.md)\n+\n+[^zip-2001]: [ZIP 2001: Lockbox Funding Streams](zip-2001.rst)\n+\n+[^draft-str4d-balance-proof]: [draft-str4d-orchard-balance-proof: Air drops, Proof-of-Balance, and Stake-weighted Polling](draft-str4d-orchard-balance-proof.md)\n+\n+[^bip-0065]: [BIP 65: OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)\n+\n+[^zebra-script]: [Zebra: zebra-script/src/lib.rs, script verification flags P2SH and CHECKLOCKTIMEVERIFY](https://github.com/ZcashFoundation/zebra/blob/main/zebra-script/src/lib.rs)\n+\n+[^zebra-locktime]: [Zebra: zebra-chain/src/transaction.rs, `Transaction::lock_time`](https://github.com/ZcashFoundation/zebra/blob/main/zebra-chain/src/transaction.rs)\n+\n+[^forum-nu7-vote]: [Zcash Community Forum: NU7 Coinholder Vote](https://forum.zcashcommunity.com/t/nu7-coinholder-vote/56912)\n+\n+[^zcashlabs-voting]: [Zcash Labs: NU7 coinholder vote](https://zcashlabs.org/voting)\n+\n+[^coindesk-nu7]: [CoinDesk: Zcash holders overwhelmingly back faster transactions and bitcoin-style halvings, 2026-09-16](https://www.coindesk.com/tech/2026/09/16/zcash-holders-overwhelmingly-back-faster-transactions-and-bitcoin-style-halvings)\n+\n+[^zfnd-nu7-results]: [Zcash Foundation: NU7 Polling Results: What We Heard and Where We Go From Here](https://zfnd.org/nu7-polling-results-what-we-heard-and-where-we-go-from-here/)\n+\n+[^curve-vecrv]: [Curve DAO: Vote-Escrowed CRV (veCRV)](https://resources.curve.finance/vecrv/overview)\n+\n+[^polkadot-opengov]: [Polkadot Wiki: Polkadot OpenGov — Voluntary Locking (Conviction Voting)](https://wiki.polkadot.com/learn/learn-polkadot-opengov/)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-arya-lock-weighted-voting.md","title":"Lock-weighted Coinholder Polling","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":2,"id":"scope-decision","text":"Scope decision"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"parameters","text":"Parameters"},{"level":2,"id":"weight-function","text":"Weight function"},{"level":2,"id":"lock-records","text":"Lock records"},{"level":3,"id":"class-h","text":"Class H"},{"level":3,"id":"class-t","text":"Class T"},{"level":3,"id":"class-s","text":"Class S"},{"level":2,"id":"counting","text":"Counting"},{"level":2,"id":"excluded-coins","text":"Excluded coins"},{"level":2,"id":"poll-operator-obligations","text":"Poll operator obligations"},{"level":1,"id":"security-considerations","text":"Security Considerations"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"alternatives-considered","text":"Alternatives considered"},{"level":2,"id":"rationale-for-the-cap-and-floor","text":"Rationale for the cap and floor"},{"level":2,"id":"rationale-for-measuring-in-blocks","text":"Rationale for measuring in blocks"},{"level":1,"id":"open-questions","text":"Open questions"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1430,"title":"Add draft ZIP: 10-second Block Target Spacing","author":"upbqdn","createdAt":"2026-10-31T13:10:11Z","updatedAt":"2026-10-31T13:11:40Z","url":"https://github.com/zcash/zips/pull/1430","diffUrl":"https://github.com/zcash/zips/pull/1430.diff","headRef":"10s-block-target-spacing","headRepo":"upbqdn/zips","headSha":"4a3bfed7d3b3f518a4e1dce608a7dc6d87b9d36f","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"[Rendered.](https://github.com/upbqdn/zips/blob/10s-block-target-spacing/zips/draft-marek-10s-block-target-spacing.md) Proposes reducing the block target spacing from 25 seconds (ZIP 218, NU7) to 10 seconds in NU8. The change is limited to latency. The block subsidy, halving interval, funding-stream","zipRefs":[218],"draftRefs":["draft-marek-10s-block-target-spacing"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -233,6 +233,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-jvff-p2p-quic-transport</td> <td class=\"left\"><a href=\"zips/draft-arya-jvff-p2p-quic-transport.md\">Version 2 Zcash P2P Network Protocol</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/352\">zips#352</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-marek-10s-block-target-spacing</td> <td class=\"left\"><a href=\"zips/draft-marek-10s-block-target-spacing.md\">10-second Block Target Spacing</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>"},{"filename":"zips/draft-marek-10s-block-target-spacing.md","status":"added","additions":279,"deletions":0,"patch":"@@ -0,0 +1,279 @@\n+    ZIP: Unassigned\n+    Title: 10-second Block Target Spacing\n+    Owners: Marek <marek@zfnd.org>\n+    Status: Draft\n+    Category: Consensus\n+    Created: 2026-10-31\n+    License: MIT\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"SHALL\", \"SHOULD\", and \"SHOULD NOT\" in this document are\n+to be interpreted as described in BCP 14 [^BCP14] when, and only when, they\n+appear in all capitals.\n+\n+The term \"network upgrade\" is to be interpreted as defined in ZIP 200.\n+[^zip-0200]\n+\n+The term \"block target spacing\" is to be interpreted as defined in ZIP 218.\n+[^zip-0218]\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in\n+§ 3.12 'Mainnet and Testnet'. [^protocol-networks]\n+\n+\"NU8\" is the code name of the network upgrade that deploys this proposal.\n+\n+\n+# Abstract\n+\n+This proposal changes the block target spacing from 25 seconds to 10 seconds\n+in NU8. The block subsidy, the block size limit, and the shielded action limits\n+of each block are scaled by the same factor, so that issuance, block space, and\n+shielded capacity per unit of time are unchanged.\n+\n+\n+# Motivation\n+\n+ZIP 218 reduces the block target spacing from 75 seconds to 25 seconds in NU7\n+in order to reduce transaction latency. [^zip-0218] This proposal reduces it to\n+10 seconds, which reduces the expected time to a first confirmation from 25\n+seconds to 10 seconds.\n+\n+The proposal changes latency only. Each quantity that NU7 specifies per unit of\n+time is preserved: issuance, funding stream address periods, reissuance by the\n+Network Sustainability Mechanism, the duration of the difficulty averaging\n+window, and the resource limits of the block chain.\n+\n+\n+# Specification\n+\n+The changes below are made to the Zcash Protocol Specification [^protocol] and\n+to ZIPs 207, 214, and 237 [^zip-0207] [^zip-0214] [^zip-0237], as amended for\n+NU7 by ZIP 218 [^zip-0218] and ZIP 259 [^zip-0259].\n+\n+## Block target spacing\n+\n+In § 5.3 'Constants' [^protocol-constants], define:\n+\n+$$\\mathsf{NU8ActivationHeight} : \\mathbb{N}$$\n+$$\\mathsf{PostNU8PoWTargetSpacing} : \\mathbb{N} := 10 \\textsf{ seconds}$$\n+\n+In § 7.7.3 'Difficulty adjustment' [^protocol-diffadjustment], define:\n+\n+$$\\mathsf{NU8PoWTargetSpacingRatio} : \\mathbb{Q} := \\mathsf{PostNU7PoWTargetSpacing} \\;/\\; \\mathsf{PostNU8PoWTargetSpacing} = 5/2$$\n+\n+$$\\mathsf{IsNU8Activated} : \\mathbb{N} \\to \\{\\mathsf{false}, \\mathsf{true}\\}$$\n+$$\\mathsf{IsNU8Activated}(\\mathsf{height}) := (\\mathsf{height} \\geq \\mathsf{NU8ActivationHeight})$$\n+\n+On each network, $\\mathsf{NU8ActivationHeight}$ SHALL be even.\n+\n+In the definition of $\\mathsf{PoWTargetSpacing}$ in ZIP 218, replace the case\n+\"otherwise\" with \"if $\\mathsf{IsNU7Activated}(\\mathsf{height})$ and not\n+$\\mathsf{IsNU8Activated}(\\mathsf{height})$\", and add the case\n+$\\mathsf{PostNU8PoWTargetSpacing}$ \"otherwise\".\n+\n+## Difficulty averaging window\n+\n+In § 5.3 'Constants', define:\n+\n+$$\\mathsf{PostNU8PoWAveragingWindow} : \\mathbb{N} := 255$$\n+\n+In the definition of $\\mathsf{PoWAveragingWindow}$ in ZIP 218, replace the case\n+\"otherwise\" with \"if $\\mathsf{IsNU7Activated}(\\mathsf{height})$ and not\n+$\\mathsf{IsNU8Activated}(\\mathsf{height})$\", and add the case\n+$\\mathsf{PostNU8PoWAveragingWindow}$ \"otherwise\".\n+\n+The averaging window timespan therefore remains 2,550 seconds.\n+\n+## Halving interval and block subsidy\n+\n+Define:\n+\n+$$\\mathsf{PostNU8HalvingInterval} : \\mathbb{N} := \\mathsf{PostNU7HalvingInterval} \\cdot \\mathsf{NU8PoWTargetSpacingRatio} = 12{,}600{,}000$$\n+\n+In the definition of $\\mathsf{Halving}$ in ZIP 218, replace the case\n+\"otherwise\" with \"if $\\mathsf{IsNU7Activated}(\\mathsf{height})$ and not\n+$\\mathsf{IsNU8Activated}(\\mathsf{height})$\", and add the case:\n+\n+$$\n+\\left\\lfloor \\dfrac{\\mathsf{BlossomActivationHeight} - \\mathsf{SlowStartShift}}{\\mathsf{PreBlossomHalvingInterval}}\n+  + \\dfrac{\\mathsf{NU7ActivationHeight} - \\mathsf{BlossomActivationHeight}}{\\mathsf{PostBlossomHalvingInterval}}\n+  + \\dfrac{\\mathsf{NU8ActivationHeight} - \\mathsf{NU7ActivationHeight}}{\\mathsf{PostNU7HalvingInterval}}\n+  + \\dfrac{\\mathsf{height} - \\mathsf{NU8ActivationHeight}}{\\mathsf{PostNU8HalvingInterval}} \\right\\rfloor,\n+  \\quad \\textsf{otherwise}\n+$$\n+\n+In the definition of $\\mathsf{ScheduledBlockSubsidy}$ in ZIP 237, replace the\n+condition \"if $\\mathsf{IsNU7Activated}(\\mathsf{height})$\" of the case added by\n+ZIP 218 with \"if $\\mathsf{IsNU7Activated}(\\mathsf{height})$ and not\n+$\\mathsf{IsNU8Activated}(\\mathsf{height})$\", and add the case:\n+\n+$$\n+\\mathsf{floor}\\left(\\dfrac{\\mathsf{MaxBlockSubsidy}}{\\mathsf{BlossomPoWTargetSpacingRatio} \\cdot \\mathsf{NU7PoWTargetSpacingRatio} \\cdot \\mathsf{NU8PoWTargetSpacingRatio} \\cdot 2^{\\mathsf{Halving}(\\mathsf{height})}}\\right),\n+  \\quad \\textsf{if } \\mathsf{IsNU8Activated}(\\mathsf{height})\n+$$\n+\n+The denominator is $15 \\cdot 2^{\\mathsf{Halving}(\\mathsf{height})}$.\n+\n+## Block size and shielded action limits\n+\n+For a block at a height for which $\\mathsf{IsNU8Activated}(\\mathsf{height})$,\n+the size of the block MUST be less than or equal to 800,000 bytes, in place of\n+the limit of 2,000,000 bytes in § 7.6 'Block Header Encoding and Consensus'.\n+[^protocol-blockheader]\n+\n+For such a block, the shielded action limits of ZIP 218 apply with\n+$\\mathsf{GlobalShieldedBudget} = 132$,\n+$\\mathsf{OrchardProtocolBlockActionLimit} = 132$, and\n+$\\mathsf{SaplingBlockIOLimit} = 120$. $\\mathsf{SproutBlockJoinSplitLimit}$\n+remains 0.\n+\n+## Funding streams\n+\n+Let $A$, $R$, $C$, $I$, and $H_1$ be as in the definition of\n+$\\mathsf{AddressPeriod}$ in Revision 2 of ZIP 207, and let\n+$B = \\mathsf{NU8ActivationHeight}$. In that definition, replace the case\n+\"otherwise\" with \"if $\\mathsf{IsNU7Activated}(\\mathsf{height})$ and not\n+$\\mathsf{IsNU8Activated}(\\mathsf{height})$\", and add the case:\n+\n+$$\n+\\mathsf{floor}\\left(\\dfrac{5\\left(R(A + I - H_1) + B - A\\right) + 2(\\mathsf{height} - B)}{5RC}\\right),\n+  \\quad \\textsf{otherwise}\n+$$\n+\n+This preserves the address index at NU8 activation. Each later address period\n+lasts $5RC/2 = 262{,}500$ blocks.\n+\n+For each funding stream that is active at $B$ and whose end height is\n+$\\mathsf{HeightForHalving}(3)$ by Revision 3 of ZIP 214, the end height SHALL\n+be $\\mathsf{HeightForHalving}(3)$ calculated using the $\\mathsf{Halving}$\n+function of this proposal. If $H_3$ is the third-halving height under ZIP 218\n+(6,132,390 on Mainnet and 4,497,948 on Testnet), this end height is\n+$B + 5(H_3 - B)/2$. The number of addresses that ZIP 207 requires\n+for each such stream is unchanged.\n+\n+## Network Sustainability Mechanism\n+\n+In ZIP 237, $\\mathsf{HalvingInterval}(\\mathsf{height})$ is\n+$\\mathsf{PostNU8HalvingInterval}$ if $\\mathsf{IsNU8Activated}(\\mathsf{height})$.\n+From NU8 activation, $\\mathsf{BLOCK\\_SUBSIDY\\_FRACTION}$ is therefore\n+$550 / 10^{10}$.\n+\n+If $B$ is less than the $\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ that ZIP 237\n+specifies for a network, then $\\mathsf{DEPLOYMENT\\_BLOCK\\_HEIGHT}$ for that\n+network SHALL instead be calculated as in ZIP 237, with $B$ in place of $A$,\n+with $\\mathsf{ScheduledBlockSubsidy}$, $H_3$, and $H_4$ as specified by this\n+proposal, and with 550 in place of 1375. NU8 activation heights SHALL be chosen\n+so that this height exists before $H_4$. The NU8 deployment ZIP specifies the\n+resulting height for each network.\n+\n+## Minimum-difficulty blocks on Testnet\n+\n+On Testnet, from NU8 activation, the minimum-difficulty threshold specified by\n+ZIP 218 is $45 \\cdot \\mathsf{PoWTargetSpacing}(\\mathsf{height})$ seconds, which\n+remains 450 seconds.\n+\n+## Unchanged consensus rules\n+\n+Consensus rules that are expressed in blocks and not changed above, including\n+the coinbase maturity of 100 blocks and $\\mathsf{PoWMedianBlockSpan}$, are\n+unchanged.\n+\n+## Non-consensus changes\n+\n+From NU8 activation:\n+\n+- Wallets SHOULD use a default expiry delta of 300 blocks, in place of the 120\n+  blocks recommended by ZIP 218.\n+- Nodes SHOULD support rolling back at least 1,500 blocks, in place of the 600\n+  blocks recommended by ZIP 218.\n+- Implementations SHOULD multiply the block-count constants that ZIP 218\n+  multiplies by 3 by $\\mathsf{NU8PoWTargetSpacingRatio}$, rounding up.\n+- Nodes SHOULD NOT admit to their mempools or relay, and wallets SHOULD NOT\n+  create, a transaction that alone exceeds the block size or shielded action\n+  limits above.\n+\n+\n+# Rationale\n+\n+A target spacing of 10 seconds makes each scaled quantity an integer: the\n+averaging window of 255 blocks, the halving interval of 12,600,000 blocks, the\n+address period of 262,500 blocks, and the limits of 800,000 bytes, 132\n+actions, and 120 Sapling inputs and outputs.\n+\n+Every post-NU7 halving height is even on both networks. An even activation\n+height $B$ therefore maps each such height $H$ to the integer height\n+$B + 5(H - B)/2$, so that no halving boundary falls within a 25-second block.\n+\n+Scaling the per-block limits by 2/5 keeps the worst-case validation work,\n+compact sync bandwidth, and chain growth per unit of time at their NU7 values.\n+ZIP 218 bounds worst-case compact sync bandwidth at 169.10 MB per day.\n+[^zip-0218] Without the scaling, each of these would increase by a factor of\n+5/2.\n+\n+The post-NU8 block subsidy differs from 2/5 of the post-NU7 block subsidy by\n+less than one zatoshi, due to rounding. If NU8 activates before the third\n+halving, this reduces total scheduled issuance, relative to ZIP 218, by\n+$(H_3 - B)/2$ zatoshi before the third halving and by 1.134 ZEC after it.\n+\n+\n+# Security and Privacy Considerations\n+\n+The stale block rate increases with the ratio of block propagation time to the\n+block target spacing. ZIP 218 reports a stale block rate of 4.86% for 2 MB\n+blocks at 25-second spacing on a 99-node devnet. [^zip-0218] The rate at\n+10-second spacing has not been measured; it needs to be measured before\n+deployment. Smaller blocks and compact block relay [^draft-compact-block-relay]\n+reduce propagation time.\n+\n+A given number of confirmations corresponds to 2.5 times less time. Where\n+propagation time is significant relative to the target spacing, it also\n+provides less security against an attacker with a given fraction of the hash\n+power.\n+\n+Wallets that select an anchor three blocks behind the chain tip, as\n+recommended by ZIP 315 [^zip-0315], select one about 30 seconds old. A rollback\n+past the anchor invalidates the transaction, and links its revealed nullifiers\n+to any later transaction that spends the same notes. More frequent blocks also\n+give observers finer timing information.\n+\n+\n+# Deployment\n+\n+This proposal is intended to be deployed in NU8. The NU8 deployment ZIP will\n+specify $\\mathsf{NU8ActivationHeight}$ for each network.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2026.8.0 or later](protocol/protocol.pdf)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2026.8.0. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^protocol-constants]: [Zcash Protocol Specification, Version 2026.8.0. Section 5.3: Constants](protocol/protocol.pdf#constants)\n+\n+[^protocol-blockheader]: [Zcash Protocol Specification, Version 2026.8.0. Section 7.6: Block Header Encoding and Consensus](protocol/protocol.pdf#blockheader)\n+\n+[^protocol-diffadjustment]: [Zcash Protocol Specification, Version 2026.8.0. Section 7.7.3: Difficulty adjustment](protocol/protocol.pdf#diffadjustment)\n+\n+[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)\n+\n+[^zip-0207]: [ZIP 207: Funding Streams](zip-0207.rst)\n+\n+[^zip-0214]: [ZIP 214: Consensus rules for a Zcash Development Fund](zip-0214.rst)\n+\n+[^zip-0218]: [ZIP 218: 25-second Block Target Spacing](zip-0218.md)\n+\n+[^zip-0237]: [ZIP 237: Network Sustainability Mechanism: Halving-Preserving Issuance](zip-0237.md)\n+\n+[^zip-0259]: [ZIP 259: Deployment of the NU7 Network Upgrade](zip-0259.md)\n+\n+[^zip-0315]: [ZIP 315: Best Practices for Wallet Implementations](zip-0315.rst)\n+\n+[^draft-compact-block-relay]: [Draft ZIP: Compact Block Relay](https://github.com/zcash/zips/pull/1429)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-marek-10s-block-target-spacing.md","title":"10-second Block Target Spacing","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"block-target-spacing","text":"Block target spacing"},{"level":2,"id":"difficulty-averaging-window","text":"Difficulty averaging window"},{"level":2,"id":"halving-interval-and-block-subsidy","text":"Halving interval and block subsidy"},{"level":2,"id":"block-size-and-shielded-action-limits","text":"Block size and shielded action limits"},{"level":2,"id":"funding-streams","text":"Funding streams"},{"level":2,"id":"network-sustainability-mechanism","text":"Network Sustainability Mechanism"},{"level":2,"id":"minimum-difficulty-blocks-on-testnet","text":"Minimum-difficulty blocks on Testnet"},{"level":2,"id":"unchanged-consensus-rules","text":"Unchanged consensus rules"},{"level":2,"id":"non-consensus-changes","text":"Non-consensus changes"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1379,"title":"Draft ZIP: Dandelion++ Transaction Propagation for Zcash P2P Nodes","author":"y4ssi","createdAt":"2026-09-29T21:10:21Z","updatedAt":"2026-10-31T04:13:14Z","url":"https://github.com/zcash/zips/pull/1379","diffUrl":"https://github.com/zcash/zips/pull/1379.diff","headRef":"draft-zodl-dandelion","headRepo":"y4ssi/zips","headSha":"04f59e5f65c04c737fff284b0b31eabe7fdc186b","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"## Summary Proposes a network-layer specification for Dandelion++ stem/fluff transaction propagation in Zcash full nodes. **Problem**: Zcash transactions are currently flood-broadcast immediately, allowing passive network observers to correlate originating IP addresses with transactions even for shi","zipRefs":[],"draftRefs":["draft-zodl-dandelion"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-zodl-dandelion.md","status":"added","additions":260,"deletions":0,"patch":"@@ -0,0 +1,260 @@\n+    ZIP: Unassigned\n+    Title: Dandelion++ Transaction Propagation for Zcash P2P Nodes\n+    Owners: y4ssi <y4ssi@zodl.com>\n+    Status: Draft\n+    Category: Network\n+    Created: 2026-07-09\n+    License: MIT\n+    Discussions-To: <https://github.com/zcash/zips/issues/TODO>\n+    Pull-Request: <https://github.com/zcash/zips/pull/1379>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"MAY\", and \"RECOMMENDED\"\n+in this document are to be interpreted as described in BCP 14 [^BCP14] when, and only\n+when, they appear in all capitals.\n+\n+**Stem phase:** The unicast relay phase of the Dandelion++ protocol [^DPPLUSPLUS] in\n+which a transaction is forwarded to a single randomly-selected peer per epoch before\n+entering the fluff phase.\n+\n+**Fluff phase:** The standard flood-broadcast phase of transaction propagation that\n+follows the stem phase.\n+\n+**Stem peer:** The single P2P peer to which a Dandelion++ node relays stem-phase\n+transactions during a given epoch.\n+\n+**IP-to-transaction correlation:** The ability of a network observer to determine\n+which IP address originated a given transaction by observing when and where the\n+transaction first appeared on the P2P network.\n+\n+\n+# Abstract\n+\n+This ZIP specifies how Zcash full nodes SHOULD implement Dandelion++ [^DPPLUSPLUS]\n+transaction propagation. In Dandelion++, a newly-received transaction first travels\n+through a randomly-selected \"stem\" of peers (unicast per epoch) before entering a\n+standard flood broadcast (\"fluff\" phase). This makes it significantly harder for\n+network observers to determine which node originated a transaction, reducing\n+IP-to-transaction correlation.\n+\n+This ZIP covers only the **full-node P2P relay behavior** (sometimes called\n+\"Component B\"). A companion mechanism for wallets to submit transactions directly\n+to a node via P2P rather than through a compact-block server is described in\n+draft-zodl-dandelion-wallet (PR [#1380](https://github.com/zcash/zips/pull/1380)). [^draft-zodl-dandelion-wallet]\n+\n+\n+# Motivation\n+\n+Currently, Zcash transactions are immediately flood-broadcast to all connected peers\n+the moment they enter a node's mempool. A passive network adversary can observe the\n+timing of transaction appearances at many vantage points to identify the originating\n+node with high confidence, even for shielded transactions whose content is cryptographically\n+protected.\n+\n+Dandelion++ addresses this by introducing an intermediate \"stem\" phase during which\n+the transaction is relayed to only one peer. The originating node is hidden in the\n+anonymity set of all nodes along the stem path.\n+\n+\n+# Requirements\n+\n+1. Each Dandelion++ node MUST maintain an **epoch** with a randomly-chosen duration\n+   in the range [600, 720] seconds (10 minutes ± 120 second jitter).\n+\n+2. At the start of each epoch, the node MUST select a single **stem peer** uniformly\n+   at random from its currently-connected outbound peers. If no outbound peers are\n+   available, the node operates in fluff mode for that epoch.\n+\n+3. For each transaction received locally (via RPC, direct P2P submission, or\n+   relayed from a peer), the node MUST:\n+\n+   a. Enter the transaction into **stem phase**: route it exclusively to the stem peer\n+      using a unicast P2P message.\n+   b. Record the stem-phase start time. If the transaction has not entered fluff\n+      within **30 seconds**, the node MUST promote it to fluff phase.\n+   c. If the stem peer disconnects or becomes unavailable before the transaction\n+      is acknowledged, the node MUST immediately promote the transaction to fluff\n+      phase rather than silently dropping it.\n+\n+4. During stem phase, the node MUST NOT advertise the transaction to any other peer.\n+   Specifically:\n+\n+   a. The transaction MUST NOT be included in responses to `mempool` P2P requests\n+      from remote peers.\n+   b. The transaction MUST NOT be returned by any RPC that exposes mempool contents\n+      (e.g. `getrawmempool`, `getrawtransaction` with `verbose=0` on unconfirmed\n+      transactions) until it enters fluff phase.\n+\n+5. Once the transaction enters fluff phase, it MUST be broadcast normally using\n+   the existing `inv`/`tx` flood mechanism.\n+\n+\n+# Specification\n+\n+## Epoch management\n+\n+A new **epoch** begins immediately when the node starts and whenever the previous\n+epoch expires. The epoch duration is:\n+\n+```\n+epoch_duration = BASE_EPOCH_DURATION + uniform_random(0, MAX_EPOCH_JITTER)\n+```\n+\n+where `BASE_EPOCH_DURATION` = 600 s and `MAX_EPOCH_JITTER` = 120 s.\n+\n+The stem peer for an epoch is selected uniformly at random from the addresses of\n+currently-connected outbound peers at the moment the epoch begins. If no outbound\n+peers are connected, the stem peer is `None` and the node operates in **fluff mode**\n+for the entire epoch (all transactions are immediately flood-broadcast).\n+\n+## Stem-phase forwarding\n+\n+When a node enters a new transaction into stem phase, it SHOULD send the transaction\n+identifier to the stem peer using the standard `inv` P2P message with type\n+`MSG_TX`. The stem peer is expected to request the full transaction via `getdata`\n+and continue the stem relay using its own stem peer for the current epoch.\n+\n+> **Note:** The Dandelion++ paper defines a separate \"unadvertised `tx`\" convention\n+> (sending the raw `tx` message to the stem peer without a prior `inv`) as a\n+> signal that the transaction should be treated as stem-phase by the recipient.\n+> This convention is OPTIONAL under this ZIP. Nodes that do not implement it\n+> MUST fall back gracefully to standard `inv` forwarding, which provides the\n+> same privacy property at the forwarding node. A future ZIP MAY standardize\n+> the unadvertised-`tx` convention once implementations converge.\n+\n+## Stem-phase mempool filtering\n+\n+Implementations MUST ensure that stem-phase transactions are not observable by\n+remote peers before entering fluff. This applies to:\n+\n+1. Responses to `mempool` P2P requests — the node MUST filter stem-phase\n+   transaction IDs from the list returned to any remote peer.\n+\n+2. Transaction-related RPCs — the node SHOULD suppress stem-phase transactions\n+   from any RPC that returns mempool contents. Implementations MAY defer this\n+   to a subsequent release.\n+\n+## Fail-closed routing\n+\n+If the stem peer is not reachable at the time a stem-phase transaction should be\n+forwarded (e.g. the peer has been evicted from the ready set), the node MUST\n+promote the transaction to fluff immediately. The node MUST NOT silently discard\n+the transaction, and MUST NOT fall back to routing to a different peer without\n+updating the propagation state (which would result in the transaction being\n+advertised to a non-stem peer during what the node still records as stem phase).\n+\n+## Fluff phase\n+\n+After the 30-second stem timeout, or when a stem-peer failure triggers early\n+promotion, the transaction enters fluff phase and MUST be broadcast to all\n+connected peers using the normal `inv` + `getdata` + `tx` mechanism.\n+\n+\n+# Rationale\n+\n+## Why per-epoch rather than per-transaction stem peer selection?\n+\n+The Dandelion++ paper recommends per-transaction selection for maximum anonymity.\n+However, per-epoch selection has an important advantage: an adversary who controls\n+multiple peers cannot learn which peer consistently forwards a given sender's\n+transactions by comparing which peer they saw first for each transaction. With\n+per-epoch selection, an adversary learns only one stem identity per 10-minute window.\n+The privacy trade-off is well-documented in [^DPPLUSPLUS].\n+\n+## Why 30 seconds for the stem timeout?\n+\n+The Dandelion++ paper recommends a stem timeout in the range 30–60 seconds. The\n+lower bound is used here to minimize transaction propagation delay under normal\n+conditions. If a transaction is rejected by the stem peer (e.g. due to policy),\n+the 30-second timeout ensures it is not silently dropped.\n+\n+## Why not a new P2P message type?\n+\n+Defining a new P2P message type would require a network upgrade and coordination\n+across all Zcash full node implementations. Using the existing `inv` message for\n+stem forwarding achieves the same routing behavior with no protocol-level changes,\n+allowing incremental deployment. Nodes that do not implement this ZIP process the\n+`inv` normally and flood-broadcast the transaction, which is the safe fallback.\n+\n+\n+# Reference Implementation\n+\n+## Component B — Full-node Dandelion++ relay (Zebra)\n+\n+Reference implementation proposed upstream:\n+[zcashfoundation/zebra#10928](https://github.com/zcashfoundation/zebra/pull/10928).\n+\n+Verified end-to-end on a 3-node regtest network (2026-07-09). See\n+`docs/dandelion-e2e-regtest.md` in that PR for the complete test procedure,\n+build flags, and result evidence (txid accepted, 30 s stem-retention confirmed,\n+adversary observer saw no `inv` during the stem window).\n+\n+Implemented:\n+\n+- Epoch management with 10 min base + up to 2 min jitter\n+  (`zebra-network/src/dandelion/epoch.rs`)\n+- Per-transaction propagation state, 30 s stem timeout, `stem_txids()`\n+  (`zebra-network/src/dandelion/state.rs`)\n+- Fail-closed stem unicast via `PeerSet::route_to_peer` with `PeerError::NoReadyPeers`\n+  (`zebra-network/src/peer_set/set.rs`)\n+- Gossip task wired into `zebrad` startup; `MempoolChangeKind::StemAdded` routes\n+  locally-submitted txs through stem; peer-relayed `Added` goes directly to fluff\n+  (`zebrad/src/components/mempool/dandelion_gossip.rs`, `mempool.rs`)\n+- Phase 4 — stem-phase hiding from all exposure surfaces:\n+  - `MempoolTransactionIds` P2P response (inbound handler)\n+  - `TransactionsById` P2P getdata handler (adversary cannot fetch full tx during stem)\n+  - `getrawmempool` RPC (verbose and non-verbose)\n+  - `getrawtransaction` RPC\n+  - Zebra indexer gRPC `mempool_change` stream (suppresses `StemAdded` events;\n+    Zaino/lightwalletd only sees the tx once it enters fluff)\n+- 10 unit tests (epoch, propagation state, Phase 4 filter) + 2 routing tests\n+\n+Remaining (optional / future coordination): unadvertised-`tx` wire convention\n+(requires other implementations to recognize the signal).\n+\n+## Component A — Wallet direct P2P submission\n+\n+Proposed upstream to the Zcash wallet SDKs:\n+\n+- **Android**: [zcash/zcash-android-wallet-sdk#2010](https://github.com/zcash/zcash-android-wallet-sdk/pull/2010)\n+- **iOS (Swift)**: [zcash/zcash-swift-wallet-sdk#1804](https://github.com/zcash/zcash-swift-wallet-sdk/pull/1804)\n+\n+Both SDKs add a `DandelionSubmitConfig` opt-in (`DirectP2P` / `directP2P`) that:\n+1. Resolves peers via the Zcash mainnet DNS seeders (same set as Zebra defaults).\n+2. Opens a TCP connection directly to a full node.\n+3. Performs the Zcash P2P version/verack handshake.\n+4. Sends the raw `tx` message **without a prior `inv`** (the unadvertised-tx\n+   convention described in §Stem-phase forwarding above).\n+5. Disconnects.\n+\n+Chain synchronisation always uses the lightwalletd endpoint regardless of this setting.\n+A fallback to lwd submission is available when all P2P peers are unreachable.\n+\n+The wallet-side specification (peer discovery, tx message format, error handling) is\n+in the companion draft (PR [#1380](https://github.com/zcash/zips/pull/1380)). [^draft-zodl-dandelion-wallet]\n+\n+\n+# Changelog\n+\n+- 2026-07-09: Initial draft. Component B reference implementation complete (Zebra).\n+- 2026-07-09: Component A reference implementations added (Android + iOS SDKs).\n+  Updated Reference Implementation section to list all PRs.\n+- 2026-07-09: Reference implementations proposed upstream — Zebra Component B to\n+  `zcashfoundation/zebra`, and Component A to `zcash/zcash-android-wallet-sdk#2010`\n+  and `zcash/zcash-swift-wallet-sdk#1804`.\n+\n+\n+# References\n+\n+[^BCP14]: [Key words for use in RFCs to Indicate Requirement Levels](https://www.rfc-editor.org/info/bcp14)\n+\n+[^DPPLUSPLUS]: Venkatakrishnan, S.B., Fanti, G., Viswanath, P. \"Dandelion++: Lightweight\n+Cryptocurrency Networking with Formal Anonymity Guarantees.\" ACM SIGMETRICS 2018.\n+https://arxiv.org/abs/1805.11060\n+\n+[^zip-0307]: [ZIP 307: Light Client Protocol for Payment Detection](https://zips.z.cash/zip-0307)\n+\n+[^draft-zodl-dandelion-wallet]: draft-zodl-dandelion-wallet: Direct Wallet Transaction Submission via Dandelion++ P2P Relay. PR: https://github.com/zcash/zips/pull/1380\n+(In preparation — see zcash/zips#1380.)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-zodl-dandelion.md","title":"Dandelion++ Transaction Propagation for Zcash P2P Nodes","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"epoch-management","text":"Epoch management"},{"level":2,"id":"stem-phase-forwarding","text":"Stem-phase forwarding"},{"level":2,"id":"stem-phase-mempool-filtering","text":"Stem-phase mempool filtering"},{"level":2,"id":"fail-closed-routing","text":"Fail-closed routing"},{"level":2,"id":"fluff-phase","text":"Fluff phase"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"why-per-epoch-rather-than-per-transaction-stem-peer-selection","text":"Why per-epoch rather than per-transaction stem peer selection?"},{"level":2,"id":"why-30-seconds-for-the-stem-timeout","text":"Why 30 seconds for the stem timeout?"},{"level":2,"id":"why-not-a-new-p2p-message-type","text":"Why not a new P2P message type?"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":2,"id":"component-b--full-node-dandelion-relay-zebra","text":"Component B — Full-node Dandelion++ relay (Zebra)"},{"level":2,"id":"component-a--wallet-direct-p2p-submission","text":"Component A — Wallet direct P2P submission"},{"level":1,"id":"changelog","text":"Changelog"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1415,"title":"Add draft ZIP: Disabling Sapling Pool Deposits","author":"ValarDragon","createdAt":"2026-10-27T16:16:59Z","updatedAt":"2026-10-28T17:07:19Z","url":"https://github.com/zcash/zips/pull/1415","diffUrl":"https://github.com/zcash/zips/pull/1415.diff","headRef":"claude/blissful-archimedes-03oh54","headRepo":"valargroup/zips","headSha":"0a0b9179a2683202c610ef41e4c5de0abb04a794","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Proposes making the Sapling pool withdraw-only from NU8. Every transaction's valueBalanceSapling must be nonnegative and coinbase transactions may no longer contain Sapling outputs. This continues the process of encouraging the migration of remaining Sapling funds into quantum-recoverable pools, mir","zipRefs":[211],"draftRefs":["draft-valargroup-sapling-disable-deposits"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -235,6 +235,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-mcgee-keyholders-organizations</td> <td class=\"left\"><a href=\"zips/draft-mcgee-keyholders-organizations.md\">Update to ZIP 1016 & ZIP 271: Key-Holder Organizations</a></td> <td class=\"left\"><a href=\"URL TBD\">URL TBD</a></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n+    <tr> <td class=\"left\">draft-valargroup-sapling-disable-deposits</td> <td class=\"left\"><a href=\"zips/draft-valargroup-sapling-disable-deposits.md\">Disabling Sapling Pool Deposits</a></td> <td class=\"left\"><a href=\"https://forum.zcashcommunity.com/t/sapling-withdraw-only-discussion-kickoff/55223\">https://forum.zcashcommunity.com/t/sapling-withdraw-only-discussion-kickoff/55223</a></td>\n   </table></embed>\n \n Withdrawn, Rejected, or Obsolete ZIPs"},{"filename":"zips/draft-valargroup-sapling-disable-deposits.md","status":"added","additions":341,"deletions":0,"patch":"@@ -0,0 +1,341 @@\n+    ZIP: Unassigned\n+    Title: Disabling Sapling Pool Deposits\n+    Owners: Dev Ojha <dev@valargroup.dev>\n+    Status: Draft\n+    Category: Consensus / Wallet\n+    Created: 2026-10-27\n+    License: MIT\n+    Discussions-To: <https://forum.zcashcommunity.com/t/sapling-withdraw-only-discussion-kickoff/55223>\n+    Pull-Request: <https://github.com/zcash/zips/pull/???>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and only\n+when, they appear in all capitals.\n+\n+The term \"network upgrade\" in this document is to be interpreted as described\n+in ZIP 200. [^zip-0200]\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in\n+§ 3.12 ‘Mainnet and Testnet’. [^protocol-networks]\n+\n+The term \"Sapling shielded protocol\" refers to the shielded payment protocol\n+introduced in the Sapling network upgrade. [^zip-0205] Following the\n+convention of ZIP 229 [^zip-0229], *Sapling pool*, *Orchard pool* and\n+*Ironwood pool* refer to chain value pools, as distinct from the protocols that\n+operate on them.\n+\n+The term \"Sapling chain value pool balance\" is to be interpreted as described\n+in ZIP 209 [^zip-0209] and § 4.17 ‘Chain Value Pool Balances’.\n+[^protocol-chainvaluepoolbalances]\n+\n+The term \"quantum recoverable\" is to be interpreted as described in\n+ZIP 2005. [^zip-2005]\n+\n+NU8 refers to the network upgrade following NU7.\n+\n+\n+# Abstract\n+\n+This proposal disables the ability to add new value to the Sapling chain value\n+pool balance. From NU8 activation, every transaction's Sapling value balance\n+must be nonnegative: value may leave the *Sapling pool*, or move between\n+Sapling notes, but no new value may enter it. Coinbase transactions may no\n+longer contain Sapling outputs.\n+\n+This starts the migration of the remaining *Sapling pool* funds into\n+quantum-recoverable shielded pools (the *Ironwood pool* today). It is the\n+Sapling counterpart of what ZIP 211 [^zip-0211] did for Sprout at Canopy and\n+what ZIP 2006 [^zip-2006] did for the *Orchard pool* at NU6.3.\n+\n+\n+# Motivation\n+\n+The Sapling shielded protocol is not quantum recoverable: an adversary able to\n+compute discrete logarithms, for example with a quantum computer, could steal\n+funds held in the *Sapling pool*. ZIP 2005 expects Sapling funds to be\n+migrated to the *Ironwood pool* instead [^zip-2005]. Ironwood notes are quantum\n+recoverable, and formal verification shows that undetectable counterfeiting\n+bugs do not exist in Ironwood. [^Ironwood-book] [^ironwood-verification]\n+Ironwood and later Tachyon [^tachyon] are steps toward a fully post-quantum\n+shielded pool soon after.\n+\n+Nothing currently stops new value from entering the *Sapling pool*, and every\n+deposit adds to what must later be migrated. Pool migrations take years: about\n+22,000 ZEC remains in Sprout six years after ZIP 211 disabled additions to it\n+[^zip-0211]. The *Sapling pool* holds about 419,900 ZEC, or 8.5% of shielded\n+ZEC [^cipherscan-supply]. Disabling deposits now, while all funds remain fully\n+spendable, gives custodians and wallets a firm deadline and makes the\n+remaining balance only decline.\n+\n+The cost of doing so is low. Excluding mining pool coinbase shielding and the\n+Zcash Community Grants funding stream, which can move to Ironwood (see Open\n+issues), only 1.2% of non-coinbase transactions in the last 30 days touched\n+the *Sapling pool*, and they withdrew 101,216 ZEC more than they deposited.\n+Coinbase outputs to Sapling are essentially unused. [^sapling-activity]\n+\n+As with Sprout [^zip-0211], disabling additions also limits the impact of any\n+undiscovered soundness bug in Sapling, and is a precondition for eventually\n+removing the Sapling shielded protocol.\n+\n+\n+# Privacy Implications\n+\n+Withdrawals from the *Sapling pool* reveal the amount that leaves it, through\n+the Sapling value balance. This is unchanged by this ZIP. However, the ZIP will\n+cause most remaining Sapling value to cross a turnstile over time. Wallets\n+should therefore migrate Sapling funds in a way that limits linkability of the\n+amounts moved; see [Changes to ZIP 318](#changestozip318).\n+\n+The *Sapling pool* anonymity set is its note commitment set, which this ZIP\n+does not reduce. As fewer transactions use the pool, individual Sapling\n+transactions may become easier to distinguish by timing and amount, as is true\n+of any shielded pool with low usage, but the unlinkability of spent notes from\n+the transactions that created them is unaffected.\n+\n+Full nodes change behaviour at the NU8 activation height, and from that height\n+consensus prevents all wallets from adding value to the *Sapling pool*, so\n+wallets also change behaviour at that height in this respect. When a wallet\n+chooses to migrate its users' Sapling funds is not constrained to a single\n+height; using a common migration procedure (ZIP 318 [^zip-0318]) limits how much\n+that choice distinguishes wallets.\n+\n+\n+# Requirements\n+\n+* No new value can enter the *Sapling pool* after activation, from any\n+  transaction version, including coinbase.\n+* Holders keep full access to their Sapling funds. They can spend to any\n+  other pool (in practice Ironwood or transparent), and can keep making\n+  Sapling-to-Sapling transfers, including to other people.\n+* No change to the Sapling circuits or parameters, and no new trusted setup.\n+* The consensus change is small and easy to analyse.\n+\n+\n+# Non-requirements\n+\n+* This ZIP does not set a date after which Sapling notes can no longer be\n+  spent. That is left to a future proposal. So is what happens to any value\n+  left in the pool at that point.\n+* This ZIP does not make Sapling quantum recoverable.\n+* This ZIP does not restrict the *Sprout pool* or the transparent pool.\n+\n+\n+# Specification\n+\n+## Consensus rules\n+\n+The following rules apply to every transaction in a block at a height greater\n+than or equal to the NU8 activation height, regardless of transaction version:\n+\n+* $\\mathsf{v}^{\\textit{SaplingPoolBalance}}$ MUST be nonnegative, where\n+  $\\mathsf{v}^{\\textit{SaplingPoolBalance}}$ is the transaction's Sapling\n+  balancing value as defined in § 4.13 ‘Balance and Binding Signature\n+  (Sapling)’. [^protocol-saplingbalance]\n+\n+* A coinbase transaction MUST NOT contain any Sapling Output descriptions;\n+  that is, `nOutputsSapling` MUST be zero. Coinbase transactions are already\n+  required to have `nSpendsSapling` equal to zero.\n+\n+Non-normative notes:\n+\n+* $\\mathsf{v}^{\\textit{SaplingPoolBalance}}$ is the value of Sapling Spends\n+  minus Outputs. It is encoded as the `valueBalanceSapling` field of version 4,\n+  5 and 6 transactions (implicitly zero when absent), so for those versions the\n+  rule is equivalent to requiring that encoded field to be nonnegative. For a\n+  transaction format that expresses value balances as value pool deltas\n+  [^zip-0248], it corresponds to the delta for the Sapling bundle and the ZEC\n+  asset. When it is nonnegative, value can only move out of the *Sapling pool* or stay inside\n+  it. Together with the ZIP 209 [^zip-0209] rule that the Sapling chain value\n+  pool balance never goes negative, the balance can only stay the same or fall\n+  from activation onward.\n+\n+* Sapling Output descriptions remain valid in non-coinbase transactions. A\n+  transaction may pay Sapling notes to any Sapling address, including a third\n+  party's, if Sapling spends in the same transaction fund those outputs.\n+\n+* The coinbase rule is partly implied by the balance rule, because a coinbase\n+  transaction has no Sapling spends. It is stated explicitly so that\n+  zero-valued Sapling coinbase outputs are excluded too, and to mirror the\n+  ZIP 2006 rule for the *Orchard pool* [^zip-2006].\n+\n+## Changes to ZIP 213\n+\n+In the Specification section of ZIP 213 [^zip-0213], after the NU6.3 rules,\n+add:\n+\n+> Once the NU8 network upgrade activates:\n+>\n+> - Coinbase transactions MAY contain *Ironwood-pool* outputs.\n+> - Coinbase transactions MUST NOT contain Sapling outputs.\n+\n+## Changes to the Protocol Specification\n+\n+§ 7.1.2 ‘Transaction Consensus Rules’ [^protocol-txnconsensus] gains the two\n+consensus rules above, gated on NU8 activation, alongside the existing NU6.3\n+rule that $\\mathsf{v}^{\\textit{OrchardPoolBalance}}$ is nonnegative.\n+\n+## Changes to ZIP 318\n+\n+ZIP 318 [^zip-0318] is extended so that its migration procedure can take either\n+the *Orchard pool* or the *Sapling pool* as its source pool, with the\n+*Ironwood pool* as the destination in both cases:\n+\n+* A migration MUST draw on a single source pool. A wallet with funds in both\n+  the *Sapling pool* and the *Orchard pool* runs a separate migration for\n+  each, and no note preparation or migration transaction spends from both.\n+* For a Sapling-source migration, each canonical migration transaction has\n+  exactly one Sapling spend in place of the single Orchard spend, with the\n+  Sapling bundle padded so that the presence or absence of a Sapling change\n+  output is not observable, and no spends from any other pool. All other\n+  properties of the canonical migration transaction are unchanged.\n+* Note preparation for a Sapling-source migration is performed within the\n+  *Sapling pool*.\n+\n+Mixing source pools within one transaction or one migration schedule would\n+link a user's Sapling and Orchard holdings, so each migration is restricted to\n+one source pool.\n+\n+## Requirements for wallets and node software\n+\n+From NU8 activation:\n+\n+* Wallets MUST NOT create transactions that contain one or more Sapling\n+  outputs together with one or more inputs from outside the *Sapling pool*\n+  (transparent inputs, or spends from any other shielded pool). As with\n+  ZIP 211 [^zip-0211], this is stricter than consensus, for usability\n+  reasons. Consensus would accept a mixed transaction whose Sapling spends\n+  cover its Sapling outputs.\n+\n+* Wallets SHOULD NOT create new Unified Addresses that include a Sapling\n+  receiver. They MAY keep producing Sapling receivers before activation for\n+  compatibility.\n+\n+* Wallets that hold Sapling funds SHOULD offer to migrate them to the\n+  *Ironwood pool*. Wallets SHOULD use the migration procedure of ZIP 318\n+  [^zip-0318], as extended below.\n+\n+* Block template software SHOULD reject Sapling-only miner addresses with a\n+  clear error.\n+\n+Transactions signed for an earlier consensus branch become invalid at NU8\n+activation under ZIP 200 [^zip-0200] and ZIP 244 [^zip-0244], so no\n+mempool-specific rule is needed.\n+\n+\n+# Rationale\n+\n+## Why a balance rule, not a circuit change\n+\n+The rule mirrors ZIP 211 for Sprout and ZIP 2006 for Orchard. It only\n+needs a check on a field validators already parse. It does not touch the\n+Sapling Spend or Output circuits. That avoids a new parameter generation\n+ceremony, any risk of a soundness regression in Groth16 circuits, and any new\n+transaction version. The implementation is a few lines in each full validator,\n+and its security is easy to analyse.\n+\n+## Why internal transfers stay allowed\n+\n+ZIP 2006 also disabled cross-address transfers within the *Orchard pool*. That\n+was possible because the new Orchard Action circuit had an `enableCrossAddress`\n+flag [^zip-2006]. Doing the same for Sapling would need a new Sapling circuit\n+and trusted setup, which this ZIP sets out to avoid.\n+\n+Allowing Sapling outputs also lets wallets keep using Sapling change when\n+withdrawing, so a withdrawal reveals only the amount leaving the pool rather\n+than the total value of the notes spent.\n+\n+## Why the coinbase prohibition is explicit\n+\n+ZIP 213 has allowed coinbase outputs to Sapling addresses since Heartwood\n+[^zip-0213], but miners have not adopted it: only one Mainnet block has ever\n+paid its coinbase to a Sapling output. {TODO: cite the block height and txid.}\n+Shielded coinbase has instead moved to the Orchard protocol, which since NU6.3\n+pays into the *Ironwood pool*. In the 30 days to height 3,532,124, 4.6% of\n+blocks paid their coinbase directly to the *Ironwood pool*, and none contained\n+a Sapling output. [^sapling-activity] Leaving Sapling coinbase outputs\n+available invites zero-value or misconfigured outputs that add commitments\n+with no purpose. The explicit rule matches the ZIP 2006 Orchard rule.\n+\n+# Deployment\n+\n+This ZIP is proposed for deployment in the NU8 network upgrade.\n+\n+\n+# Open issues\n+\n+* **Mining pool coinbase shielding.** 58.8% of all Sapling transactions in\n+  the 30 days to height 3,532,124 (3,737 of 6,352) are attributable to mining\n+  pools shielding transparent coinbase rewards into Sapling: 2,705 deposits\n+  carrying 31,228 ZEC, and at least 1,032 withdrawals carrying 16,432 ZEC that\n+  return those funds to transparent addresses. Most of these pools send the\n+  funds back to their own coinbase payout address shortly afterwards (F2Pool,\n+  Luxor, Kryptex, Mining Dutch), or on to a transparent payout transaction\n+  (2Miners), so the shielding is a round trip rather than a shielded payout to\n+  miners. ViaBTC and ViaBTC-Solo deposited a further 14,633 ZEC whose\n+  withdrawals could not be attributed. Together, pools that shield their\n+  coinbase into Sapling mined about 72% of blocks in that window.\n+  [^sapling-activity] These pools need to shield into the *Ironwood pool*\n+  instead, and should be contacted before activation.\n+\n+* **Zcash Community Grants funding stream.** The ZCG funding stream address\n+  [^zip-0214] sweeps its coinbase outputs into Sapling. In the same window\n+  this was 32 transactions, 0.5% of all Sapling transactions, but 4,000 ZEC,\n+  or 9.5% of all ZEC deposited into Sapling. [^sapling-activity] These sweeps\n+  should move to the *Ironwood pool* before activation.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2026.6.4 or later](protocol/protocol.pdf)\n+\n+[^protocol-networks]: [Zcash Protocol Specification. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^protocol-chainvaluepoolbalances]: [Zcash Protocol Specification. Section 4.17: Chain Value Pool Balances](protocol/protocol.pdf#chainvaluepoolbalances)\n+\n+[^protocol-saplingbalance]: [Zcash Protocol Specification. Section 4.13: Balance and Binding Signature (Sapling)](protocol/protocol.pdf#saplingbalance)\n+\n+[^protocol-txnconsensus]: [Zcash Protocol Specification. Section 7.1.2: Transaction Consensus Rules](protocol/protocol.pdf#txnconsensus)\n+\n+[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)\n+\n+[^zip-0205]: [ZIP 205: Deployment of the Sapling Network Upgrade](zip-0205.rst)\n+\n+[^zip-0209]: [ZIP 209: Prohibit Negative Shielded Chain Value Pool Balances](zip-0209.rst)\n+\n+[^zip-0211]: [ZIP 211: Disabling Addition of New Value to the Sprout Chain Value Pool](zip-0211.rst)\n+\n+[^zip-0213]: [ZIP 213: Shielded Coinbase](zip-0213.rst)\n+\n+[^zip-0214]: [ZIP 214: Consensus rules for a Zcash Development Fund](zip-0214.rst)\n+\n+[^zip-0229]: [ZIP 229: Version 6 Transaction Format](zip-0229.md)\n+\n+[^zip-0244]: [ZIP 244: Transaction Identifier Non-Malleability](zip-0244.rst)\n+\n+[^zip-0248]: [ZIP 248: Extensible Transaction Format](zip-0248.rst)\n+\n+\n+\n+[^zip-0318]: [ZIP 318: Orchard to Ironwood Migration](zip-0318.md)\n+\n+[^zip-2005]: [ZIP 2005: Ironwood Quantum Recoverability](zip-2005.md)\n+\n+[^zip-2006]: [ZIP 2006: Restricting Transfers into the Orchard Pool](zip-2006.md)\n+\n+[^Ironwood-book]: [The Ironwood Book](https://zcash.github.io/ironwood/)\n+\n+[^ironwood-verification]: [Formal Verification of Zcash Ironwood Completed](https://tachyon.z.cash/blog/ironwood-verification-complete/)\n+\n+[^sapling-activity]: Analysis of all 34,560 Mainnet blocks at heights 3,497,565 to 3,532,124 (2026-09-27 to 2026-10-27), using per-transaction data from the [CipherScan API](https://api.mainnet.cipherscan.app/api/block/3532124). Transactions were classified by the sign of `valueBalanceSapling`: deposits (negative), withdrawals (positive beyond the fee, or with transparent or other shielded outputs), and internal transfers (otherwise). A deposit is attributed to a mining pool or to the ZCG funding stream when that party's coinbase payout address supplied most of its transparent input value. A withdrawal is attributed to a pool when it pays that pool's coinbase payout address, or when the value leaving Sapling exactly equals the sum of up to its number of Sapling spends of that pool's deposits made in the preceding 4,032 blocks. Pool names are from CipherScan's labels for the coinbase payout address. Hashpower share is estimated by share of blocks mined. Each block is assigned to the *Ironwood pool* if its coinbase transaction has Ironwood outputs; otherwise to the pool into which most of the in-window coinbase value of its miner payout address (excluding the funding stream address `t3cFfPt1Bcvgez9ZbMBFWeZsskxTkPzGCow`) was subsequently shielded. This gives Sapling 71.6% of blocks and Ironwood 28.4% (23.8% by later sweep and 4.6% by shielded coinbase).\n+\n+[^tachyon]: [Tachyon: Scaling Zcash with Oblivious Synchronization](https://seanbowe.com/blog/tachyon-scaling-zcash-oblivious-synchronization/)\n+\n+[^cipherscan-supply]: [CipherScan Mainnet chain value pool balances](https://api.mainnet.cipherscan.app/api/supply), retrieved 2026-10-27 at height 3,532,124."}],"filesTruncated":false,"preview":{"filename":"zips/draft-valargroup-sapling-disable-deposits.md","title":"Disabling Sapling Pool Deposits","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"consensus-rules","text":"Consensus rules"},{"level":2,"id":"changes-to-zip-213","text":"Changes to ZIP 213"},{"level":2,"id":"changes-to-the-protocol-specification","text":"Changes to the Protocol Specification"},{"level":2,"id":"changes-to-zip-318","text":"Changes to ZIP 318"},{"level":2,"id":"requirements-for-wallets-and-node-software","text":"Requirements for wallets and node software"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"why-a-balance-rule-not-a-circuit-change","text":"Why a balance rule, not a circuit change"},{"level":2,"id":"why-internal-transfers-stay-allowed","text":"Why internal transfers stay allowed"},{"level":2,"id":"why-the-coinbase-prohibition-is-explicit","text":"Why the coinbase prohibition is explicit"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1420,"title":"ZIP 221: say which epoch's branch ID personalizes the root at an activation block","author":"daira","createdAt":"2026-10-28T12:51:35Z","updatedAt":"2026-10-28T12:54:19Z","url":"https://github.com/zcash/zips/pull/1420","diffUrl":"https://github.com/zcash/zips/pull/1420.diff","headRef":"zip-221-branch-id-wording","headRepo":"daira/zips","headSha":"5b7a5e49fdbfaae398ed0ccb1147b3869a72cb19","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Fixes #1417. ZIP 221's \"Tree Node specification\" said that `CONSENSUS_BRANCH_ID` in the personalization is the branch ID \"for the epoch of the block containing the commitment\". At the activation block of a network upgrade after Heartwood, that reads as the new epoch's branch ID. The pseudocode (`mak","zipRefs":[221],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0221.rst","status":"modified","additions":5,"deletions":3,"patch":"@@ -188,9 +188,11 @@ Tree Node specification\n \n Unless otherwise noted, all hashes use BLAKE2b-256 with the personalization field set\n to ``'ZcashHistory' || CONSENSUS_BRANCH_ID``. ``CONSENSUS_BRANCH_ID`` is the 4-byte\n-little-endian encoding of the consensus branch ID for the epoch of the block containing\n-the commitment. [#zip-0200]_ Which is to say, each node in the tree commits to the\n-consensus branch that produced it.\n+little-endian encoding of the consensus branch ID for the epoch of the blocks that the\n+tree covers. [#zip-0200]_ That is, each node in the tree commits to the consensus\n+branch that produced it. At the activation block of a network upgrade after\n+Heartwood, the tree covers the blocks of the previous epoch, so the root commitment in\n+that block's header is hashed with the consensus branch ID of the previous epoch.\n \n Each MMR node is defined as follows:\n "}],"filesTruncated":false,"preview":{"filename":"zips/zip-0221.rst","title":"FlyClient - Consensus-Layer Changes","status":"Final","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"background","text":"Background"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"tree-node-specification","text":"Tree Node specification"},{"level":2,"id":"tree-nodes-and-hashing-pseudocode","text":"Tree nodes and hashing (pseudocode)"},{"level":2,"id":"incremental-push-and-pop-pseudocode","text":"Incremental push and pop (pseudocode)"},{"level":2,"id":"block-header-semantics-and-consensus-rules","text":"Block header semantics and consensus rules"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"tree-nodes","text":"Tree nodes"},{"level":3,"id":"flyclient-requirements-and-recommendations","text":"FlyClient Requirements and Recommendations"},{"level":3,"id":"non-flyclient-commitments","text":"Non-FlyClient Commitments"},{"level":2,"id":"header-format-change","text":"Header Format Change"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"additional-reading","text":"Additional Reading"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1406,"title":"ZIP 311: add Orchard and Ironwood output disclosures","author":"Cosmiq2025","createdAt":"2026-10-20T05:38:23Z","updatedAt":"2026-10-27T13:48:57Z","url":"https://github.com/zcash/zips/pull/1406","diffUrl":"https://github.com/zcash/zips/pull/1406.diff","headRef":"zip-311-orchard-ironwood-outputs","headRepo":"Cosmiq2025/zips","headSha":"8a1d4dfb45e207e711597aa5f6e81e14731c1453","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This fills in the \"TODO: Add support for Orchard\" in ZIP 311 for outputs only. - Adds `orchardOutputs` and `ironwoodOutputs` to the payment disclosure, each a list of (Action index, ock), with the same structural checks as `saplingOutputs`. - Verification recovers each disclosed output with the exis","zipRefs":[311],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0311.rst","status":"modified","additions":84,"deletions":10,"patch":"@@ -167,6 +167,11 @@ We also define the following notation here:\n \n - $\\mathsf{length}(a)$ means the length of the sequence $a$.\n \n+- For a transaction $\\mathsf{tx}$ that has no $\\mathsf{vActionsOrchard}$ (respectively\n+  $\\mathsf{vActionsIronwood}$) field, that is, with a version lower than 5 (respectively 6)\n+  [#zip-0225]_ [#zip-0229]_, $\\mathsf{tx.vActionsOrchard}$ (respectively\n+  $\\mathsf{tx.vActionsIronwood}$) denotes the empty sequence.\n+\n \n Specification\n =============\n@@ -217,7 +222,23 @@ A payment disclosure has the following fields:\n   - $\\mathsf{ock}$: The outgoing cipher key that allows this output to be recovered.\n     [#protocol-saplingencrypt]_\n \n-TODO: Add support for Orchard.\n+- $\\mathsf{orchardOutputs}$: A sequence of the Orchard-pool Actions whose outputs we are\n+  disclosing $[0..\\mathsf{length}(\\mathsf{tx.vActionsOrchard})]$\n+\n+  - $\\mathsf{index}$: An index into $\\mathsf{tx.vActionsOrchard}$.\n+  - $\\mathsf{ock}$: The outgoing cipher key that allows the output of this Action to be\n+    recovered. [#protocol-saplingencrypt]_\n+\n+- $\\mathsf{ironwoodOutputs}$: A sequence of the Ironwood-pool Actions whose outputs we\n+  are disclosing $[0..\\mathsf{length}(\\mathsf{tx.vActionsIronwood})]$\n+\n+  - $\\mathsf{index}$: An index into $\\mathsf{tx.vActionsIronwood}$.\n+  - $\\mathsf{ock}$: The outgoing cipher key that allows the output of this Action to be\n+    recovered. [#protocol-saplingencrypt]_\n+\n+TODO: Add support for proving Orchard and Ironwood spend authority. Until then, a payment\n+disclosure can only disclose Orchard or Ironwood outputs of a transaction that also has a\n+transparent input or a Sapling spend.\n \n TODO: Decide on payment disclosure versioning.\n \n@@ -231,7 +252,8 @@ The inputs to a payment disclosure are:\n - The transaction.\n - The SLIP-44 [#slip-0044]_ coin type.\n - The message $msg$ to be included (which may be empty).\n-- A sequence of $(\\mathsf{outputIndex}, \\mathsf{ock})$ tuples (which may be empty).\n+- For each of the Sapling, Orchard, and Ironwood pools, a sequence of\n+  $(\\mathsf{outputIndex}, \\mathsf{ock})$ tuples (each of which may be empty).\n - A sequence of Sapling spend tuples (which may be empty) containing:\n \n   - A Sapling spend index.\n@@ -243,6 +265,12 @@ The inputs to a payment disclosure are:\n   - $\\mathsf{index}$: An index into $\\mathsf{tx.vin}$.\n   - The inputs to a BIP 322 signature (excluding `message_data`).\n \n+For Orchard and Ironwood outputs, the sender computes\n+$\\mathsf{ock} = \\mathsf{PRF^{ockOrchard}_{ovk}}(\\mathtt{cv}, \\mathtt{cmx}, \\mathtt{ephemeralKey})$\n+from the fields of the Action, where $\\mathsf{ovk}$ is the (external or internal)\n+outgoing viewing key that was used when the transaction was created.\n+[#protocol-saplingencrypt]_\n+\n The caller MUST provide at least one input tuple of any type (either a Sapling spend tuple\n or a transparent input tuple).\n \n@@ -264,8 +292,9 @@ The payment disclosure is created as follows:\n \n   - TODO: Prepare BIP 322 signature inputs using `msg` as the `message_data`.\n \n-- Construct an unsigned payment disclosure from the disclosed Sapling outputs, and the\n-  above data for the Sapling spends and transparent inputs. Define the encoding of this as\n+- Construct an unsigned payment disclosure from the disclosed Sapling, Orchard, and\n+  Ironwood outputs, and the above data for the Sapling spends and transparent inputs.\n+  Define the encoding of this as\n   $unsignedPaymentDisclosure$.\n \n - For each Sapling spend index:\n@@ -300,13 +329,19 @@ by the caller), the verifier proceeds as follows:\n   - Sequence length correctness:\n \n     - $\\mathsf{length}(\\mathsf{pd.saplingOutputs}) \\leq \\mathsf{length}(\\mathsf{tx.shieldedOutputs})$\n+    - $\\mathsf{length}(\\mathsf{pd.orchardOutputs}) \\leq \\mathsf{length}(\\mathsf{tx.vActionsOrchard})$\n+    - $\\mathsf{length}(\\mathsf{pd.ironwoodOutputs}) \\leq \\mathsf{length}(\\mathsf{tx.vActionsIronwood})$\n     - $\\mathsf{length}(\\mathsf{pd.saplingSpends}) \\leq \\mathsf{length}(\\mathsf{tx.shieldedSpends})$\n     - $\\mathsf{length}(\\mathsf{pd.transparentInputs}) \\leq \\mathsf{length}(\\mathsf{tx.vin})$\n \n   - Index uniqueness:\n \n     - For every $\\mathsf{output}$ in $\\mathsf{pd.saplingOutputs}$,\n       $\\mathsf{output.index}$ only occurs once.\n+    - For every $\\mathsf{output}$ in $\\mathsf{pd.orchardOutputs}$,\n+      $\\mathsf{output.index}$ only occurs once.\n+    - For every $\\mathsf{output}$ in $\\mathsf{pd.ironwoodOutputs}$,\n+      $\\mathsf{output.index}$ only occurs once.\n     - For every $\\mathsf{spend}$ in $\\mathsf{pd.saplingSpends}$,\n       $\\mathsf{spend.index}$ only occurs once.\n     - For every $\\mathsf{input}$ in $\\mathsf{pd.transparentInputs}$,\n@@ -316,6 +351,10 @@ by the caller), the verifier proceeds as follows:\n \n     - For every $\\mathsf{output}$ in $\\mathsf{pd.saplingOutputs}$,\n       $\\mathsf{output.index} < \\mathsf{length}(\\mathsf{tx.shieldedOutputs})$\n+    - For every $\\mathsf{output}$ in $\\mathsf{pd.orchardOutputs}$,\n+      $\\mathsf{output.index} < \\mathsf{length}(\\mathsf{tx.vActionsOrchard})$\n+    - For every $\\mathsf{output}$ in $\\mathsf{pd.ironwoodOutputs}$,\n+      $\\mathsf{output.index} < \\mathsf{length}(\\mathsf{tx.vActionsIronwood})$\n     - For every $\\mathsf{spend}$ in $\\mathsf{pd.saplingSpends}$,\n       $\\mathsf{spend.index} < \\mathsf{length}(\\mathsf{tx.shieldedSpends})$\n     - For every $\\mathsf{input}$ in $\\mathsf{pd.transparentInputs}$,\n@@ -354,11 +393,28 @@ by the caller), the verifier proceeds as follows:\n \n   - TODO: BIP 322 verification.\n \n+In the following steps, \"recover\" means running the procedure specified in\n+[#protocol-saplingdecryptovk]_ (for Ironwood-pool outputs, as specified in\n+[#protocol-nu6-3-decryptovk]_, which includes the changes made by [#zip-2005]_), with\n+$\\mathsf{ock} = \\mathsf{output.ock}$ in place of its first step,\n+$\\mathsf{BlockHeight} = height$, $\\mathsf{TxVersion}$ set to the version of\n+$\\mathsf{tx}$, and $\\mathsf{Pool}$ set to the pool of the disclosed output.\n+\n - For every $\\mathsf{output}$ in $\\mathsf{pd.saplingOutputs}$:\n \n-  - Recover the Sapling note in $\\mathsf{tx.shieldedOutputs}[\\mathsf{output.index}]$\n-    via the process specified in [#protocol-saplingdecryptovk]_ with inputs\n-    $(height, \\mathsf{output.ock})$. If recovery returns $\\bot$, return false.\n+  - Recover the Sapling note in $\\mathsf{tx.shieldedOutputs}[\\mathsf{output.index}]$.\n+    If recovery returns $\\bot$, return false.\n+\n+- For every $\\mathsf{output}$ in $\\mathsf{pd.orchardOutputs}$:\n+\n+  - Recover the Orchard-pool note in $\\mathsf{tx.vActionsOrchard}[\\mathsf{output.index}]$.\n+    If recovery returns $\\bot$, return false.\n+\n+- For every $\\mathsf{output}$ in $\\mathsf{pd.ironwoodOutputs}$:\n+\n+  - Recover the Ironwood-pool note in\n+    $\\mathsf{tx.vActionsIronwood}[\\mathsf{output.index}]$. If recovery returns\n+    $\\bot$, return false.\n \n - Return true.\n \n@@ -383,6 +439,19 @@ payment disclosure that discloses that output. This maintains the semantics of\n $\\mathsf{ovk}$, in that the sender explicitly chose to lose the capability to\n recover that output.\n \n+The same applies to Orchard and Ironwood: an Action whose output was encrypted with\n+$\\mathsf{ovk} = \\bot$ cannot be disclosed. Where a value of $\\mathsf{ovk}$ was used,\n+$\\mathsf{ock}$ is derived from it and from the $\\mathtt{cv}$, $\\mathtt{cmx}$, and\n+$\\mathtt{ephemeralKey}$ fields of that Action [#protocol-saplingencrypt]_, so disclosing\n+one $\\mathsf{ock}$ reveals only the output of that Action.\n+\n+Orchard and Ironwood outputs are disclosed in separate sequences because their indices\n+refer to different transaction fields; both use the same Action encoding [#zip-0229]_.\n+\n+From NU6.3 activation, no new value may enter the Orchard pool and Orchard-pool Actions\n+cannot make cross-address transfers [#zip-0258]_. Disclosing an Orchard output is\n+therefore mainly useful for transactions mined before NU6.3 activation.\n+\n Payment disclosures that prove Sapling spend authority are not required to reveal a\n sender address. This is because it is impossible: we can \"prove\" the transaction came from\n any of the diversified addresses linked to the spending key. Fundamentally, the \"sender\"\n@@ -419,7 +488,7 @@ order to simplify the verification UX. In particular, if payment disclosures wit\n spends were considered valid, an invalid payment disclosure with invalid signatures (that\n would be shown as invalid by UIs) could be mutated into a payment disclosure that would be\n shown as valid by UIs, by stripping off the signatures. We do not believe that this\n-prevents any useful use cases; meanwhile if someone is intent on obtaining Sapling output\n+prevents any useful use cases; meanwhile if someone is intent on obtaining shielded output\n disclosures regardless of the validity of their source, they will do so without a common\n standard.\n \n@@ -437,10 +506,15 @@ References\n .. [#RFC4648] `RFC 4648: The Base16, Base32, and Base64 Data Encodings <https://www.rfc-editor.org/rfc/rfc4648>`_\n .. [#protocol] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1] or later <protocol/protocol.pdf>`_\n .. [#protocol-spendstatement] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.15.2: Spend Statement (Sapling) <protocol/protocol.pdf#spendstatement>`_\n-.. [#protocol-saplingencrypt] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. 4.19.1: Encryption (Sapling) <protocol/protocol.pdf#saplingencrypt>`_\n-.. [#protocol-saplingdecryptovk] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.3: Decryption using an Outgoing Viewing Key (Sapling) <protocol/protocol.pdf#saplingdecryptovk>`_\n+.. [#protocol-saplingencrypt] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.1: Encryption (Sapling and Orchard) <protocol/protocol.pdf#saplingandorchardencrypt>`_\n+.. [#protocol-saplingdecryptovk] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.3: Decryption using an Outgoing Viewing Key (Sapling and Orchard) <protocol/protocol.pdf#decryptovk>`_\n+.. [#protocol-nu6-3-decryptovk] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.20.3: Decryption using an Outgoing Viewing Key (Sapling and Orchard) <protocol/nu6_3.pdf#decryptovk>`_\n .. [#protocol-concretediversifyhash] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.1.6: DiversifyHash Hash Function <protocol/protocol.pdf#concretediversifyhash>`_\n .. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.6.1: Spend Authorization Signature <protocol/protocol.pdf#concretespendauthsig>`_\n .. [#bip-0322] `BIP 322: Generic Signed Message Format <https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki>`_\n .. [#slip-0044] `SLIP-0044 : Registered coin types for BIP-0044 <https://github.com/satoshilabs/slips/blob/master/slip-0044.md>`_\n+.. [#zip-0225] `ZIP 225: Version 5 Transaction Format <zip-0225.rst>`_\n+.. [#zip-0229] `ZIP 229: Version 6 Transaction Format <zip-0229.md>`_\n+.. [#zip-0258] `ZIP 258: Deployment of the NU6.3 Network Upgrade <zip-0258.md>`_\n .. [#zip-0304] `ZIP 304: Sapling Address Signatures <zip-0304.rst>`_\n+.. [#zip-2005] `ZIP 2005: Ironwood Quantum Recoverability <zip-2005.md>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0311.rst","title":"Zcash Payment Disclosures","status":"Draft","headings":[{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"use-cases","text":"Use cases"},{"level":2,"id":"managing-contributions-for-an-event","text":"Managing contributions for an event"},{"level":2,"id":"donation-drive","text":"Donation drive"},{"level":2,"id":"merchant-dispute","text":"Merchant dispute"},{"level":2,"id":"shielded-withdrawals-from-an-exchange","text":"Shielded withdrawals from an exchange"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"conventions","text":"Conventions"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"payment-disclosure-data-structure","text":"Payment disclosure data structure"},{"level":2,"id":"creating-a-payment-disclosure","text":"Creating a payment disclosure"},{"level":2,"id":"verifying-a-payment-disclosure","text":"Verifying a payment disclosure"},{"level":2,"id":"payment-disclosure-validity-in-uis","text":"Payment disclosure validity in UIs"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1398,"title":"Bump actions/create-github-app-token from 3.2.0 to 3.3.0","author":"dependabot[bot]","createdAt":"2026-10-15T15:22:48Z","updatedAt":"2026-10-26T09:20:47Z","url":"https://github.com/zcash/zips/pull/1398","diffUrl":"https://github.com/zcash/zips/pull/1398.diff","headRef":"dependabot/github_actions/actions/create-github-app-token-3.3.0","headRepo":"zcash/zips","headSha":"bea4c040ea4cbd69814200a9f69498494f9e709f","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 3.2.0 to 3.3.0. Release notes Sourced from actions/create-github-app-token's releases. v3.3.0 3.3.0 (2026-10-12) Features update permission inputs (#399) (a47236f) Bug Fixes deps: bump nested undici from","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":".github/workflows/publish.yaml","status":"modified","additions":1,"deletions":1,"patch":"@@ -27,7 +27,7 @@ jobs:\n     steps:\n       - name: Generate app token\n         id: app-token\n-        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0\n+        uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0\n         with:\n           client-id: ${{ secrets.APP_ID }}\n           private-key: ${{ secrets.APP_PRIVATE_KEY }}"}],"filesTruncated":false,"preview":null},{"number":1407,"title":"ZIP 226: Update for the Ironwood pool and ZIP 248's transaction format","author":"daira","createdAt":"2026-10-21T12:43:46Z","updatedAt":"2026-10-22T15:43:12Z","url":"https://github.com/zcash/zips/pull/1407","diffUrl":"https://github.com/zcash/zips/pull/1407.diff","headRef":"zsa-ironwood-update","headRepo":"daira/zips","headSha":"972214a9ff218b1dce0564a21a462bd62b809e27","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This updates ZIP 226 (OrchardZSA) for the current state of the protocol, notes where the ZSA ZIPs still rely on the withdrawn ZIP 230 design, and removes reference entries that nothing cites from the ZSA ZIPs. It should be merged after #1358. ## ZIP 226 - **Pool** — OrchardZSA notes would be held on","zipRefs":[226,227,228],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"AGENTS.md","status":"modified","additions":6,"deletions":0,"patch":"@@ -131,6 +131,12 @@ footnotes) and add a corresponding entry in the References section. See\n `zips/zip-guide-markdown.md` for more information about conventions and\n syntax.\n \n+Fix errors in references as you discover them. Examples are a citation\n+with no entry in the References section, an entry that nothing cites\n+(often left behind when the citing text moved to another ZIP), and an\n+entry whose link target or title is out of date. Whether the fix goes in\n+the current PR or in a new one is decided case by case.\n+\n For reStructuredText ZIPs, see `zips/zip-guide.rst` for conventions and\n syntax.\n "},{"filename":"zips/zip-0226.rst","status":"modified","additions":82,"deletions":72,"patch":"@@ -29,6 +29,8 @@ The character § is used when referring to sections of the Zcash Protocol Specif\n \n The terms \"Orchard\" and \"Action\" in this document are to be interpreted as described in ZIP 224. [#zip-0224]_\n \n+The terms \"Orchard shielded pool\" and \"Ironwood shielded pool\" in this document are to be interpreted as described in ZIP 229. [#zip-0229]_\n+\n The terms \"Asset\" and \"Custom Asset\" in this document are to be interpreted as described in ZIP 227. [#zip-0227]_\n \n We define the following additional terms:\n@@ -42,7 +44,7 @@ Abstract\n This ZIP (ZIP 226) proposes the Orchard Zcash Shielded Assets (OrchardZSA) protocol, in conjunction with ZIP 227 [#zip-0227]_. The OrchardZSA protocol is an extension of the Orchard protocol that enables the issuance, transfer and burn of custom Assets on the Zcash chain. The issuance of such Assets is defined in ZIP 227 [#zip-0227]_, while the transfer and burn of such Assets is defined in this ZIP (ZIP 226).\n While the proposed OrchardZSA protocol is a modification to the Orchard protocol, it has been designed with adaptation to possible future shielded protocols in mind.\n \n-This ZIP is defined relative to the Zcash protocol with the changes specified in ZIP 2005 [#zip-2005]_ applied. ZIP 2005 (Orchard Quantum Recoverability) is expected to deploy before any ZSA activation; the references in this document to $\\mathsf{H^{rcm,Orchard}}$, $\\mathsf{H^{\\text{ψ},Orchard}}$, recoverable note plaintexts, and related constructs are to be interpreted as defined by ZIP 2005's modifications to the protocol specification.\n+This ZIP is defined relative to the Zcash protocol specification version 2026.8.0 [#protocol-base]_.\n \n Motivation\n ==========\n@@ -73,13 +75,12 @@ Finally, in this ZIP we also describe the *burn* mechanism, which is a direct ex\n Privacy Implications\n ====================\n \n-- After the protocol upgrade, the Orchard shielded pool will be shared by the\n-  Orchard protocol and the OrchardZSA protocol.\n+- After the protocol upgrade, the Ironwood shielded pool will be shared by the\n+  Orchard protocol and the OrchardZSA protocol. The Orchard shielded pool cannot\n+  contain OrchardZSA notes.\n - Deploying the OrchardZSA protocol does not necessitate disabling the Orchard\n   protocol. Both can co-exist and be addressed via different transaction\n-  versions (V5 for Orchard and V6 for OrchardZSA). Due to this, Orchard note\n-  commitments can be distinguished from OrchardZSA note commitments. This holds\n-  whether or not the two protocols are active simultaneously.\n+  versions.\n - OrchardZSA note commitments for the native asset (ZEC) are indistinguishable\n   from OrchardZSA note commitments for non-native Assets.\n - When including new Assets we would like to maintain the amount and identifiers\n@@ -157,24 +158,25 @@ Note that $\\mathsf{repr}_{\\mathbb{P}}$ and $\\mathsf{GroupHash}^{\\mathbb{P}}$ are\n The nullifier is generated in the same manner as in the Orchard protocol §4.16 ‘Computing ρ values and Nullifiers’ [#protocol-rhoandnullifiers]_.\n \n The OrchardZSA note plaintext also includes the Asset Base $\\mathsf{asset\\_base} : \\mathbb{B}^{[\\ell_{\\mathbb{P}}]}$ in addition to the components in the Orchard note plaintext [#protocol-notept]_.\n-The explicit encoding of the note plaintext is provided in ZIP 230 [#zip-0230-orchard-note-plaintext]_.\n \n-When § 4.7.3 'Sending Notes (Orchard)' [#protocol-orchardsend]_ or § 4.8.3 'Dummy Notes (Orchard)' [#protocol-orcharddummynotes]_ are invoked directly or indirectly in the computation of $\\text{ρ}$ and $\\text{ψ}$ for an OrchardZSA note, $\\mathsf{leadByte}$ MUST be set to {{ZSALEADBYTE}}.\n+**Open Issue:** The encoding of OrchardZSA bundles in the transaction format of ZIP 248 [#zip-0248]_ is yet to be specified, including the explicit encoding of the OrchardZSA note plaintext. These encodings, including that of the note plaintext [#zip-0230-orchard-note-plaintext]_, were specified for the withdrawn ZIP 230 design [#zip-0230]_.\n+\n+When § 4.7.3 'Sending Notes (Orchard)' [#protocol-orchardsend]_ or § 4.8.3 'Dummy Notes (Orchard)' [#protocol-orcharddummynotes]_ are invoked directly or indirectly in the computation of $\\text{ρ}$ and $\\text{ψ}$ for an OrchardZSA note, $\\mathsf{leadByte}$ MUST be set to {{ZSALEADBYTE}}. This is a placeholder for the note plaintext lead byte of OrchardZSA notes, whose value is yet to be assigned.\n \n The explicit order of addition of the note commitments to the note commitment tree is specified in ZIP 227 [#zip-0227-note-commitment-order]_.\n \n Rationale for Note Commitment\n `````````````````````````````\n \n-In the OrchardZSA protocol, the instance of the note commitment scheme, $\\mathsf{NoteCommit^{OrchardZSA}_{rcm}}$, differs from the Orchard note commitment $\\mathsf{NoteCommit^{Orchard}_{rcm}}$ in that for Custom Assets, the Asset Base will be added as an input to the commitment computation.\n-In the case where the Asset is the ZEC Asset, the commitment is computed identically to the Orchard note commitment, without making use of the ZEC Asset Base as an input.\n+In the OrchardZSA protocol, the instance of the note commitment scheme, $\\mathsf{NoteCommit^{OrchardZSA}_{rcm}}$, differs from the Orchard-protocol note commitment scheme $\\mathsf{NoteCommit^{Orchard}_{rcm}}$ in that for Custom Assets, the Asset Base will be added as an input to the commitment computation.\n+In the case where the Asset is the ZEC Asset, the commitment is computed identically to the Orchard-protocol note commitment, without making use of the ZEC Asset Base as an input.\n As we will see, the nested structure of the Sinsemilla-based commitment [#protocol-concretesinsemillacommit]_ allows us to add the Asset Base as a final recursive step.\n \n-The note commitment output is still indistinguishable from the original Orchard ZEC note commitments, by definition of the Sinsemilla hash function [#protocol-concretesinsemillahash]_. OrchardZSA note commitments will therefore be added to the same Orchard Note Commitment Tree. In essence, we have:\n+The note commitment output is still indistinguishable from Orchard-protocol ZEC note commitments, by definition of the Sinsemilla hash function [#protocol-concretesinsemillahash]_. OrchardZSA-protocol note commitments will therefore be added to the same note commitment tree as the Orchard-protocol notes of the Ironwood shielded pool. In essence, we have:\n \n .. math:: \\mathsf{NoteCommit^{OrchardZSA}_{rcm}}(\\mathsf{repr}_{\\mathbb{P}}(\\mathsf{g_d}), \\mathsf{repr}_{\\mathbb{P}}(\\mathsf{pk_d}), \\mathsf{v}, \\text{ρ}, \\text{ψ}, \\mathsf{AssetBase}) \\in \\mathsf{NoteCommit^{Orchard}.Output}\n \n-This definition can be viewed as a generalization of the Orchard note commitment, and will allow maintaining a single commitment instance for the note commitment, which will be used both for pre-ZSA Orchard and OrchardZSA notes.\n+This definition can be viewed as a generalization of the Orchard-protocol note commitment, and will allow maintaining a single commitment instance for the note commitment, which will be used both for pre-ZSA Orchard-protocol notes and OrchardZSA-protocol notes.\n \n Value Commitment\n ----------------\n@@ -214,6 +216,8 @@ For every Custom Asset (represented by its $\\mathsf{AssetBase}$) that is burnt i\n We define a constant $\\mathsf{MAX\\_BURN\\_VALUE} := 2^{63} - 1$, which denotes the maximum amount of a given Custom Asset that can be burnt in a transaction.\n We denote by $L$ the cardinality of the $\\mathsf{assetBurn}$ set in a transaction.\n \n+**Open Issue:** The encoding of the $\\mathsf{assetBurn}$ set is yet to be specified, as noted in `Note Structure and Commitment`_.\n+\n As described in `Value Balance Verification`_, this provides the information for the validator of the transaction to compute the value commitment with the corresponding Asset Base. \n This ensures that the values are all balanced out on a per-Asset basis in the transaction.\n \n@@ -233,7 +237,7 @@ Rationale for MAX_BURN_VALUE\n ````````````````````````````\n \n The maximum amount of any Custom Asset allowed to be burnt in a transaction is set to $\\mathsf{MAX\\_BURN\\_VALUE}$ in order to prevent it from being incompatible with other valueBalance fields, which are signed 64-bit integers.\n-It will also allow for compatibility with future Custom-asset-specific value balances in subsequent pools that support transferring ZSAs from the Orchard pool via a turnstile.\n+It will also allow for compatibility with future Custom-asset-specific value balances in subsequent pools that support transferring ZSAs from the Ironwood shielded pool via a turnstile.\n \n Value Balance Verification\n --------------------------\n@@ -249,6 +253,8 @@ The verifier MUST compute the value balance verification equation:\n \n After computing $\\mathsf{bvk}$, the verifier MUST use it to verify the binding signature on the SIGHASH transaction hash.\n \n+**Open Issue:** The transaction format of ZIP 248 [#zip-0248]_ records value balances as value pool deltas. How the value balance $\\mathsf{v^{balanceOrchard}}$ and the $\\mathsf{assetBurn}$ set used in this equation are represented in that format is yet to be specified, as noted in `Note Structure and Commitment`_.\n+\n \n Rationale for Value Balance Verification\n ````````````````````````````````````````\n@@ -294,7 +300,7 @@ For Split Notes, the nullifier is generated as follows:\n \n .. math:: \\mathsf{nf_{old}} = \\mathsf{Extract}_{\\mathbb{P}} ([(\\mathsf{PRF^{nfOrchard}_{nk}} (\\text{ρ}^{\\mathsf{old}}) + \\text{ψ}^{\\mathsf{nf}}) \\bmod q_{\\mathbb{P}}]\\,\\mathcal{K}^\\mathsf{Orchard} + \\mathsf{cm^{old}} + \\mathcal{L}^\\mathsf{Orchard})\n \n-where $\\text{ψ}^{\\mathsf{nf}}$ is computed as $\\mathsf{ToBase^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed\\_nf}}([\\mathtt{0x0A}] \\,||\\, \\underline{\\text{ρ}^{\\mathsf{old}}})\\kern-0.1em\\big)$ for $\\mathsf{rseed\\_nf}$ sampled uniformly at random on $\\mathbb{B}^{{\\kern-0.1em\\tiny\\mathbb{Y}}[32]}$, $\\mathcal{K}^{\\mathsf{Orchard}}$ is the Orchard Nullifier Base as defined in §4.16 ‘Computing ρ values and Nullifiers’ [#protocol-rhoandnullifiers]_, and $\\mathcal{L}^{\\mathsf{Orchard}} := \\mathsf{GroupHash^{\\mathbb{P}}}(\\texttt{“z.cash:Orchard”}, \\texttt{“L”})$. The first-byte domain separator $\\mathtt{0x0A}$ for $\\mathsf{PRF^{expand}}$ is reserved by ZIP 2005 [#zip-2005]_ for this use.\n+where $\\text{ψ}^{\\mathsf{nf}}$ is computed as $\\mathsf{ToBase^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed\\_nf}}([\\mathtt{0x0A}] \\,||\\, \\underline{\\text{ρ}^{\\mathsf{old}}})\\kern-0.1em\\big)$ for $\\mathsf{rseed\\_nf}$ sampled uniformly at random on $\\mathbb{B}^{{\\kern-0.1em\\tiny\\mathbb{Y}}[32]}$, $\\mathcal{K}^{\\mathsf{Orchard}}$ is the Orchard Nullifier Base as defined in §4.16 ‘Computing ρ values and Nullifiers’ [#protocol-rhoandnullifiers]_, and $\\mathcal{L}^{\\mathsf{Orchard}} := \\mathsf{GroupHash^{\\mathbb{P}}}(\\texttt{“z.cash:Orchard”}, \\texttt{“L”})$. The first-byte domain separator $\\mathtt{0x0A}$ for $\\mathsf{PRF^{expand}}$ is reserved for this use by ZIP 2005 [#zip-2005]_, as recorded in §4.1.2 ‘Pseudo Random Functions’ [#protocol-abstractprfs]_.\n \n Rationale for Split Notes\n `````````````````````````\n@@ -370,39 +376,45 @@ The input note in the old note commitment integrity check must either include an\n OrchardZSA Transaction Structure\n --------------------------------\n \n-The transaction format for v6 transactions is described in ZIP 230 [#zip-0230]_.\n-The ZSA-related changes in v6 include:\n+ZIP 248 [#zip-0248]_ specifies a new transaction version, and provisionally\n+registers a bundle variant for OrchardZSA in its format.\n \n-* updates to the transaction structure [#zip-0230-transaction-format]_ and the\n-  sighash digest computation [#zip-0246]_;\n-* new note plaintext formats for both Sapling and Orchard outputs [#zip-0230-note-plaintexts]_,\n-  with corresponding changes to the note decryption algorithms for incoming and\n-  outgoing viewing keys [#zip-2005]_.\n+**Open Issue:** The encoding of OrchardZSA bundles is yet to be specified, as\n+noted in `Note Structure and Commitment`_.\n \n Implications for Wallets\n ````````````````````````\n \n-The following requirements on wallets are specified and motivated in ZIP 230\n-[#zip-0230-implications-for-wallets]_:\n-\n-* *All* wallets should switch to sending only v6 transactions once NU7 has activated.\n-  The main consequence of not doing so relevant to ZSAs, is that users would not obtain\n-  the privacy benefit of indistinguishability between native and non-native assets in\n-  the Orchard shielded pool, because v5 transactions can only transact in the native\n-  ZEC asset. For other consequences see ZIP 230.\n-\n-* *All* wallets should be ready to receive funds in outputs of v6 transactions as soon\n-  as ZSAs activate. The consequence of not doing so would be that funds sent to Orchard\n-  addresses of a wallet without this support could be temporarily inaccessible, until\n-  the wallet is upgraded to fully support v6 and to rescan outputs since v6 activation.\n-\n-Sighash modifications relative to ZIP 244 [#zip-0244]_\n-------------------------------------------------------\n-\n-Relative to the sighash algorithm defined in ZIP 244 [#zip-0244]_, the sighash algorithm\n-that applies to v6 transactions differs by altering the Orchard bundle within\n-the tree hash to match the corresponding OrchardZSA changes. See ZIP 246 [#zip-0246]_\n-for details.\n+Once the transaction version specified by ZIP 248 is allowed on the network,\n+that ZIP requires wallets to be able to parse transactions of that version, and\n+to send only such transactions [#zip-0248-implications-for-wallets]_. For ZSAs,\n+the corresponding requirements on wallets are:\n+\n+* Once ZSAs have activated, *all* wallets should switch to sending only the transaction\n+  version that supports ZSAs. They should also use the OrchardZSA bundle variant for all of\n+  their Ironwood-pool actions, including those that transfer ZEC. The main consequence of\n+  not doing so is that users would not otherwise obtain the privacy benefit of\n+  indistinguishability between native and non-native assets in the Ironwood shielded pool,\n+  because actions in v6 and earlier transactions, and in bundles of other variants, can only\n+  transfer the native ZEC asset.\n+\n+* *All* wallets should be ready to receive funds in outputs of the transaction version that\n+  supports ZSAs as soon as they activate. The consequence of not doing so would be that funds\n+  sent to Orchard-protocol addresses of a wallet without this support could be temporarily\n+  inaccessible, until the wallet is upgraded to fully support the relevant transaction version\n+  and note plaintext lead byte, and to rescan Ironwood-pool outputs since ZSA activation.\n+\n+Sighash\n+-------\n+\n+ZIP 248 specifies the digest algorithms for the transaction version that it\n+defines [#zip-0248-digest-algorithms]_, and a mechanism for versioning sighash\n+algorithms [#zip-0248-sighash-versioning]_. Each bundle type defines its\n+contributions to those digests, and the sighash versions available to its\n+signers together with the associated data that each version requires.\n+\n+**Open Issue:** The digests of OrchardZSA bundles, and the sighash versions\n+available to their signers, are yet to be specified.\n \n Transaction Fees\n ----------------\n@@ -415,7 +427,7 @@ Backward Compatibility\n In order to have backward compatibility with the ZEC notes, we have designed the circuit to support both ZEC and OrchardZSA notes. As we specify above, there are three main reasons we can do this:\n \n - Note commitments for ZEC notes will remain the same, while note commitments for Custom Assets will be computed taking into account the $\\mathsf{AssetBase}$ value as well.\n-- The existing Orchard shielded pool will continue to be used for the new OrchardZSA notes post the upgrade.\n+- The Ironwood shielded pool, which already holds Orchard-protocol notes, will also hold the new OrchardZSA-protocol notes after the upgrade.\n - The value commitment is abstracted to allow for the value base-point as a variable private input to the proof.\n - The ZEC-based Actions will still include dummy input notes, whereas the OrchardZSA Actions will include split input notes and will not include dummy input notes.\n \n@@ -446,39 +458,37 @@ References\n .. [#zip-0209] `ZIP 209: Prohibit Out-of-Range Chain Value Pool Balances <zip-0209.rst>`_\n .. [#zip-0224] `ZIP 224: Orchard <zip-0224.rst>`_\n .. [#zip-0227] `ZIP 227: Issuance of Zcash Shielded Assets <zip-0227.rst>`_\n-.. [#zip-0227-specification-global-issuance-state] `ZIP 227: Issuance of Zcash Shielded Assets — Specification: Global Issuance State <zip-0227.rst#specification-global-issuance-state>`_\n .. [#zip-0227-assetidentifier] `ZIP 227: Issuance of Zcash Shielded Assets — Specification: Asset Identifier <zip-0227.rst#specification-asset-identifier-asset-digest-and-asset-base>`_\n .. [#zip-0227-consensus] `ZIP 227: Issuance of Zcash Shielded Assets — Specification: Consensus Rule Changes <zip-0227.rst#specification-consensus-rule-changes>`_\n .. [#zip-0227-note-commitment-order] `ZIP 227: Issuance of Zcash Shielded Assets — Addition to the Note Commitment Tree <zip-0227.rst#addition-to-the-note-commitment-tree>`_\n-.. [#zip-0227-txiddigest] `ZIP 227: Issuance of Zcash Shielded Assets — TxId Digest - Issuance <zip-0227.rst#txid-digest-issuance>`_\n-.. [#zip-0227-authcommitment] `ZIP 227: Issuance of Zcash Shielded Assets — Authorizing Data Commitment <zip-0227.rst#authorizing-data-commitment-issuance>`_\n .. [#zip-0227-orchardzsa-fee-calculation] `ZIP 227: Issuance of Zcash Shielded Assets — OrchardZSA Fee Calculation <zip-0227.rst#orchardzsa-fee-calculation>`_\n-.. [#zip-0230] `ZIP 230: Version 6 Transaction Format <zip-0230.rst>`_\n-.. [#zip-0230-transaction-format] `ZIP 230: Version 6 Transaction Format — Transaction Format <zip-0230.rst#transaction-format>`_\n-.. [#zip-0230-note-plaintexts] `ZIP 230: Version 6 Transaction Format — Note Plaintexts <zip-0230.rst#note-plaintexts>`_\n-.. [#zip-0230-orchard-note-plaintext] `ZIP 230: Version 6 Transaction Format — Orchard Note Plaintext <zip-0230.rst#orchard-note-plaintext>`_\n-.. [#zip-0230-implications-for-wallets] `ZIP 230: Version 6 Transaction Format — Implications for Wallets <zip-0230.rst#implications-for-wallets>`_\n-.. [#zip-0244] `ZIP 244: Transaction Identifier Non-Malleability <zip-0244.rst>`_\n-.. [#zip-0246] `ZIP 246: Digests for the Version 6 Transaction Format <zip-0246.rst>`_\n-.. [#zip-0307] `ZIP 307: Light Client Protocol for Payment Detection <zip-0307.rst>`_\n-.. [#zip-2005] `ZIP 2005: Orchard Quantum Recoverability <zip-2005.md>`_\n-.. [#protocol] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1] or later. <protocol/protocol.pdf>`_\n-.. [#protocol-notes] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 3.2: Notes <protocol/protocol.pdf#notes>`_\n-.. [#protocol-actions] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 3.7: Action Transfers and their Descriptions <protocol/protocol.pdf#actions>`_\n-.. [#protocol-abstractcommit] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.1.8: Commitment <protocol/protocol.pdf#abstractcommit>`_\n-.. [#protocol-orchardsend] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.7.3: Sending Notes (Orchard) <protocol/protocol.pdf#orchardsend>`_\n-.. [#protocol-orcharddummynotes] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.8.3: Dummy Notes (Orchard) <protocol/protocol.pdf#orcharddummynotes>`_\n-.. [#protocol-orchardbalance] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.14: Balance and Binding Signature (Orchard) <protocol/protocol.pdf#orchardbalance>`_\n-.. [#protocol-rhoandnullifiers] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.16: Computing ρ values and Nullifiers <protocol/protocol.pdf#rhoandnullifiers>`_\n-.. [#protocol-actionstatement] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.18.4: Action Statement (Orchard) <protocol/protocol.pdf#actionstatement>`_\n-.. [#protocol-endian] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.1: Integers, Bit Sequences, and Endianness <protocol/protocol.pdf#endian>`_\n-.. [#protocol-constants] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.3: Constants <protocol/protocol.pdf#constants>`_\n-.. [#protocol-concretesinsemillahash] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.4.1.9: Sinsemilla hash function <protocol/protocol.pdf#concretesinsemillahash>`_\n-.. [#protocol-concretehomomorphiccommit] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.4.8.3: Homomorphic Pedersen commitments (Sapling and Orchard) <protocol/protocol.pdf#concretehomomorphiccommit>`_\n-.. [#protocol-concretesinsemillacommit] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.4.8.4: Sinsemilla commitments <protocol/protocol.pdf#concretesinsemillacommit>`_\n-.. [#protocol-pallasandvesta] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.4.9.6: Pallas and Vesta <protocol/protocol.pdf#pallasandvesta>`_\n-.. [#protocol-notept] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.5: Encodings of Note Plaintexts and Memo Fields <protocol/protocol.pdf#notept>`_\n-.. [#protocol-actionencodingandconsensus] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 7.5: Action Description Encoding and Consensus <protocol/protocol.pdf#actionencodingandconsensus>`_\n+.. [#zip-0229] `ZIP 229: Version 6 Transaction Format <zip-0229.md>`_\n+.. [#zip-0230] `ZIP 230: Withdrawn Version 6 Transaction Format <zip-0230.rst>`_\n+.. [#zip-0230-orchard-note-plaintext] `ZIP 230: Withdrawn Version 6 Transaction Format — Orchard Note Plaintext <zip-0230.rst#orchard-note-plaintext>`_\n+.. [#zip-0248] `ZIP 248: Extensible Transaction Format <zip-0248.rst>`_\n+.. [#zip-0248-digest-algorithms] `ZIP 248: Extensible Transaction Format — Digest Algorithms <zip-0248.rst#digest-algorithms>`_\n+.. [#zip-0248-sighash-versioning] `ZIP 248: Extensible Transaction Format — Sighash Versioning <zip-0248.rst#sighash-versioning>`_\n+.. [#zip-0248-implications-for-wallets] `ZIP 248: Extensible Transaction Format — Implications for Wallets <zip-0248.rst#implications-for-wallets>`_\n+.. [#zip-2005] `ZIP 2005: Ironwood Quantum Recoverability <zip-2005.md>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3] or later. <protocol/protocol.pdf>`_\n+.. [#protocol-base] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3] <protocol/protocol.pdf#2026.8.0>`_\n+.. [#protocol-notes] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.2: Shielded Pools and Notes <protocol/protocol.pdf#notes>`_\n+.. [#protocol-actions] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.7: Action Transfers and their Descriptions <protocol/protocol.pdf#actions>`_\n+.. [#protocol-abstractprfs] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.1.2: Pseudo Random Functions <protocol/protocol.pdf#abstractprfs>`_\n+.. [#protocol-abstractcommit] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.1.8: Commitment <protocol/protocol.pdf#abstractcommit>`_\n+.. [#protocol-orchardsend] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.7.3: Sending Notes (Orchard) <protocol/protocol.pdf#orchardsend>`_\n+.. [#protocol-orcharddummynotes] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.8.3: Dummy Notes (Orchard) <protocol/protocol.pdf#orcharddummynotes>`_\n+.. [#protocol-orchardbalance] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.14: Balance and Binding Signature (Orchard) <protocol/protocol.pdf#orchardbalance>`_\n+.. [#protocol-rhoandnullifiers] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.16: Computing ρ values and Nullifiers <protocol/protocol.pdf#rhoandnullifiers>`_\n+.. [#protocol-actionstatement] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.18.4: Action Statement (Orchard) <protocol/protocol.pdf#actionstatement>`_\n+.. [#protocol-endian] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.1: Integers, Bit Sequences, and Endianness <protocol/protocol.pdf#endian>`_\n+.. [#protocol-constants] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.3: Constants <protocol/protocol.pdf#constants>`_\n+.. [#protocol-concretesinsemillahash] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.1.9: Sinsemilla Hash Function <protocol/protocol.pdf#concretesinsemillahash>`_\n+.. [#protocol-concretehomomorphiccommit] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.8.3: Homomorphic Pedersen commitments (Sapling and Orchard) <protocol/protocol.pdf#concretehomomorphiccommit>`_\n+.. [#protocol-concretesinsemillacommit] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.8.4: Sinsemilla commitments <protocol/protocol.pdf#concretesinsemillacommit>`_\n+.. [#protocol-pallasandvesta] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.9.6: Pallas and Vesta <protocol/protocol.pdf#pallasandvesta>`_\n+.. [#protocol-notept] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.5: Encodings of Note Plaintexts and Memo Fields <protocol/protocol.pdf#notept>`_\n+.. [#protocol-actionencodingandconsensus] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.5: Action Description Encoding and Consensus <protocol/protocol.pdf#actionencodingandconsensus>`_\n .. [#initial-zsa-issue] `User-Defined Assets and Wrapped Assets <https://github.com/str4d/zips/blob/zip-udas/drafts/zip-user-defined-assets.rst>`_\n .. [#generalized-value-commitments] `Comment on Generalized Value Commitments <https://github.com/zcash/zcash/issues/2277#issuecomment-321106819>`_\n .. [#circuit-modifications] `Modifications to the Orchard circuit for the OrchardZSA Protocol <https://docs.google.com/document/d/1DzXBqZl_l3aIs_gcelw3OuZz2OVMnYk6Xe_1lBsTji8/edit?usp=sharing>`_"},{"filename":"zips/zip-0227.rst","status":"modified","additions":44,"deletions":30,"patch":"@@ -325,9 +325,11 @@ An Issue Note is a tuple $(\\mathsf{d}, \\mathsf{pk_d}, \\mathsf{v}, \\mathsf{AssetB\n - $\\text{ρ}: \\mathbb{F}_{q_{\\mathbb{P}}}$ is used to derive the nullifier of the note, and is computed as in `Computation of ρ`_.\n - $\\mathsf{rseed}: \\mathbb{B}^{[\\mathbb{Y}^{32}]}$ MUST be sampled uniformly at random by the issuer.\n \n-ZIP 230 [#zip-0230-issue-note]_ defines, in ``IssueNoteDescription``, field encodings which together with\n-$\\mathsf{issuer}$ from the parent `Issuance Bundle`_ and $\\mathsf{AssetDescHash}$ from the parent\n-`Issuance Action`_, specify an Issue Note.\n+**Open Issue:** The encodings of Issue Notes, issuance actions, and the issuance bundle in the\n+transaction format of ZIP 248 [#zip-0248]_ are yet to be specified. In the withdrawn ZIP 230 design,\n+an Issue Note is specified by the field encodings in ``IssueNoteDescription``\n+[#zip-0230-issue-note]_, together with $\\mathsf{issuer}$ from the parent `Issuance Bundle`_ and\n+$\\mathsf{AssetDescHash}$ from the parent `Issuance Action`_.\n \n Let $\\mathsf{Note^{Issue}}$ be the type of an Issue Note, i.e.\n \n@@ -352,7 +354,7 @@ An issuance action, ``IssueAction``, is the instance of issuing a specific Custo\n The $\\mathsf{finalize}$ boolean is set by the Issuer to signal that there will be no further issuance of the specific Custom Asset.\n As we will see in `Specification: Consensus Rule Changes`_, transactions that attempt to issue further amounts of a Custom Asset that has previously been finalized will be rejected.\n \n-The complete encoding of these fields into an ``IssueAction`` is defined in ZIP 230 [#zip-0230-issuance-action-description]_.\n+**Open Issue:** The complete encoding of these fields into an ``IssueAction`` is yet to be specified, as noted in `Issue Note`_.\n \n \n Issuance Bundle\n@@ -366,8 +368,9 @@ It contains the following fields:\n - ``vIssueActions``: an array of issuance actions, of type ``IssueAction``.\n - ``issueAuthSig``: the encoding of a signature of the transaction SIGHASH, signed by the issuance authorizing key, $\\mathsf{isk}$, that validates the issuance.\n \n-The issuance bundle is added within the transaction format as a new bundle. \n-The detailed encoding of the issuance bundle as a part of the V6 transaction format is defined in ZIP 230 [#zip-0230-transaction-format]_.\n+The issuance bundle is added within the transaction format as a new bundle.\n+\n+**Open Issue:** ZIP 248 [#zip-0248]_ lists ZSA issuance among its potential future bundle kinds. The encoding of the issuance bundle is yet to be specified, as noted in `Issue Note`_.\n \n Computation of ρ\n ----------------\n@@ -391,6 +394,8 @@ where $\\mathsf{nf}_{0,0}$ is the nullifier for the input note in the first Actio\n **NOTE:** This implicitly requires that there always is an Action Group in the OrchardZSA bundle of the transaction.\n This is enforced by a consensus rule in the `Specification: Consensus Rule Changes`_ section.\n \n+**Open Issue:** Action Groups were part of the withdrawn ZIP 230 design. Supporting them would require a modification to the transaction format of ZIP 248 [#zip-0248]_, which is possible independently of whether ZSAs activate at the same time.\n+\n Issuance Protocol\n -----------------\n The issuer program performs the following operations:\n@@ -471,7 +476,7 @@ We use $\\mathsf{issued\\_assets}_{\\mathsf{IN}}$ and $\\mathsf{issued\\_assets}_{\\ma\n Specification: Consensus Rule Changes\n =====================================\n \n-Let $\\mathsf{SigHash}$ be the SIGHASH transaction hash of this transaction, as defined in §4.10 ‘SIGHASH Transaction Hashing’ [#protocol-sighash]_ with the modifications described in ZIP 226 [#zip-0226-txiddigest]_, using $\\mathsf{SIGHASH\\_ALL}$.\n+Let $\\mathsf{SigHash}$ be the SIGHASH transaction hash of this transaction, as defined in §4.10 ‘SIGHASH Transaction Hashing’ [#protocol-sighash]_ with the modifications described in `Sighash`_, using $\\mathsf{SIGHASH\\_ALL}$.\n \n For every transaction:\n \n@@ -503,6 +508,8 @@ If the transaction contains an issuance bundle:\n     - The node MUST compute the note commitment, $\\mathsf{cm}_{\\mathsf{i,j}}$, as defined in the Note Structure and Commitment section of ZIP 226 [#zip-0226-note-structure-and-commitment]_.\n   - If $\\mathsf{finalize} = 1$ within the ``flagsIssuance`` field of ``IssueAction``, the node MUST set $\\mathsf{issued\\_assets}_{\\mathsf{OUT}}(\\mathsf{AssetBase}).\\mathsf{final} = 1$.\n \n+**Open Issue:** These rules refer to the ``nActionGroupsOrchard`` and ``nAGExpiryHeight`` fields and to OrchardZSA Action Groups, which the transaction format of ZIP 248 does not support, as noted in `Computation of ρ`_. They also refer to the encoding of ``IssueNoteDescription``, which is yet to be specified, as noted in `Issue Note`_.\n+\n Addition to the Note Commitment Tree\n ------------------------------------\n \n@@ -517,6 +524,8 @@ The order of addition to the tree is as specified below:\n \n   - For every Issue Note in the Issue Action, append the note commitment of the Issue Note to the note commitment tree.\n \n+**Open Issue:** This order is defined in terms of OrchardZSA Action Groups, which the transaction format of ZIP 248 does not support, as noted in `Computation of ρ`_.\n+\n Rationale\n =========\n The following is a list of rationale for different decisions made in the proposal:\n@@ -525,6 +534,8 @@ The following is a list of rationale for different decisions made in the proposa\n - The Custom Asset is described via a combination of the issuer identifier and an asset description string, to preclude the possibility of two different issuers creating colliding Custom Assets.\n - The requirement of at least one OrchardZSA Action Group in the presence of an issue bundle is both to allow for the computation of the $\\text{ρ}$ field of the issue notes, as well as to prevent replay attacks. If a transaction includes only an issue bundle, the SIGHASH transaction hash would be computed solely based on the issue bundle. A duplicate bundle would have the same SIGHASH transaction hash, potentially allowing for a replay attack.\n \n+  **Open Issue:** The transaction format of ZIP 248 does not support Action Groups, as noted in `Computation of ρ`_.\n+\n Hash of the asset description\n -----------------------------\n \n@@ -593,12 +604,19 @@ Concrete Applications\n \n \n \n-Modifications relative to ZIP 244 [#zip-0244]_\n-==============================================\n+Sighash\n+=======\n+\n+ZIP 248 specifies the digest algorithms for the transaction version that it\n+defines [#zip-0248-digest-algorithms]_, and a mechanism for versioning sighash\n+algorithms [#zip-0248-sighash-versioning]_. Each bundle type defines its\n+contributions to those digests, and the sighash versions available to its\n+signers together with the associated data that each version requires.\n \n-Relative to the sighash algorithm defined in ZIP 244, the sighash algorithm\n-that applies to v6 transactions differs by including the issuance bundle\n-components within the tree hash. See ZIP 246 [#zip-0246]_ for details.\n+**Open Issue:** The digests of the issuance bundle are yet to be specified. So is\n+the sighash version that the issuance authorization signature uses, in place of\n+the $\\mathsf{SIGHASH\\_ALL}$ hash type referred to in\n+`Specification: Consensus Rule Changes`_.\n \n \n Changes to ZIP 317 [#zip-0317]_\n@@ -690,26 +708,22 @@ References\n .. [#zip-0226] `ZIP 226: Transfer and Burn of Zcash Shielded Assets <zip-0226.rst>`_\n .. [#zip-0226-note-structure-and-commitment] `ZIP 226: Transfer and Burn of Zcash Shielded Assets — Note Structure and Commitment <zip-0226.rst#note-structure-and-commitment>`_\n .. [#zip-0226-assetburn] `ZIP 226: Transfer and Burn of Zcash Shielded Assets — Additional Consensus Rules for the assetBurn set <zip-0226.rst#additional-consensus-rules-for-the-assetburn-set>`_\n-.. [#zip-0226-txiddigest] `ZIP 226: Transfer and Burn of Zcash Shielded Assets — TxId Digest <zip-0226.rst#txid-digest>`_\n-.. [#zip-0226-authcommitment] `ZIP 226: Transfer and Burn of Zcash Shielded Assets — Authorizing Data Commitment <zip-0226.rst#authorizing-data-commitment>`_\n-.. [#zip-0230-issuance-action-description] `ZIP 230: Version 6 Transaction Format — Issuance Action Description (IssueAction) <zip-0230.rst#issuance-action-description-issueaction>`_\n-.. [#zip-0230-issue-note] `ZIP 230: Version 6 Transaction Format — Issue Note Description (IssueNoteDescription) <zip-0230.rst#issue-note-description-issuenotedescription>`_\n-.. [#zip-0230-transaction-format] `ZIP 230: Version 6 Transaction Format — Transaction Format <zip-0230.rst#transaction-format>`_\n-.. [#zip-0244] `ZIP 244: Transaction Identifier Non-Malleability <zip-0244.rst>`_\n-.. [#zip-0246] `ZIP 246: Digests for the Version 6 Transaction Format <zip-0246.rst>`_\n+.. [#zip-0230-issue-note] `ZIP 230: Withdrawn Version 6 Transaction Format — Issue Note Description (IssueNoteDescription) <zip-0230.rst#issue-note-description-issuenotedescription>`_\n+.. [#zip-0230-transaction-format] `ZIP 230: Withdrawn Version 6 Transaction Format — Transaction Format <zip-0230.rst#transaction-format>`_\n+.. [#zip-0248] `ZIP 248: Extensible Transaction Format <zip-0248.rst>`_\n+.. [#zip-0248-digest-algorithms] `ZIP 248: Extensible Transaction Format — Digest Algorithms <zip-0248.rst#digest-algorithms>`_\n+.. [#zip-0248-sighash-versioning] `ZIP 248: Extensible Transaction Format — Sighash Versioning <zip-0248.rst#sighash-versioning>`_\n .. [#zip-0317] `ZIP 317: Proportional Transfer Fee Mechanism <zip-0317.rst>`_\n .. [#zip-0317-fee-calculation] `ZIP 317: Proportional Transfer Fee Mechanism — Fee calculation <zip-0317.rst#fee-calculation>`_\n .. [#bip-0043] `BIP 43: Purpose Field for Deterministic Wallets <https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki>`_\n .. [#bip-0340] `BIP 340: Schnorr Signatures for secp256k1 <https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki>`_\n-.. [#protocol] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1] or later. <protocol/protocol.pdf>`_\n-.. [#protocol-notation] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 2: Notation <protocol/protocol.pdf#notation>`_\n-.. [#protocol-notes] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 3.2: Notes <protocol/protocol.pdf#notes>`_\n-.. [#protocol-abstractsig] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.1.7: Signature <protocol/protocol.pdf#abstractsig>`_\n-.. [#protocol-orchardkeycomponents] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.2.3: Orchard Key Components <protocol/protocol.pdf#orchardkeycomponents>`_\n-.. [#protocol-sighash] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.10: SIGHASH Transaction Hashing <protocol/protocol.pdf#sighash>`_\n-.. [#protocol-constants] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.3: Constants <protocol/protocol.pdf#constants>`_\n-.. [#protocol-concreteprfs] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.4.2: Pseudo Random Functions <protocol/protocol.pdf#concreteprfs>`_\n-.. [#protocol-concretegrouphashpallasandvesta] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.4.9.8: Group Hash into Pallas and Vesta <protocol/protocol.pdf#concretegrouphashpallasandvesta>`_\n-.. [#protocol-orchardpaymentaddrencoding] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.6.4.2: Orchard Raw Payment Addresses <protocol/protocol.pdf#orchardpaymentaddrencoding>`_\n-.. [#protocol-txnencoding] `Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 7.1: Transaction Encoding and Consensus <protocol/protocol.pdf#txnencoding>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3] or later. <protocol/protocol.pdf>`_\n+.. [#protocol-notation] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 2: Notation <protocol/protocol.pdf#notation>`_\n+.. [#protocol-notes] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.2: Shielded Pools and Notes <protocol/protocol.pdf#notes>`_\n+.. [#protocol-abstractsig] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.1.7: Signature <protocol/protocol.pdf#abstractsig>`_\n+.. [#protocol-orchardkeycomponents] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.2.3: Orchard Key Components <protocol/protocol.pdf#orchardkeycomponents>`_\n+.. [#protocol-sighash] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.10: SIGHASH Transaction Hashing <protocol/protocol.pdf#sighash>`_\n+.. [#protocol-constants] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.3: Constants <protocol/protocol.pdf#constants>`_\n+.. [#protocol-concreteprfs] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.2: Pseudo Random Functions <protocol/protocol.pdf#concreteprfs>`_\n+.. [#protocol-concretegrouphashpallasandvesta] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.9.8: Group Hash into Pallas and Vesta <protocol/protocol.pdf#concretegrouphashpallasandvesta>`_\n .. [#petnames] `An Introduction to Petname Systems. Marc Stiegler, updated June 2010. <HTTP://www.skyhunter.com/marcs/petnames/IntroPetNames.html>`_"},{"filename":"zips/zip-0228.rst","status":"modified","additions":25,"deletions":14,"patch":"@@ -40,9 +40,11 @@ We define the following additional terms:\n Abstract\n ========\n \n-This ZIP introduces Asset Swaps for Zcash Shielded Assets (ZSA). It builds on top of ZIP 227 [#zip-0227]_ and ZIP 226 [#zip-0226]_, and uses the transaction format encoding described in ZIP 230 [#zip-0230]_. \n+This ZIP introduces Asset Swaps for Zcash Shielded Assets (ZSA). It builds on top of ZIP 227 [#zip-0227]_ and ZIP 226 [#zip-0226]_.\n ZSA Swaps simplify the process of trading assets within the Zcash ecosystem, and lower the reliance on centralized exchanges for trading.\n \n+**Open Issue:** This ZIP uses the transaction format encoding of the withdrawn ZIP 230 design [#zip-0230]_, including the encoding of Action Groups. Supporting Action Groups would require a modification to the transaction format of ZIP 248 [#zip-0248]_, which is possible independently of whether ZSAs activate at the same time.\n+\n Motivation\n ==========\n \n@@ -83,9 +85,11 @@ The specification of the structure of Swap Orders (that are sent off-chain) is p\n v1 Signature Digest\n -------------------\n \n-ZIP 246 [#zip-0246]_ introduces versioning for SIGHASH algorithms.\n+ZIP 248 [#zip-0248-sighash-versioning]_ specifies versioning for SIGHASH algorithms, in which each bundle type defines the sighash versions available to its signers.\n We define the **v1 Signature Digest** for an Action Group as the output of the ``orchard_action_groups_digest`` for that Action Group.\n \n+**Open Issue:** ``orchard_action_groups_digest`` was a digest of the withdrawn ZIP 230 design. The v1 Signature Digest is yet to be specified as a sighash version of the OrchardZSA bundle, together with the associated data that it requires [#zip-0248-sighash-versioning]_.\n+\n Spend Authorization Signature Changes\n -------------------------------------\n \n@@ -94,7 +98,9 @@ The spend authorization signature is computed in the manner specified in Section\n Binding Signature Changes\n -------------------------\n \n-The binding signature is generated using the Orchard Binding Signature scheme, by signing the v0 Signature Digest as defined in ZIP 246 [#zip-0246]_, using the signing key $\\mathsf{bsk}$ computed as described below.\n+The binding signature is generated using the Orchard Binding Signature scheme, by signing the v0 Signature Digest as defined in ZIP 248 [#zip-0248-v0-signature-digest]_, using the signing key $\\mathsf{bsk}$ computed as described below.\n+\n+**Open Issue:** The v0 Signature Digest of ZIP 248 does not cover Action Groups, which its transaction format does not support, as noted in `Abstract`_.\n \n The party performing the matching has some Swap Orders $\\mathsf{SO}_i$, each of which contains a $\\mathsf{bsk}_i$.\n The party generates the $\\mathsf{bsk}$ for the binding signature by summing up the $\\mathsf{bsk}_i$ values of the Swap Orders that are matched into the Swap Bundle.\n@@ -105,12 +111,14 @@ The binding signature is verified against the value commitments of all Actions i\n Swap Bundle \n -----------\n \n-The V6 transaction format [#zip-0230-transaction-format]_ groups Actions [#protocol-actions]_ into **Action Groups** characterized by anchors, ``flagsOrchard`` and ``nAGExpiryHeight``. \n+The transaction format of the withdrawn ZIP 230 design [#zip-0230-transaction-format]_ groups Actions [#protocol-actions]_ into **Action Groups** characterized by anchors, ``flagsOrchard`` and ``nAGExpiryHeight``. \n We continue to use the same transaction format. \n The consensus rules are updated to remove the requirement that there be only one Action Group per transaction.\n The Spend Authorization Signatures for Action Group in the Swap Bundle signs a `v1 Signature Digest`_, as specified above.\n A Swap Bundle then consists of two or more Action Groups, as illustrated in the figure below.\n \n+**Open Issue:** The transaction format of ZIP 248 does not support Action Groups, as noted in `Abstract`_.\n+\n \n .. figure:: ../rendered/assets/images/zip-0228-swap-bundle.png\n     :width: 1000px\n@@ -136,6 +144,8 @@ Specifically, the consensus rule becomes (if any checks fail, the transaction MU\n             - check the result of ZKAction.Verify on the proof $\\mathsf{AG}.\\mathtt{proofsOrchard}[i]$\n             - check $\\mathsf{SpendAuthSig.Validate}(\\mathsf{AG}.\\mathtt{vActionsOrchard}[i].\\mathsf{rk}, \\mathtt{v1\\_sig\\_digest}, \\mathsf{AG}.\\mathtt{vSpendAuthSigsOrchard}[i])$\n \n+**Open Issue:** These rules use the ``vActionGroupsOrchard`` and ``nAGExpiryHeight`` fields of Action Groups, which the transaction format of ZIP 248 does not support, as noted in `Abstract`_. The v1 Signature Digest that they use is yet to be specified, as noted in `v1 Signature Digest`_.\n+\n Fees\n ----\n \n@@ -200,7 +210,7 @@ Rationale for v1 Signature Digests and Spend Authorization Signature Changes\n In the OrchardZSA protocol, each Action includes a Spend Authorization Signature [#protocol-spendauthsig]_ that binds a specific spend instruction to a specific transaction and prevents replay attacks.\n However, in the context of Asset Swaps, there are two problems that prevent this mechanism from being carried over unchanged.\n \n-- The SIGHASH represents a hash over a full consensus-compliant transaction object. Since a Swap Order is a trade intent to be used by the party performing the matching to form a valid bundle/transaction, the sender of the Swap Order cannot compute a v0 SIGHASH as per ZIP 246. This trader doesn't know all the transaction fields in advance, as they are set when the full bundle transaction is formed and sent to the chain.\n+- The SIGHASH represents a hash over a full consensus-compliant transaction object. Since a Swap Order is a trade intent to be used by the party performing the matching to form a valid bundle/transaction, the sender of the Swap Order cannot compute a v0 SIGHASH as per ZIP 248. This trader doesn't know all the transaction fields in advance, as they are set when the full bundle transaction is formed and sent to the chain.\n \n To make sure the time limit isn't malleable, the sender signs the information in the Action Group of the Order. \n The ``orchard_action_groups_digest`` contains all the information that is in a Swap Order, which is why we use this for the v1 Signature Digest. \n@@ -248,6 +258,8 @@ We specify here the structure of a Swap Order. Note that this is not a change to\n |                             |                          |                                           |Action and the Fee Action.                                           |  \n +-----------------------------+--------------------------+-------------------------------------------+---------------------------------------------------------------------+\n \n+**Open Issue:** ``ActionGroupDescription`` is the Action Group encoding of the withdrawn ZIP 230 design, which the transaction format of ZIP 248 does not support, as noted in `Abstract`_.\n+\n The ``SwapIO`` data type is used to represent the input and output of a Swap Order.\n \n +-----------------------------+--------------------------+-------------------------------------------+---------------------------------------------------------------------+\n@@ -432,15 +444,14 @@ References\n .. [#zip-0200] `ZIP 200: Network Upgrade Mechanism <zip-0200.html>`_\n .. [#zip-0224] `ZIP 224: Orchard <zip-0224.html>`_\n .. [#zip-0226] `ZIP 226: Transfer and Burn of Zcash Shielded Assets <zip-0226.html>`_\n-.. [#zip-0226-circuit-statement] `ZIP 226: Transfer and Burn of Zcash Shielded Assets - Circuit Statement <zip-0226.html#circuit-statement>`_\n .. [#zip-0227] `ZIP 227: Issuance of Zcash Shielded Assets <zip-0227.html>`_\n .. [#zip-0227-assetbase] `ZIP 227: Issuance of Zcash Shielded Assets - Specification: Asset Identifier <zip-0227.html#specification-asset-identifier>`_\n-.. [#zip-0230] `ZIP 230: Version 6 Transaction Format <zip-0230.html>`_\n-.. [#zip-0230-transaction-format] `ZIP 230: Version 6 Transaction Format - Transaction Format <zip-0230.html#transaction-format>`_\n-.. [#zip-0244-orchard-digest] `ZIP 244: Transaction Identifier Non-Malleability: T4. orchard_digest <zip-0244.html#t-4-orchard-digest>`_\n-.. [#zip-0246] `ZIP 246: Digests for the Version 6 Transaction Format <zip-0246.html>`_\n-.. [#protocol-notes] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. Section 3.2: Notes <protocol/protocol.pdf#notes>`_\n-.. [#protocol-actions] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. Section 3.7: Action Transfers and their Descriptions <protocol/protocol.pdf#actions>`_\n-.. [#protocol-spendauthsig] `Zcash Protocol Specification, Version 2024.5.1. Section 4.15: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#spendauthsig>`_\n-.. [#protocol-txnencoding] `Zcash Protocol Specification, Version 2024.5.1. Section 7.1: Transaction Encoding and Consensus <protocol/protocol.pdf#txnencoding>`_\n+.. [#zip-0230] `ZIP 230: Withdrawn Version 6 Transaction Format <zip-0230.html>`_\n+.. [#zip-0230-transaction-format] `ZIP 230: Withdrawn Version 6 Transaction Format - Transaction Format <zip-0230.html#transaction-format>`_\n+.. [#zip-0248] `ZIP 248: Extensible Transaction Format <zip-0248.html>`_\n+.. [#zip-0248-sighash-versioning] `ZIP 248: Extensible Transaction Format - Sighash Versioning <zip-0248.html#sighash-versioning>`_\n+.. [#zip-0248-v0-signature-digest] `ZIP 248: Extensible Transaction Format - v0 Signature Digest <zip-0248.html#v0-signature-digest>`_\n+.. [#protocol-notes] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.2: Shielded Pools and Notes <protocol/protocol.pdf#notes>`_\n+.. [#protocol-actions] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.7: Action Transfers and their Descriptions <protocol/protocol.pdf#actions>`_\n+.. [#protocol-spendauthsig] `Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.15: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#spendauthsig>`_\n .. [#clob] `Central Limit Order Book Definition - Risk.net <https://www.risk.net/definition/central-limit-order-book-clob>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0226.rst","title":"Transfer and Burn of Zcash Shielded Assets","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"overview","text":"Overview"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"asset-identifiers","text":"Asset Identifiers"},{"level":3,"id":"rationale-for-asset-identifiers","text":"Rationale for Asset Identifiers"},{"level":2,"id":"note-structure-and-commitment","text":"Note Structure and Commitment"},{"level":3,"id":"rationale-for-note-commitment","text":"Rationale for Note Commitment"},{"level":2,"id":"value-commitment","text":"Value Commitment"},{"level":3,"id":"rationale-for-value-commitment","text":"Rationale for Value Commitment"},{"level":2,"id":"burn-mechanism","text":"Burn Mechanism"},{"level":3,"id":"additional-consensus-rules-for-the-assetburn-set","text":"Additional Consensus Rules for the assetBurn set"},{"level":3,"id":"rationale-for-max-burn-value","text":"Rationale for MAX_BURN_VALUE"},{"level":2,"id":"value-balance-verification","text":"Value Balance Verification"},{"level":3,"id":"rationale-for-value-balance-verification","text":"Rationale for Value Balance Verification"},{"level":2,"id":"split-notes","text":"Split Notes"},{"level":3,"id":"rationale-for-split-notes","text":"Rationale for Split Notes"},{"level":2,"id":"circuit-statement","text":"Circuit Statement"},{"level":3,"id":"asset-base-equality","text":"Asset Base Equality"},{"level":3,"id":"the-mathsf-enablezsa-flag","text":"The 𝖾 𝗇 𝖺 𝖻 𝗅 𝖾 𝖹 𝖲 𝖠 Flag"},{"level":3,"id":"value-commitment-correctness","text":"Value Commitment Correctness"},{"level":3,"id":"asset-identifier-consistency-for-split-actions","text":"Asset Identifier Consistency for Split Actions"},{"level":3,"id":"backwards-compatibility-with-zec-notes","text":"Backwards Compatibility with ZEC Notes"},{"level":2,"id":"orchardzsa-transaction-structure","text":"OrchardZSA Transaction Structure"},{"level":3,"id":"implications-for-wallets","text":"Implications for Wallets"},{"level":2,"id":"sighash","text":"Sighash"},{"level":2,"id":"transaction-fees","text":"Transaction Fees"},{"level":2,"id":"backward-compatibility","text":"Backward Compatibility"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"test-vectors","text":"Test Vectors"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1410,"title":"ZIP 221: Add the Ironwood fields that ZIP 258 specifies","author":"daira","createdAt":"2026-10-21T17:01:14Z","updatedAt":"2026-10-21T17:07:46Z","url":"https://github.com/zcash/zips/pull/1410","diffUrl":"https://github.com/zcash/zips/pull/1410.diff","headRef":"zip-221-ironwood","headRepo":"daira/zips","headSha":"19db1310bdb707ae82dd0d74c45b8059f887995c","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"ZIP 258, which deploys NU6.3, specifies changes to ZIP 221 that add Ironwood counterparts of the Orchard history tree node fields. They had not been applied: ZIP 221 did not mention Ironwood. This PR applies them. - **Pseudocode slips** — the node structure declared `nSaplingTxCount` a second time w","zipRefs":[221,258],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0221.rst","status":"modified","additions":81,"deletions":22,"patch":"@@ -362,13 +362,47 @@ Each MMR node is defined as follows:\n \n     Serialized as ``CompactSize uint``.\n \n+15. [NU6.3 onward] ``hashEarliestIronwoodRoot``\n \n-The fields marked \"[NU5 onward]\" are omitted before NU5 activation [#zip-0252]_.\n+    Leaf node\n+      Calculated as the note commitment root of the final Ironwood treestate\n+      (similar to ``hashEarliestOrchardRoot``).\n+\n+    Internal or root node\n+      Inherited from the left child.\n+\n+    Serialized as ``char[32]``.\n+\n+16. [NU6.3 onward] ``hashLatestIronwoodRoot``\n+\n+    Leaf node\n+      Calculated as the note commitment root of the final Ironwood treestate\n+      (similar to ``hashLatestOrchardRoot``).\n+\n+    Internal or root node\n+      Inherited from the right child.\n+\n+    Serialized as ``char[32]``.\n+\n+17. [NU6.3 onward] ``nIronwoodTxCount``\n+\n+    Leaf node\n+      The number of transactions in the leaf block where ``vActionsIronwood``\n+      is non-empty.\n+\n+    Internal or root node\n+      The sum of the ``nIronwoodTxCount`` field of both children.\n+\n+    Serialized as ``CompactSize uint``.\n+\n+\n+The fields marked \"[NU5 onward]\" are omitted before NU5 activation [#zip-0252]_. The\n+fields marked \"[NU6.3 onward]\" are omitted before NU6.3 activation.\n \n Each node, when serialized, is between 147 and 171 bytes long (between 212 and 244 bytes\n-after NU5 activation). The canonical serialized representation of a node is used whenever\n-creating child commitments for future nodes. Other than the metadata commitments, the\n-MMR tree's construction is standard.\n+after NU5 activation, and between 277 and 317 bytes after NU6.3 activation). The canonical\n+serialized representation of a node is used whenever creating child commitments for future\n+nodes. Other than the metadata commitments, the MMR tree's construction is standard.\n \n Once the MMR has been generated, we produce ``hashChainHistoryRoot``, which we define as\n the BLAKE2b-256 digest of the serialization of the root node.\n@@ -402,7 +436,11 @@ Tree nodes and hashing (pseudocode)\n         # NU5 only.\n         hashEarliestOrchardRoot: Optional[bytes] # left child's Orchard root\n         hashLatestOrchardRoot: Optional[bytes] # right child's Orchard root\n-        nSaplingTxCount: Optional[int] # number of Orchard transactions in block\n+        nOrchardTxCount: Optional[int] # number of Orchard transactions in block\n+        # NU6.3 only.\n+        hashEarliestIronwoodRoot: Optional[bytes] # left child's Ironwood root\n+        hashLatestIronwoodRoot: Optional[bytes] # right child's Ironwood root\n+        nIronwoodTxCount: Optional[int] # number of Ironwood transactions in block\n \n         consensusBranchId: bytes\n \n@@ -426,6 +464,9 @@ Tree nodes and hashing (pseudocode)\n                 hashEarliestOrchardRoot=block.orchard_root,\n                 hashLatestOrchardRoot=block.orchard_root,\n                 nOrchardTxCount=block.orchard_tx_count,\n+                hashEarliestIronwoodRoot=block.ironwood_root,\n+                hashLatestIronwoodRoot=block.ironwood_root,\n+                nIronwoodTxCount=block.ironwood_tx_count,\n                 consensusBranchId=block.consensusBranchId)\n \n         def serialize(self) -> bytes:\n@@ -435,16 +476,20 @@ Tree nodes and hashing (pseudocode)\n                 + serialize_uint32(self.nLatestTimestamp)\n                 + serialize_uint32(self.nEarliestTargetBits)\n                 + serialize_uint32(self.nLatestTargetBits)\n-                + hashEarliestSaplingRoot\n-                + hashLatestSaplingRoot\n+                + self.hashEarliestSaplingRoot\n+                + self.hashLatestSaplingRoot\n                 + serialize_uint256(self.nSubTreeTotalWork)\n                 + serialize_compact_uint(self.nEarliestHeight)\n                 + serialize_compact_uint(self.nLatestHeight)\n                 + serialize_compact_uint(self.nSaplingTxCount))\n-            if hashEarliestOrchardRoot is not None:\n-                buf += (hashEarliestOrchardRoot\n-                    + hashLatestOrchardRoot\n+            if self.hashEarliestOrchardRoot is not None:\n+                buf += (self.hashEarliestOrchardRoot\n+                    + self.hashLatestOrchardRoot\n                     + serialize_compact_uint(self.nOrchardTxCount))\n+            if self.hashEarliestIronwoodRoot is not None:\n+                buf += (self.hashEarliestIronwoodRoot\n+                    + self.hashLatestIronwoodRoot\n+                    + serialize_compact_uint(self.nIronwoodTxCount))\n             return buf\n \n \n@@ -460,17 +505,22 @@ Tree nodes and hashing (pseudocode)\n             nLatestTimestamp=right_child.nLatestTimestamp,\n             nEarliestTargetBits=left_child.nEarliestTargetBits,\n             nLatestTargetBits=right_child.nLatestTargetBits,\n-            hashEarliestSaplingRoot=left_child.sapling_root,\n-            hashLatestSaplingRoot=right_child.sapling_root,\n+            hashEarliestSaplingRoot=left_child.hashEarliestSaplingRoot,\n+            hashLatestSaplingRoot=right_child.hashLatestSaplingRoot,\n             nSubTreeTotalWork=left_child.nSubTreeTotalWork + right_child.nSubTreeTotalWork,\n             nEarliestHeight=left_child.nEarliestHeight,\n             nLatestHeight=right_child.nLatestHeight,\n             nSaplingTxCount=left_child.nSaplingTxCount + right_child.nSaplingTxCount,\n-            hashEarliestOrchardRoot=left_child.orchard_root,\n-            hashLatestOrchardRoot=right_child.orchard_root,\n+            hashEarliestOrchardRoot=left_child.hashEarliestOrchardRoot,\n+            hashLatestOrchardRoot=right_child.hashLatestOrchardRoot,\n             nOrchardTxCount=(left_child.nOrchardTxCount + right_child.nOrchardTxCount\n                              if left_child.nOrchardTxCount is not None and right_child.nOrchardTxCount is not None\n                              else None),\n+            hashEarliestIronwoodRoot=left_child.hashEarliestIronwoodRoot,\n+            hashLatestIronwoodRoot=right_child.hashLatestIronwoodRoot,\n+            nIronwoodTxCount=(left_child.nIronwoodTxCount + right_child.nIronwoodTxCount\n+                              if left_child.nIronwoodTxCount is not None and right_child.nIronwoodTxCount is not None\n+                              else None),\n             consensusBranchId=left_child.consensusBranchId)\n \n     def make_root_commitment(root: ZcashMMRNode) -> bytes:\n@@ -608,21 +658,22 @@ Tree nodes\n ----------\n \n Nodes in the commitment tree are canonical and immutable. They are cheap to generate, as\n-(with the exception of ``nSaplingTxCount`` and ``nOrchardTxCount``) all metadata is already\n-generated during block construction and/or checked during block validation. Nodes are\n-relatively compact in memory. As of the original publication of this ZIP, approximately\n-140,000 blocks had elapsed since Sapling activation. Assuming a 164-byte commitment to\n-each of these, we would have generated approximately 24 MB of additional storage cost for\n-the set of leaf nodes (and an additional ~24 MB for storage of intermediate nodes).\n+(with the exception of ``nSaplingTxCount``, ``nOrchardTxCount``, and ``nIronwoodTxCount``)\n+all metadata is already generated during block construction and/or checked during block\n+validation. Nodes are relatively compact in memory. As of the original publication of this\n+ZIP, approximately 140,000 blocks had elapsed since Sapling activation. Assuming a\n+164-byte commitment to each of these, we would have generated approximately 24 MB of\n+additional storage cost for the set of leaf nodes (and an additional ~24 MB for storage of\n+intermediate nodes).\n \n ``hashSubtreeCommitment`` forms the structure of the commitment tree. Other metadata\n commitments were chosen to serve specific purposes. Originally variable-length commitments\n were placed last, so that most metadata in a node could be directly indexed. We considered\n using fixed-length commitments here, but opted for variable-length, in order to marginally\n reduce the memory requirements for managing and updating the commitment trees.\n \n-Orchard fields are placed last, in order to avoid complicating existing uses of the other\n-fields.\n+Orchard fields are placed after the original fields, and Ironwood fields after the Orchard\n+fields, in order to avoid complicating existing uses of the other fields.\n \n In leaf nodes, some information is repeated. We chose to do this so that leaf nodes could\n be treated identically to internal and root nodes for all algorithms and (de)serializers.\n@@ -708,6 +759,10 @@ inclusion in a future upgrade.\n \n   * These are included with the same rationale as for Sapling.\n \n+- ``hashEarliestIronwoodRoot``, ``hashLatestIronwoodRoot``, and ``nIronwoodTxCount``\n+\n+  * These are included with the same rationale as for Sapling.\n+\n \n Header Format Change\n --------------------\n@@ -809,6 +864,9 @@ network upgrade. [#zip-0250]_\n Additional fields are added on activation of the NU5 network upgrade [#zip-0252]_,\n to support the new Orchard shielded protocol.\n \n+Further fields are added on activation of the NU6.3 network upgrade [#zip-0258]_,\n+to support the Ironwood pool.\n+\n \n Additional Reading\n ==================\n@@ -838,5 +896,6 @@ References\n .. [#zip-0244] `ZIP 244: Transaction Identifier Non-Malleability <zip-0244.rst>`_\n .. [#zip-0250] `ZIP 250: Deployment of the Heartwood Network Upgrade <zip-0250.rst>`_\n .. [#zip-0252] `ZIP 252: Deployment of the NU5 Network Upgrade <zip-0252.rst>`_\n+.. [#zip-0258] `ZIP 258: Deployment of the NU6.3 Network Upgrade <zip-0258.md>`_\n .. [#zip-0301] `ZIP 301: Zcash Stratum Protocol <https://github.com/zcash/zips/pull/78>`_\n .. [#zip-0307] `ZIP 307: Light Client Protocol for Payment Detection <https://github.com/zcash/zips/pull/226>`_"},{"filename":"zips/zip-0258.md","status":"modified","additions":24,"deletions":1,"patch":"@@ -223,11 +223,34 @@ with\n > Each node, when serialized, is between 147 and 171 bytes long (between 212 and 244 bytes\n > after NU5 activation, and between 277 and 317 bytes after NU6.3 activation). [...]\n \n-The pseudocode node structure and `serialize` / `make_leaf` / `make_parent` functions are\n+The pseudocode node structure and `from_block` / `serialize` / `make_parent` functions are\n extended with `hashEarliestIronwoodRoot`, `hashLatestIronwoodRoot`, and `nIronwoodTxCount`\n in the same way the \"# NU5 only\" Orchard fields were added (present iff NU6.3 has activated;\n the roots inherited from the left/right child and the count summed, in internal nodes).\n \n+In the Rationale, under \"Tree nodes\", replace \"(with the exception of `nSaplingTxCount` and\n+`nOrchardTxCount`)\" with \"(with the exception of `nSaplingTxCount`, `nOrchardTxCount`, and\n+`nIronwoodTxCount`)\", and replace\n+\n+> Orchard fields are placed last, in order to avoid complicating existing uses of the other\n+> fields.\n+\n+with\n+\n+> Orchard fields are placed after the original fields, and Ironwood fields after the Orchard\n+> fields, in order to avoid complicating existing uses of the other fields.\n+\n+Under \"Non-FlyClient Commitments\", after the entry for the Orchard fields, add\n+\n+> - `hashEarliestIronwoodRoot`, `hashLatestIronwoodRoot`, and `nIronwoodTxCount`\n+>\n+>     * These are included with the same rationale as for Sapling.\n+\n+In the Deployment section, add\n+\n+> Further fields are added on activation of the NU6.3 network upgrade [ZIP 258], to support\n+> the Ironwood pool.\n+\n `hashChainHistoryRoot` continues to be the BLAKE2b-256 digest of the serialized root node;\n its value changes at NU6.3 only through the added node fields. The `hashBlockCommitments`\n header field [^zip-0244] is unaffected beyond this."}],"filesTruncated":false,"preview":{"filename":"zips/zip-0221.rst","title":"FlyClient - Consensus-Layer Changes","status":"Final","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"background","text":"Background"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"tree-node-specification","text":"Tree Node specification"},{"level":2,"id":"tree-nodes-and-hashing-pseudocode","text":"Tree nodes and hashing (pseudocode)"},{"level":2,"id":"incremental-push-and-pop-pseudocode","text":"Incremental push and pop (pseudocode)"},{"level":2,"id":"block-header-semantics-and-consensus-rules","text":"Block header semantics and consensus rules"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"tree-nodes","text":"Tree nodes"},{"level":3,"id":"flyclient-requirements-and-recommendations","text":"FlyClient Requirements and Recommendations"},{"level":3,"id":"non-flyclient-commitments","text":"Non-FlyClient Commitments"},{"level":2,"id":"header-format-change","text":"Header Format Change"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"additional-reading","text":"Additional Reading"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1380,"title":"Draft ZIP: Direct Wallet Transaction Submission via Dandelion++ P2P Relay","author":"y4ssi","createdAt":"2026-09-29T21:10:25Z","updatedAt":"2026-09-29T21:18:26Z","url":"https://github.com/zcash/zips/pull/1380","diffUrl":"https://github.com/zcash/zips/pull/1380.diff","headRef":"draft-zodl-dandelion-wallet","headRepo":"y4ssi/zips","headSha":"04c8268f4318e2be10b69f6bacecc067cc44548e","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"## Summary Proposes the wallet-side counterpart to `draft-zodl-dandelion` (#1379): how a light-client wallet submits a transaction directly to a Zcash full node over P2P (under Dandelion++ stem-phase forwarding) instead of through a compact-block server. **Problem**: when a wallet calls `SendTransac","zipRefs":[],"draftRefs":["draft-zodl-dandelion-wallet"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-zodl-dandelion-wallet.md","status":"added","additions":281,"deletions":0,"patch":"@@ -0,0 +1,281 @@\n+    ZIP: Unassigned\n+    Title: Direct Wallet Transaction Submission via Dandelion++ P2P Relay\n+    Owners: y4ssi <y4ssi@zodl.com>\n+    Status: Draft\n+    Category: Standards / Network / Wallet\n+    Created: 2026-07-09\n+    License: MIT\n+    Discussions-To: <https://github.com/zcash/zips/issues/TODO>\n+    Pull-Request: <https://github.com/zcash/zips/pull/1380>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"MAY\", and \"RECOMMENDED\"\n+in this document are to be interpreted as described in BCP 14 [^BCP14] when, and\n+only when, they appear in all capitals.\n+\n+**Light client:** A Zcash wallet that does not store or validate a full copy of\n+the block chain, relying instead on a compact-block server for block data.\n+\n+**Compact-block server:** A server implementing the lightwalletd gRPC protocol\n+[^zip-0307], or a compatible implementation such as Zaino, that serves compact\n+blocks and transaction-related RPCs to light clients.\n+\n+**Submission intermediary:** Any server to which a wallet sends a\n+newly-constructed transaction for broadcast, including but not limited to a\n+compact-block server.\n+\n+**Dandelion++ stem phase / fluff phase / stem peer:** As defined in draft-zodl-dandelion (PR [#1379](https://github.com/zcash/zips/pull/1379)).\n+[^draft-zodl-dandelion]\n+\n+**IP-to-transaction correlation:** The ability of a network observer to determine\n+which IP address originated a given transaction by observing when and where the\n+transaction first appeared on the P2P network.\n+\n+\n+# Abstract\n+\n+This ZIP specifies how Zcash light-client wallets MAY submit newly-constructed\n+transactions directly to a full node peer via the Zcash P2P network — using the\n+Dandelion++ stem-phase forwarding defined in draft-zodl-dandelion (PR [#1379](https://github.com/zcash/zips/pull/1379)) [^draft-zodl-dandelion] — instead of\n+through a compact-block server, while continuing to use compact-block servers\n+exclusively for chain synchronisation.\n+\n+Under the current architecture, a wallet's submission intermediary (a\n+compact-block server such as lightwalletd or Zaino) learns both the wallet's IP\n+address and the transaction it submits at the same instant, letting a passive\n+observer correlate transaction origin with network identity. This ZIP removes\n+that correlation for the submission path.\n+\n+No consensus-rule changes are required. This ZIP covers the **wallet side**\n+(sometimes called \"Component A\"); the full-node relay behaviour it depends on is\n+specified in draft-zodl-dandelion (PR [#1379](https://github.com/zcash/zips/pull/1379)) (Component B). [^draft-zodl-dandelion]\n+\n+\n+# Motivation\n+\n+ZIP 307 [^zip-0307] defines a light-client architecture in which a compact-block\n+server mediates between the wallet and the P2P network. This is appropriate for\n+block synchronisation: a well-designed wallet fetches whole block ranges, so the\n+server learns little about which notes are the wallet's.\n+\n+Transaction submission is different. When a wallet calls the `SendTransaction`\n+gRPC RPC, the compact-block server receives:\n+\n+1. the wallet's IP address,\n+2. the exact serialised transaction, and\n+3. the submission timestamp.\n+\n+That is enough to link a transaction to a network identity. For shielded\n+transactions the on-chain data reveals nothing about the sender, yet the\n+submission channel leaks exactly the information that shielding is designed to\n+protect.\n+\n+Existing mitigations impose costs:\n+\n+- **Tor** — Zashi supports Tor [^ZASHI-TOR], but it adds latency and a\n+  dependency, and its effectiveness depends on the wallet always routing through\n+  it.\n+- **Self-hosted server** — avoids the third-party trust assumption but is\n+  impractical for most mobile users.\n+- **Zaino** [^ZAINO] — improves server-side privacy by design but still sits\n+  between the wallet and the network for submission, preserving the\n+  IP-correlation window.\n+\n+This ZIP proposes that a wallet with access to one or more Zcash P2P peer\n+endpoints SHOULD submit transactions via those peers under Dandelion++ rather\n+than via a compact-block server. Because the P2P network is a decentralised mesh\n+and the receiving node relays the transaction through its stem peer without\n+advertising it locally, no single entity learns both the transaction and the\n+submitting IP together for the transaction's onward propagation.\n+\n+\n+# Requirements\n+\n+1. A compliant wallet implementing this ZIP MUST NOT call the `SendTransaction`\n+   gRPC RPC on a compact-block server for a transaction submitted via the P2P\n+   path (concurrent submission would re-expose the origin IP through the\n+   centralised channel).\n+\n+2. A compliant wallet MUST send the transaction to a full node using the P2P\n+   handshake and message flow in the Specification below.\n+\n+3. A compliant wallet MUST continue to use compact-block servers (or equivalent)\n+   for block synchronisation, note-witness retrieval, and other read-only chain\n+   queries.\n+\n+4. The P2P submission path MUST be an optional enhancement: a wallet that cannot\n+   reach a full node peer MUST fall back to the existing `SendTransaction` RPC\n+   without degrading other functionality.\n+\n+5. The receiving full node SHOULD implement the Dandelion++ relay behaviour of\n+   draft-zodl-dandelion (PR [#1379](https://github.com/zcash/zips/pull/1379)). [^draft-zodl-dandelion] A wallet submitting via P2P gains no privacy benefit if\n+   the node immediately floods the transaction; however, a non-implementing node\n+   simply flood-broadcasts it, which is a safe fallback.\n+\n+\n+# Specification\n+\n+## Peer discovery\n+\n+A wallet implementing this ZIP MUST obtain at least one full-node peer address.\n+Acceptable mechanisms include, but are not limited to:\n+\n+- a hard-coded or user-configured list of trusted full nodes;\n+- the Zcash mainnet DNS seeders [^ZCASHSEEDS];\n+- a peer address returned by a compact-block server via an optional extension RPC\n+  (not defined here);\n+- a peer address provided directly by the user.\n+\n+The wallet MUST complete the Zcash P2P `version`/`verack` handshake before\n+submitting a transaction. The wallet MUST advertise a protocol version at least\n+equal to the receiving network's minimum accepted protocol version, or the peer\n+will reject the handshake.\n+\n+## Transaction submission message\n+\n+After the handshake, the wallet submits the transaction using the standard Zcash\n+P2P `tx` message [^ZCASHP2P]. No new P2P message type is required.\n+\n+The wallet SHOULD set the `fRelay` field in its `version` message to `false` so\n+that the peer does not push unsolicited `inv` messages back to the wallet,\n+reducing leakage about the wallet's connectivity.\n+\n+## Dandelion++ submission hint (unadvertised `tx`)\n+\n+To request that the receiving full node treat the transaction as a stem-phase\n+candidate (rather than immediately flooding it), the wallet MUST send the `tx`\n+message with **no prior `inv` announcement** on this connection. This is the\n+\"unadvertised `tx`\" convention of draft-zodl-dandelion §Stem-phase forwarding (PR [#1379](https://github.com/zcash/zips/pull/1379)). [^draft-zodl-dandelion]\n+\n+Rationale: advertising via `inv` before `tx` is the normal relay path, which\n+signals the sending node already received the transaction from elsewhere.\n+Sending `tx` without a prior `inv` on a freshly-opened connection is a reliable\n+signal that this is the transaction's first appearance.\n+\n+## No concurrent centralised submission\n+\n+The wallet MUST NOT resubmit the same transaction via a compact-block server\n+`SendTransaction` RPC while the P2P submission is in flight or within the\n+stem-phase timeout window. Concurrent submission would reveal the originating IP\n+via the centralised channel.\n+\n+## Fallback\n+\n+If the P2P submission does not result in the transaction appearing in a block\n+within a configurable timeout (RECOMMENDED default: 30 minutes), the wallet MAY\n+fall back to `SendTransaction` on a compact-block server.\n+\n+\n+# Privacy Implications\n+\n+- The wallet's P2P peer(s) see the wallet's IP and the transaction, but only\n+  before stem-phase forwarding begins. With Dandelion++ stem/fluff the peer\n+  relays the transaction to its own stem peer without advertising it locally,\n+  breaking the origin-IP↔txid association for all subsequent hops.\n+\n+- The compact-block server used for synchronisation is decoupled from the\n+  submission path and learns only that the wallet is synchronising blocks, not\n+  which transactions it submitted.\n+\n+- An adversary who controls **both** the wallet's compact-block server and the\n+  wallet's P2P peer can still correlate if the wallet uses the same IP for both.\n+  Wallets SHOULD use distinct network paths (e.g. Tor for one channel, or\n+  separate Tor circuits) if this threat model is relevant.\n+\n+- Dandelion++ does not protect against a global passive adversary monitoring all\n+  P2P traffic. It significantly raises the cost of IP-to-transaction correlation\n+  for realistic adversaries operating relay nodes or monitoring subsets of the\n+  network.\n+\n+- Shielded transactions already hide their *contents* via zero-knowledge proofs;\n+  this ZIP closes the complementary *network-metadata* leak (which IP originated\n+  the packet). The two protections are orthogonal.\n+\n+- Once a transaction enters the fluff phase it is indistinguishable from any\n+  other transaction on the network.\n+\n+\n+# Rationale\n+\n+## Why not just use Tor for all wallets?\n+\n+Tor addresses IP privacy comprehensively but adds latency (typically\n+200–500 ms), requires maintaining Tor client code per wallet, and may be\n+unavailable on some platforms. Dandelion++ is a lightweight complement. The two\n+are not mutually exclusive: a wallet using Tor **and** P2P Dandelion++ submission\n+is strictly more private than either alone.\n+\n+## Why keep compact-block servers for synchronisation?\n+\n+They provide an efficient protocol for delivering shielded-note data to mobile\n+wallets. Replacing bulk sync with a direct P2P full-sync would require\n+downloading hundreds of gigabytes, impractical on mobile. Separating the\n+submission channel from the sync channel also lets a wallet choose independent\n+providers for each, reducing the trust surface.\n+\n+## Why an unadvertised `tx` rather than a new P2P message type?\n+\n+A new message type would require a network upgrade and coordination across all\n+node implementations. The existing `tx` message with a semantic convention (no\n+prior `inv`) achieves the same signalling with no protocol change, allowing\n+incremental deployment. Non-implementing nodes flood-broadcast normally (safe\n+fallback).\n+\n+## Relationship to Zaino\n+\n+A Zaino [^ZAINO] instance co-located with a Zebra node could implement the\n+draft-zodl-dandelion (PR [#1379](https://github.com/zcash/zips/pull/1379)) relay path\n+natively, routing wallet submissions received via `SendTransaction`\n+through the local node's stem path. This would help wallets that cannot open a\n+direct P2P connection, without wallet-software changes. It is RECOMMENDED as a\n+complement to (not a replacement for) this ZIP.\n+\n+\n+# Reference Implementation\n+\n+Wallet-side (Component A):\n+\n+- **Android**: [zcash/zcash-android-wallet-sdk#2010](https://github.com/zcash/zcash-android-wallet-sdk/pull/2010)\n+- **iOS (Swift)**: [zcash/zcash-swift-wallet-sdk#1804](https://github.com/zcash/zcash-swift-wallet-sdk/pull/1804)\n+\n+Both SDKs add an opt-in `DandelionSubmitConfig` (`DirectP2P` / `directP2P`) that\n+resolves peers via the mainnet DNS seeders, opens a TCP connection to a full\n+node, performs the `version`/`verack` handshake (advertising protocol version\n+170150, the current mainnet minimum), and sends the raw `tx` without a prior\n+`inv`. Chain synchronisation continues to use the lightwalletd endpoint. A\n+fallback to `SendTransaction` is available when no P2P peer is reachable.\n+\n+Full-node relay (Component B) is specified and implemented in draft-zodl-dandelion (PR [#1379](https://github.com/zcash/zips/pull/1379)):\n+[zcashfoundation/zebra#10928](https://github.com/zcashfoundation/zebra/pull/10928).\n+\n+\n+# Changelog\n+\n+- 2026-07-09: Initial draft.\n+\n+\n+# References\n+\n+[^BCP14]: [Key words for use in RFCs to Indicate Requirement Levels](https://www.rfc-editor.org/info/bcp14)\n+\n+[^zip-0307]: [ZIP 307: Light Client Protocol for Payment Detection](https://zips.z.cash/zip-0307)\n+\n+[^draft-zodl-dandelion]: draft-zodl-dandelion: Dandelion++ Transaction Propagation for Zcash P2P Nodes. PR: https://github.com/zcash/zips/pull/1379\n+(In preparation — see zcash/zips#1379.)\n+\n+[^DPPLUSPLUS]: Venkatakrishnan, S.B., Fanti, G., Viswanath, P. \"Dandelion++: Lightweight\n+Cryptocurrency Networking with Formal Anonymity Guarantees.\" ACM SIGMETRICS 2018.\n+https://arxiv.org/abs/1805.11060\n+\n+[^ZCASHSEEDS]: Zcash mainnet DNS seeds are defined in the Zcash Protocol\n+Specification, section 3.12 (Network Upgrades).\n+\n+[^ZCASHP2P]: Zcash P2P message format follows Bitcoin's protocol with\n+Zcash-specific extensions. See the Zcash Protocol Specification for the canonical\n+definition of the `version`, `verack`, `inv`, and `tx` messages.\n+\n+[^ZAINO]: [Zaino: Zcash Chain Indexer and Light Wallet Server](https://github.com/ZcashFoundation/zaino)\n+\n+[^ZASHI-TOR]: Zashi Tor integration. https://github.com/Electric-Coin-Company/zashi-android"}],"filesTruncated":false,"preview":{"filename":"zips/draft-zodl-dandelion-wallet.md","title":"Direct Wallet Transaction Submission via Dandelion++ P2P Relay","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"peer-discovery","text":"Peer discovery"},{"level":2,"id":"transaction-submission-message","text":"Transaction submission message"},{"level":2,"id":"dandelion-submission-hint-unadvertised-tx","text":"Dandelion++ submission hint (unadvertised tx)"},{"level":2,"id":"no-concurrent-centralised-submission","text":"No concurrent centralised submission"},{"level":2,"id":"fallback","text":"Fallback"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"why-not-just-use-tor-for-all-wallets","text":"Why not just use Tor for all wallets?"},{"level":2,"id":"why-keep-compact-block-servers-for-synchronisation","text":"Why keep compact-block servers for synchronisation?"},{"level":2,"id":"why-an-unadvertised-tx-rather-than-a-new-p2p-message-type","text":"Why an unadvertised tx rather than a new P2P message type?"},{"level":2,"id":"relationship-to-zaino","text":"Relationship to Zaino"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"changelog","text":"Changelog"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1337,"title":"[ZIP TBD] Define a standard Zcash wallet export format.","author":"nuttycom","createdAt":"2026-07-21T20:27:59Z","updatedAt":"2026-09-01T22:48:02Z","url":"https://github.com/zcash/zips/pull/1337","diffUrl":"https://github.com/zcash/zips/pull/1337.diff","headRef":"draft/nuttycom-zewif","headRepo":"zcash/zips","headSha":"5e5eee73c7b68defca90ed71f521d1dec65280dc","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Fixes #821","zipRefs":[],"draftRefs":["draft-nuttycom-zewif"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -249,6 +249,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-deploy-nu7</td> <td class=\"left\"><a href=\"zips/draft-arya-deploy-nu7.md\">Deployment of the NU7 Network Upgrade</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/839\">zips#839</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-nuttycom-zewif</td> <td class=\"left\"><a href=\"zips/draft-nuttycom-zewif.md\">ZeWIF: Zcash Wallet Interchange Format</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>\n "},{"filename":"zips/draft-nuttycom-zewif.md","status":"added","additions":733,"deletions":0,"patch":"@@ -0,0 +1,733 @@\n+<pre>\n+ZIP: XXX\n+Title: ZeWIF: Zcash Wallet Interchange Format\n+Owners: Kris Nuttycombe &lt;kris@nutty.land&gt;\n+Status: Draft\n+Category: Standards / Wallet\n+Created: 2026-07-02\n+License: MIT\n+Discussions-To: &lt;https://github.com/zcash/zewif/issues&gt;\n+</pre>\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"MAY\", and\n+\"RECOMMENDED\" in this document are to be interpreted as described in BCP 14\n+[^BCP14] when, and only when, they appear in all capitals.\n+\n+\"ZeWIF document\" means a byte sequence conforming to the container format\n+defined in this specification. \"Exporter\" means software producing a ZeWIF\n+document from wallet state; \"importer\" means software consuming one.\n+\n+# Abstract\n+\n+This proposal defines ZeWIF, a file format for migrating wallet data between\n+Zcash wallet implementations. A ZeWIF document is a single-snapshot archive of\n+the state a wallet cannot recover from the block chain alone — key material,\n+account structure, address metadata, user annotations — together with optional\n+chain-derived data that lets an importing wallet avoid expensive rescanning.\n+The format is a deterministic CBOR encoding [^RFC8949] under a normative CDDL\n+schema [^RFC8610], with spending-key material segregated into a section that\n+can be independently encrypted.\n+\n+# Motivation\n+\n+Zcash wallet implementations persist their state in mutually incompatible\n+formats. When a wallet reaches end of service (as zcashd has) or a user wishes\n+to change wallets, the absence of an interchange format forces ad-hoc pairwise\n+migration tools, and data that only the source wallet knows — spending keys\n+for legacy addresses, address books, memos and annotations on transactions,\n+address-exposure state needed for gap-limit-correct restores — is routinely\n+lost.\n+\n+An interchange format for this purpose has unusual durability requirements: a\n+ZeWIF file may be written to offline storage and read decades later, possibly\n+after its producing software has disappeared. This motivates the central\n+design choices:\n+\n+- **A widely deployed, standardized encoding.** CBOR is an IETF Internet\n+  Standard (STD 94) with independent implementations in every mainstream\n+  language, and is self-describing at the data-model level: any generic CBOR\n+  tool can decode the structure of a ZeWIF file without this specification in\n+  hand. A ZeWIF document begins with the self-described-CBOR tag (RFC 8949\n+  §3.4.6), so its leading bytes are a magic number that identifies the file as\n+  CBOR to content-sniffing tools, and it carries a registered CBOR tag that\n+  identifies it specifically as ZeWIF.\n+- **A normative machine-readable schema.** The CDDL schema in this document is\n+  the authoritative definition of the format; the Rust reference\n+  implementation conforms to it, not the reverse.\n+- **Deterministic encoding.** Writers emit RFC 8949 §4.2 Core Deterministic\n+  Encoding, so equal wallet states produce byte-identical documents. This\n+  makes round-trip conformance testing exact and documents stable under\n+  re-export.\n+- **Spending/viewing separability.** All secret key material lives in a single\n+  section of the document that may be stored encrypted, so a viewing-only\n+  export is obtained by omitting one node, and an at-rest export can protect\n+  its secrets without encrypting chain-public data.\n+\n+# Requirements and non-goals\n+\n+The format MUST be able to represent the wallet state persisted by\n+`zcash_client_sqlite` (the librustzcash reference wallet backend) and the\n+state recoverable from a zcashd `wallet.dat`, without loss of any data that is\n+not recoverable from the block chain.\n+\n+The format is a snapshot archive. It is NOT a synchronization protocol, a\n+backup-rotation scheme, or an on-chain format. Nothing in this specification\n+defines network behavior.\n+\n+# Specification\n+\n+## Container format\n+\n+A ZeWIF document is a single CBOR data item, a nesting of two tags around a\n+`[version, payload]` array:\n+\n+```text\n+#6.55799(#6.133133([version, payload]))\n+```\n+\n+where, from the outside in:\n+\n+- **`#6.55799(...)`** is the \"Self-Described CBOR\" tag (RFC 8949 §3.4.6). It\n+  carries no semantics beyond marking its content; its purpose here is its\n+  encoding, the three bytes `0xD9 0xD9 0xF7`, which are a registered magic\n+  number identifying the byte stream as CBOR to generic content-sniffing\n+  tools. Every ZeWIF document begins with these three bytes.\n+- **`#6.133133(...)`** is the ZeWIF tag, 133133 being the tag number assigned\n+  to this format in the IANA \"CBOR Tags\" registry (see\n+  [Container tag](#container-tag)). It gives a decoder that knows the tag\n+  positive identification of the item as a ZeWIF document.\n+- **`[version, payload]`** is a definite-length two-element array: an unsigned\n+  `version` (this document defines version 1) followed by the `payload`, a\n+  single CBOR data item conforming to the `zewif` rule of the schema below.\n+\n+Readers MUST reject a document not framed by these two tags in this order, and\n+MUST NOT attempt to interpret the payload of a document whose version they do\n+not implement. The container version selects the schema revision and every\n+parsing rule applicable to the payload; implementations supporting multiple\n+versions MUST dispatch on it rather than attempting to unify parsers. Because\n+the version precedes the payload in the array, a reader determines the version\n+before decoding the payload. Version 1 payloads are not compressed;\n+compression, if ever introduced, will be signalled by a new container version.\n+\n+### Container tag\n+\n+IANA has assigned the tag number **133133** to ZeWIF in the First Come First\n+Served range of the \"CBOR Tags\" registry [^IANACBOR]. The value echoes Zcash's\n+SLIP-0044 registered coin type (133) by repetition.\n+\n+## CBOR profile\n+\n+- Writers MUST emit RFC 8949 §4.2 Core Deterministic Encoding: definite\n+  lengths everywhere, integers in their shortest form, and map keys sorted in\n+  bytewise lexicographic order of their encodings. Readers SHOULD accept any\n+  well-formed CBOR that satisfies the schema.\n+- Optional record fields are expressed by *omitting* the map entry. Writers\n+  MUST NOT encode an absent optional field as CBOR `null`; readers SHOULD\n+  treat a `null` value in an optional field position as if the entry were\n+  absent. (The reader tolerance is a defense in depth: encoder libraries\n+  have been observed to disagree on the encoding of absent optional values,\n+  and a document produced by a nonconformant writer should degrade to a\n+  readable document rather than an unreadable one.)\n+- Readers MUST ignore map keys not defined in the schema version they\n+  implement. This is the format's forward-compatibility mechanism.\n+- A field index (map key), once assigned a meaning in a published revision of\n+  this schema, MUST NOT be reused or assigned a different type. New fields\n+  are added under fresh indices.\n+- A boolean field documented with a default MUST be omitted by writers when it\n+  takes the default value (so that each state has exactly one encoding).\n+\n+## Structural conventions\n+\n+The schema uses three shapes, chosen so that every logical state has exactly\n+one encoding:\n+\n+1. **Records** are CBOR maps with small unsigned-integer keys (the COSE/CWT\n+   convention [^RFC9052]). The integer registry in the CDDL below maps each\n+   index to a field name.\n+2. **Enumerations without payload** (key scope, account purpose) are bare\n+   unsigned integers.\n+3. **Tagged unions** (sums-of-products) are arrays `[variant-id, body?]`:\n+   an unsigned variant identifier, followed for data-bearing variants by\n+   exactly one body item — a record, or, where the payload is a single\n+   canonical encoding, that encoding's byte or text string directly.\n+   Payload-free variants are the one-element array `[variant-id]`. Variant\n+   identifiers follow the same never-reuse rule as field indices.\n+\n+Byte sequences are CBOR byte strings (`bstr`); text is UTF-8 (`tstr`). Block\n+heights are unsigned integers less than 2^32. Monetary values are unsigned\n+integers in zatoshis, at most 2,100,000,000,000,000 (`MAX_MONEY`). Timestamps\n+are UTC integer seconds since the Unix epoch.\n+\n+Where a value has a canonical Zcash encoding, ZeWIF stores that encoding\n+verbatim rather than a decomposition, so that consumers do not require\n+protocol-library support to carry the data. Addresses and keys use their\n+canonical *string* encodings — unified addresses and viewing keys per\n+ZIP 316 [^ZIP316], Sapling extended keys per ZIP 32 [^ZIP32], Sprout keys\n+and transparent private keys (WIF) per the Zcash protocol specification's\n+Base58Check forms [^PROTOCOL], and extracted unified spending keys per the\n+unified raw encodings draft [^ZIPRAW] — which carry a checksum and embed\n+type and network discrimination, so each key is independently\n+integrity-checked; seed fingerprints likewise use their ZIP 32 Bech32m\n+string encoding (Human-Readable Part \"zip32seedfp\"). Raw transactions use\n+their canonical byte encodings.\n+\n+## Sensitive material\n+\n+All secret key material in a ZeWIF document lives in the `secret-store`\n+record, referenced from the public wallet structure by public identifiers:\n+seeds by their ZIP 32 seed fingerprint, transparent private keys by their\n+public key, Sapling spending keys by their extended full viewing key,\n+extracted unified spending keys by their unified full viewing key, and\n+Sprout spending keys by their address.\n+\n+The `secrets` node of the top-level record is either the secret store in\n+plain CBOR, or an age ciphertext [^AGE] whose plaintext is the CBOR encoding\n+of the secret store. The choice of age recipients (passphrase-derived or\n+X25519) is between the exporter and its user; the document carries only the\n+ciphertext.\n+\n+A viewing-only export omits the `secrets` node entirely. Importers MUST treat\n+the absence of an expected secret-store entry (e.g. for an address marked as\n+having an imported key) as a viewing-only import of the affected item, not as\n+an error.\n+\n+## Schema\n+\n+The following CDDL is normative. Comments give the field names used by the\n+reference implementation.\n+\n+```cddl\n+zewif = {\n+  0: [* wallet],                ; wallets\n+  1: [* transaction],           ; global transaction table, sorted\n+                                ; ascending by txid, unique by txid\n+  2: height,                    ; export-height: chain tip at export time\n+  3: bytes32,                   ; hash of the block at export-height;\n+                                ; advisory — a chain reorganization may\n+                                ; orphan this block after export\n+  ? 4: secrets,                 ; sensitive key material (see below)\n+  ? 5: bytes32,                 ; export-id: random identifier for this export\n+  ? 6: extensions,\n+  ? 7: tstr,                    ; embedded copy of the CDDL schema for this\n+                                ; document's container version, for archival\n+                                ; self-description; informative only — the\n+                                ; container version remains authoritative\n+}\n+\n+wallet = {\n+  0: network,\n+  1: [* account],\n+  2: [* address-book-entry],\n+  ? 3: extensions,\n+}\n+\n+network = [0]                     ; mainnet\n+        / [1]                     ; testnet\n+        / [2, regtest-params]     ; regtest\n+\n+; Regtest networks vary in their network-upgrade activation schedules, and\n+; wallet data recorded against one activation schedule is in general\n+; incompatible with a chain using another. The activation map makes the\n+; network's composition legible to the importer, which SHOULD refuse or\n+; flag data whose activation schedule does not match the chain it operates\n+; against.\n+regtest-params = {\n+  0: {* uint => height},        ; consensus branch ID (as defined by ZIP 200\n+                                ; and successors) => activation height, for\n+                                ; each network upgrade active on the chain\n+}\n+\n+; import-height records when an account entered the exporting wallet, rather\n+; than anything about the chain: it is the height the exporter observed at\n+; the time the account's key material was imported or restored. It is not\n+; recoverable from the chain and has no consensus meaning. An importer MAY\n+; use it to separate historical catch-up (from birthday-height up to, but\n+; not including, import-height) from following the chain tip, so that\n+; restore progress can be reported apart from ongoing sync progress.\n+account = {\n+  0: tstr,                      ; name (may be empty; no uniqueness semantics)\n+  1: account-viewing-key,\n+  ? 2: key-source,\n+  ? 3: height,                  ; birthday-height: first height to scan\n+  ? 4: bytes32,                 ; birthday-block: hash of the block at\n+                                ; birthday-height\n+  ? 5: chain-state,             ; birthday-chain-state: tree state at the end\n+                                ; of the block immediately BEFORE birthday-height;\n+                                ; scanning begins at the following block\n+  ? 6: height,                  ; import-height: the chain height at which\n+                                ; this account's data was imported into the\n+                                ; exporting wallet\n+  ? 7: account-purpose,\n+  ? 8: tstr,                    ; provenance: free-form key-material origin\n+                                ; tag, e.g. \"zcashd_mnemonic\"\n+  9: [* scan-range],            ; fully-scanned ranges\n+  10: [* address],               ; addresses belonging to the account\n+  11: {* txid => [* received-output]},  ; received outputs by transaction\n+  12: {* txid => [* sent-output]},      ; sent outputs by transaction\n+  ? 13: extensions,\n+}\n+\n+account-viewing-key = [0, ufvk]                ; ZIP 316 UFVK\n+                    / [1, sapling-extfvk]      ; standalone Sapling extfvk\n+                    / [2, sprout-vk]           ; Sprout viewing key\n+                    / [3]                      ; an indicator that this is a set of transparent addresses\n+                                               ; derived from system randomness (legacy zcashd)\n+\n+; Viewing keys are carried in their canonical string encodings, which\n+; embed a checksum plus type and network discrimination.\n+ufvk           = tstr           ; ZIP 316 Bech32m (\"uview1...\")\n+sapling-extfvk = tstr           ; ZIP 32 Bech32 (\"zxviews...\" /\n+                                ; \"zxviewtestsapling...\")\n+sprout-vk      = tstr           ; Base58Check (\"ZiVK...\" / \"ZiVt...\")\n+\n+key-source = [0, key-source-derived]\n+           / [1]                ; imported\n+\n+key-source-derived = {\n+  0: seed-fingerprint,\n+  1: uint,                      ; ZIP 32 account index (hardened)\n+  ? 2: uint,                    ; zcashd legacy address index (< 2^31); the\n+                                ; address-index component of the legacy path\n+                                ; m/32h/coin_h/0x7FFFFFFFh/index_h\n+}\n+\n+account-purpose = 0             ; spending: source wallet held spend authority\n+                / 1             ; view-only\n+\n+chain-state = {\n+  0: height,                    ; the block whose end-state this describes\n+  ? 1: bytes32,                 ; hash of that block\n+  ? 2: frontier,                ; Sapling note commitment tree frontier\n+  ? 3: frontier,                ; Orchard note commitment tree frontier\n+  ? 4: frontier,                ; Ironwood note commitment tree frontier\n+}                               ; future pools: new indices\n+\n+frontier = [0]                  ; the tree is empty as of the block\n+         / [1, frontier-data]\n+                                ; a more compact representation — omitting a\n+                                ; right-hand leaf in favor of the root of the\n+                                ; subtree it completes — may be added as a\n+                                ; future variant under a fresh identifier\n+\n+; non-empty note commitment tree frontier encoded as in `incrementalmerkletree`\n+frontier-data = {\n+  0: uint,                      ; position: 0-based index of the most\n+                                ; recently appended leaf\n+  1: bytes32,                   ; that leaf's node value\n+  2: [* bytes32],               ; ommers: roots of completed left subtrees,\n+                                ; leaf-to-root order; the count is fully\n+                                ; determined by position\n+}\n+\n+scan-range = {\n+  0: height,                    ; start (inclusive)\n+  1: height,                    ; end (exclusive)\n+}\n+\n+address = {\n+  0: protocol-address,\n+  ? 1: key-scope,\n+  ? 2: height,                  ; exposed-at-height: when the address was\n+                                ; first exposed to a user or counterparty;\n+                                ; not chain-recoverable; absent = never\n+                                ; exposed (e.g. zcashd keypool keys)\n+  ? 3: extensions,\n+}\n+\n+key-scope = 0                   ; external: user-facing receiving\n+          / 1                   ; internal: change/shielding, never exposed\n+          / 2                   ; ephemeral: ZIP 320 single-use transparent\n+          / 3                   ; foreign: imported standalone key or script\n+\n+protocol-address = [0, transparent-address]\n+                 / [1, sprout-address]\n+                 / [2, sapling-address]\n+                 / [3, unified-address]\n+\n+transparent-address = {\n+  0: tstr,                      ; canonical address string\n+  ? 1: transparent-spend-authority,\n+  ? 2: transparent-pubkey,      ; pubkey: watch-only imported P2PK/P2PKH;\n+                                ; omitted when spend authority is present\n+  ? 3: bstr,                    ; redeem-script: watch-only imported P2SH\n+}\n+\n+transparent-spend-authority\n+  = [0, derivation-info]        ; derived: recoverable from seed\n+  / [1]                         ; imported: private key, if exported, is in\n+                                ; the secret store under this address's pubkey\n+\n+derivation-info = {\n+  0: uint,                      ; change component (0 external, 1 internal,\n+                                ; 2 ephemeral)\n+  1: uint,                      ; address index (non-hardened)\n+}\n+\n+sprout-address  = { 0: tstr }\n+sapling-address = { 0: tstr, ? 1: bytes11 }   ; diversifier index\n+unified-address = { 0: tstr, ? 1: bytes11 }   ; diversifier index\n+\n+transaction = {\n+  0: txid,\n+  ? 1: transaction-data,\n+  ? 2: height,                  ; target-height\n+  ? 3: height,                  ; mined-height\n+  ? 4: tx-block-position,\n+  ? 5: uint,                    ; fee in zatoshis\n+  ? 6: height,                  ; expiry-height\n+  ? 7: int,                     ; created-time: unix seconds; creation time\n+                                ; for wallet-authored transactions, first-\n+                                ; observed time otherwise\n+  ? 8: bool,                    ; trusted (ZIP 315); default false, omitted\n+                                ; when false\n+  ? 9: extensions,\n+}\n+\n+transaction-data = [0, raw-tx-data]    ; preferred to compact transaction data if available\n+                 / [1, compact-tx-data]\n+\n+raw-tx-data = { 0: bstr }       ; canonical Zcash transaction encoding\n+\n+compact-tx-data = {\n+  0: bstr,                      ; protobuf-encoded CompactTx\n+  1: tstr,                      ; lightwalletd protocol semver that produced\n+                                ; the encoding (the protobuf schema is not\n+                                ; self-describing)\n+}\n+\n+tx-block-position = {\n+  0: bytes32,                   ; block hash\n+  1: uint,                      ; index of the transaction within the block\n+}\n+\n+; Value, memo, change status, and nullifiers are optional enrichment:\n+; recoverable from the raw transaction plus the account viewing key. The raw\n+; transaction is authoritative where both are present. Exporters SHOULD\n+; populate them when raw transaction data is absent.\n+received-output = {\n+  0: uint,                      ; output-index within the pool's output list\n+                                ; (Sprout: 2*joinsplit_index + n, each\n+                                ; JoinSplit having exactly two outputs)\n+  1: received-output-pool,\n+  ? 2: uint,                    ; value in zatoshis\n+  ? 3: bstr,                    ; memo (at most 512 bytes)\n+  ? 4: bool,                    ; is-change; absent = unknown\n+  ? 5: txid,                    ; spent-by\n+}\n+\n+received-output-pool = [0, transparent-output-data]\n+                     / [1, sprout-output-data]\n+                     / [2, sapling-output-data]\n+                     / [3, orchard-output-data]\n+                     / [4, ironwood-output-data]\n+\n+transparent-output-data = {\n+  ? 0: bstr,                    ; scriptPubKey, for UTXOs whose containing\n+                                ; transaction is unavailable; the raw\n+                                ; transaction is authoritative when present\n+  ? 1: height,                  ; max-observed-unspent-height\n+}\n+\n+sprout-output-data = {\n+  ? 0: bytes32,                 ; nullifier\n+}                               ; Sprout spendability is not reconstructible\n+                                ; from this data; a Sprout-capable importer\n+                                ; must rescan\n+\n+sapling-output-data = {\n+  ? 0: commitment-tree-data,\n+  ? 1: bytes32,                 ; nullifier\n+}\n+\n+orchard-output-data = {\n+  ? 0: commitment-tree-data,\n+  ? 1: bytes32,                 ; nullifier\n+}\n+\n+; The Ironwood pool (introduced by the NU6.3 network upgrade) uses the\n+; Orchard protocol -- the same circuit and bundle schema -- but constitutes\n+; a distinct value pool with its own note commitment tree.\n+ironwood-output-data = {\n+  ? 0: commitment-tree-data,\n+  ? 1: bytes32,                 ; nullifier\n+}\n+\n+commitment-tree-data = [0, tree-position]\n+                     / [1, incremental-witness]\n+\n+tree-position = { 0: uint }     ; 0-based leaf position of the note\n+                                ; commitment in its pool's tree\n+\n+incremental-witness = {\n+  0: bytes32,                   ; note commitment\n+  1: uint,                      ; note position\n+  2: [* bytes32],               ; merkle path, leaf to root\n+  3: bytes32,                   ; anchor\n+  4: uint,                      ; tree size as of the anchor\n+  5: [* bytes32],               ; tree frontier as of the anchor\n+}\n+\n+; No Sprout variant is defined: Sprout provides no outgoing-viewing-key\n+; mechanism by which a sender could later recover its non-change outputs,\n+; and zcashd persists no Sprout sent-output metadata (its recipient mapping\n+; records apply only to unified-address sends). A wallet that independently\n+; tracked such records may carry them as extension data.\n+sent-output = [0, transparent-sent-output]\n+            / [1, sapling-sent-output]\n+            / [2, orchard-sent-output]\n+            / [3, ironwood-sent-output]\n+\n+transparent-sent-output = {\n+  0: uint,                      ; output-index within the transaction's vout\n+  1: tstr,                      ; recipient address, verbatim as used\n+  2: uint,                      ; value in zatoshis\n+}\n+\n+sapling-sent-output = {\n+  0: uint,                      ; output-index within the Sapling output list\n+  1: tstr,                      ; recipient address, verbatim (may be a\n+                                ; unified address; not reconstructible from\n+                                ; the pool component alone)\n+  2: uint,                      ; value in zatoshis\n+  ? 3: bstr,                    ; memo\n+}\n+\n+orchard-sent-output = {\n+  0: uint,                      ; action index within the Orchard action list\n+  1: tstr,                      ; recipient address, verbatim\n+  2: uint,                      ; value in zatoshis\n+  ? 3: bstr,                    ; memo\n+}\n+\n+ironwood-sent-output = {\n+  0: uint,                      ; action index within the Ironwood action list\n+  1: tstr,                      ; recipient address, verbatim\n+  2: uint,                      ; value in zatoshis\n+  ? 3: bstr,                    ; memo\n+}\n+\n+address-book-entry = {\n+  0: tstr,                      ; address, canonical string (any protocol);\n+                                ; may be wallet-owned (recording whom it was\n+                                ; given to) or a counterparty address\n+  ? 1: tstr,                    ; label (zcashd \"name\" records)\n+  ? 2: tstr,                    ; purpose, e.g. \"receive\"/\"send\"/\"unknown\"\n+                                ; (zcashd \"purpose\" records)\n+  ? 3: extensions,\n+}\n+\n+secrets = [0, secret-store]     ; plaintext\n+        / [1, encrypted-store]\n+\n+encrypted-store = {\n+  0: bstr,                      ; age ciphertext; plaintext is the CBOR\n+                                ; encoding of a secret-store\n+}\n+\n+secret-store = {\n+  0: [* seed-entry],\n+  1: [* transparent-key-entry],\n+  2: [* sapling-key-entry],\n+  3: [* sprout-key-entry],\n+  ? 4: extensions,\n+  ? 5: [* unified-key-entry],   ; omitted when empty\n+}\n+\n+seed-entry = {\n+  0: seed-fingerprint,          ; referenced by key-source-derived entries\n+  1: seed-material,\n+}\n+\n+seed-material = [0, bip39-mnemonic]\n+              / [1, legacy-seed]\n+\n+legacy-seed = bytes32           ; raw pre-mnemonic HD seed bytes\n+\n+bip39-mnemonic = {\n+  0: tstr,                      ; the mnemonic phrase\n+  ? 1: tstr,                    ; language, BCP 47 tag (e.g. \"en\")\n+}\n+\n+; An extracted single-account unified spending key, stored under the\n+; unified full viewing key it corresponds to. Most wallets hold spend\n+; authority as seeds, from which per-account unified spending keys are\n+; derived on demand; this entry represents a wallet holding only an\n+; extracted spending key. The encoding is the Bech32m string obtained by\n+; applying F4Jumble to the key's raw encoding, per the unified raw\n+; encodings draft ZIP.\n+unified-key-entry = {\n+  0: ufvk,\n+  1: tstr,                      ; unified spending key text encoding\n+}\n+\n+transparent-key-entry = {\n+  0: transparent-pubkey,        ; secp256k1 public key\n+  1: tstr,                      ; private key in WIF Base58Check encoding\n+                                ; (version 0x80 mainnet / 0xEF testnet; a\n+                                ; trailing 0x01 marks a compressed pubkey)\n+}\n+\n+sapling-key-entry = {\n+  0: sapling-extfvk,            ; the extended full viewing key this\n+                                ; spending key corresponds to\n+  1: tstr,                      ; extended spending key, ZIP 32 Bech32\n+                                ; (\"secret-extended-key-main...\" / \"-test...\")\n+}\n+\n+sprout-key-entry = {\n+  0: tstr,                      ; Sprout address\n+  1: tstr,                      ; Sprout spending key, Base58Check\n+                                ; (\"SK...\" / \"ST...\")\n+}\n+\n+; Vendor-namespaced extension data. Vendor identifiers SHOULD be reverse-DNS\n+; or another collision-resistant convention. Re-exporting software MUST\n+; preserve extension entries it does not understand.\n+extensions = {* tstr => {* tstr => extension-value}}\n+\n+; A single embedded CBOR data item (the semantics of RFC 8949 tag 24).\n+extension-value = bstr\n+\n+; A ZIP 32 seed fingerprint in its canonical string encoding: Bech32m with\n+; the Human-Readable Part \"zip32seedfp\" over the 32 fingerprint bytes.\n+; Fingerprints are not network-bound.\n+seed-fingerprint = tstr\n+\n+transparent-pubkey = bstr .size 33 / bstr .size 65\n+\n+height  = uint .lt 4294967296\n+txid    = bytes32\n+bytes32 = bstr .size 32\n+bytes11 = bstr .size 11\n+```\n+\n+## Schema evolution\n+\n+A revision of this specification MAY:\n+\n+- add new optional fields to a record under fresh indices;\n+- add new variants to a tagged union under fresh identifiers;\n+- introduce a new container version, incrementing the `version` element of\n+  the container array, to signal a schema revision that a version-1 reader\n+  MUST NOT attempt to parse.\n+\n+A revision MUST NOT change the type or meaning of an existing index or\n+variant identifier, and MUST NOT convert an optional field to required.\n+Importers encountering an unknown variant identifier in a tagged union MUST\n+NOT misinterpret it; software that re-exports a document it read SHOULD\n+preserve unrecognized variants and extension data opaquely, and MUST report\n+to its caller any data it drops.\n+\n+# Rationale\n+\n+**Why CBOR and CDDL.** The candidate encodings seriously considered were\n+CBOR+CDDL, Protocol Buffers, and JSON+JSON-Schema. Protocol Buffers offer the\n+strongest field-evolution discipline and are already deployed in the Zcash\n+light-client protocol, but a protobuf document is not self-describing (the\n+`.proto` is required to recover even the structure), serialization is\n+explicitly non-canonical, and code generation imposes toolchain weight on\n+every implementation. JSON maximizes ubiquity and eyeball-durability but\n+handles the format's pervasive byte strings poorly. CBOR is an IETF Internet\n+Standard with a defined deterministic profile and native byte strings, and a\n+generic CBOR decoder recovers the full structure of a ZeWIF file without any\n+schema in hand.\n+\n+**Why integer map keys.** Field-name strings would make documents fully\n+self-describing, but integer keys with a published registry follow the\n+established practice of COSE and CWT, produce materially smaller documents\n+(wallet exports repeat record shapes thousands of times), and carry the same\n+never-reuse evolution discipline as protobuf field numbers. The CDDL registry\n+in this document, which is versioned and published, supplies the names.\n+\n+**Why `[id, body?]` unions.** Encoding data-bearing variants with exactly\n+one body item gives every variant body a named schema rule, keeps the\n+encoding of \"no payload\" (the one-element array `[id]`) distinct from any\n+body, and — in the reference implementation — sidesteps a class of encoder\n+disagreements about optional fields embedded directly in variant bodies. A\n+body is a record unless the payload is a single canonical encoding, in\n+which case that encoding's byte or text string is carried directly: a\n+one-field record shell around such an encoding would name the same concept\n+twice.\n+\n+**Prior format.** An earlier iteration of ZeWIF serialized to Gordian\n+Envelope (dcbor). That design imported a large dependency stack (223 crates\n+locked, including unrelated cryptography) for the reference implementation,\n+had no format version marker, and relied on encoding conventions defined\n+outside any standards process. No dcbor-based ZeWIF files are known to have\n+been produced outside development, so this specification makes a clean break;\n+the Envelope layer's useful property — deterministic encoding — is retained\n+via RFC 8949 §4.2.\n+\n+**Why age for secret encryption.** age is a small, well-specified,\n+widely-implemented file-encryption format with both passphrase and public-key\n+recipients, and is already the at-rest key-material encryption used by zallet\n+(the zcashd successor wallet). Embedding an age ciphertext rather than\n+defining a bespoke scheme keeps cryptographic agility and audit surface\n+outside this specification.\n+\n+**Enrichment fields.** Data recoverable from a raw transaction plus a viewing\n+key (output values, memos, nullifiers, change status) is optional and\n+subordinate to the raw transaction. This lets exporters that hold raw\n+transactions skip trial decryption, lets compact-data-only wallets export\n+what they know, and defines conflict resolution when both are present.\n+\n+**Witness policy.** Note commitment tree positions plus a per-account\n+birthday frontier are sufficient for an importer with chain access to rebuild\n+witnesses by scanning forward from the birthday. Full incremental witnesses\n+are carried optionally for importers without chain access. Neither is\n+required: a rescan-based importer (like zallet's zcashd migration) may ignore\n+both.\n+\n+**No compression in version 1.** Wallet exports are dominated by\n+already-compact byte strings; a compression layer adds a decompression\n+dependency to every future reader for marginal size benefit. External\n+compression of the whole file remains possible without affecting the format.\n+\n+# Reference implementation\n+\n+The `zewif` Rust crate (https://github.com/zcash/zewif) implements this\n+specification. Implementation notes, non-normative:\n+\n+- The crate uses `minicbor` with derived codecs; integer field indices in the\n+  schema correspond to `#[n(...)]` attributes, records use `#[cbor(map)]`,\n+  payload-free enumerations use `#[cbor(index_only)]`, fixed-size byte types\n+  carry generated length-checked `bstr` codecs, and canonically-text-encoded\n+  keys are transparent newtypes over `tstr`.\n+- Deterministic map-key ordering is obtained by declaring record fields in\n+  ascending index order and using ordered collections (sorted arrays keyed\n+  by txid) rather than hash maps.\n+- Conformance is tested by randomized round-trip tests plus byte-exact\n+  re-encoding checks. Conformance test vectors derived from this\n+  specification independently of the codec implementation will accompany\n+  submission for community review.\n+- The normativity of this schema, rather than of any codec library, is\n+  enforced mechanically: golden byte-exact fixture documents are committed\n+  to the repository and asserted in continuous integration, so a behavioral\n+  change in the codec dependency fails the build rather than shipping\n+  spec-divergent bytes; emitted documents are additionally validated against\n+  the CDDL with an independent tool. Codec dependency upgrades are treated\n+  as format-affecting changes, permitted only when the golden vectors\n+  continue to pass.\n+\n+# References\n+\n+[^BCP14]: BCP 14: RFC 2119 and RFC 8174, Key words for use in RFCs.\n+[^RFC8949]: RFC 8949: Concise Binary Object Representation (CBOR). STD 94.\n+[^RFC8610]: RFC 8610: Concise Data Definition Language (CDDL).\n+[^RFC9052]: RFC 9052: CBOR Object Signing and Encryption (COSE): Structures.\n+[^IANACBOR]: IANA \"CBOR Tags\" registry.\n+  https://www.iana.org/assignments/cbor-tags/\n+[^ZIP32]: ZIP 32: Shielded Hierarchical Deterministic Wallets.\n+[^ZIP200]: ZIP 200: Network Upgrade Mechanism.\n+[^ZIP315]: ZIP 315: Best Practices for Wallet Handling of Multiple Pools.\n+[^ZIP316]: ZIP 316: Unified Addresses and Unified Viewing Keys.\n+[^ZIP320]: ZIP 320: Defining an Address Type to which funds can only be sent\n+  from Transparent Addresses.\n+[^ZIPRAW]: Unified raw encodings (draft ZIP).\n+  https://github.com/zcash/zips/pull/660\n+[^PROTOCOL]: Zcash Protocol Specification, section \"Encodings of Addresses\n+  and Keys\".\n+[^AGE]: age: a simple, modern and secure file encryption format.\n+  https://age-encryption.org/v1"}],"filesTruncated":false,"preview":{"filename":"zips/draft-nuttycom-zewif.md","title":"[ZIP TBD] Define a standard Zcash wallet export format.","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements-and-non-goals","text":"Requirements and non-goals"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"container-format","text":"Container format"},{"level":3,"id":"container-tag","text":"Container tag"},{"level":2,"id":"cbor-profile","text":"CBOR profile"},{"level":2,"id":"structural-conventions","text":"Structural conventions"},{"level":2,"id":"sensitive-material","text":"Sensitive material"},{"level":2,"id":"schema","text":"Schema"},{"level":2,"id":"schema-evolution","text":"Schema evolution"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1200,"title":"[ZIP TBD] Shielded Voting Protocol","author":"p0mvn","createdAt":"2026-03-05T14:56:00Z","updatedAt":"2026-08-18T21:48:09Z","url":"https://github.com/zcash/zips/pull/1200","diffUrl":"https://github.com/zcash/zips/pull/1200.diff","headRef":"roman/voting-protocol","headRepo":"valargroup/zips","headSha":"7b1ef40df9ec85242f725e02073635c2c118d9ae","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"## Summary This PR introduces **Shielded Voting Protocol** — a complete specification for stake-weighted governance voting over the Zcash Orchard shielded pool. Holders prove their ZEC balance via the [Orchard Proof-of-Balance](zips/draft-valargroup-orchard-balance-proof.md) and cast votes without r","zipRefs":[],"draftRefs":["draft-valargroup-shielded-voting"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-valargroup-shielded-voting.md","status":"added","additions":1722,"deletions":0,"patch":"@@ -0,0 +1,1722 @@\n+    ZIP: Unassigned\n+    Title: Shielded Voting Protocol\n+    Owners: Dev Ojha <dojha@berkeley.edu>\n+            Roman Akhtariev <ackhtariev@gmail.com>\n+            Adam Tucker <adamleetucker@outlook.com>\n+            Greg Nagy <greg@dhamma.works>\n+    Credits: Daira-Emma Hopwood\n+             Jack Grigg\n+    Status: Draft\n+    Category: Standards\n+    Created: 2026-03-04\n+    License: MIT\n+    Pull-Request: <https://github.com/zcash/zips/pull/1200>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"REQUIRED\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and\n+only when, they appear in all capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms below are to be interpreted as follows:\n+\n+Ballot\n+\n+: A unit of voting weight derived from a zatoshi balance. The\n+  conversion from zatoshi to ballots is defined in [Ballot Scaling].\n+\n+Vote Authority Note (VAN)\n+\n+: A commitment inserted into the Vote Commitment Tree that represents\n+  spendable voting authority. A VAN binds a voting hotkey, a ballot\n+  count, a voting round identifier, and a proposal authority bitmask.\n+\n+Vote Commitment (VC)\n+\n+: A commitment inserted into the Vote Commitment Tree that binds a\n+  voter's encrypted share distribution, proposal choice, and vote\n+  decision for a single proposal. A VC is created when a VAN is consumed\n+  to cast a vote.\n+\n+Vote Commitment Tree (VCT)\n+\n+: An append-only Poseidon Merkle tree maintained by the vote chain that\n+  stores both VANs and VCs as leaves. A separate VCT is maintained per\n+  voting round; trees from different rounds are fully isolated.\n+\n+Vote share\n+\n+: One of $N_s$ encrypted portions of a voter's ballot count within a\n+  Vote Commitment. Each share is submitted independently for\n+  homomorphic accumulation.\n+\n+Voting round\n+\n+: A bounded period during which a set of proposals are open for voting.\n+  Each round is identified by a unique $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ and\n+  is associated with a pool snapshot, an election authority public key,\n+  and a set of proposals.\n+\n+Election authority (EA)\n+\n+: The set of validators that collectively hold Shamir shares of the\n+  El Gamal secret key $\\mathsf{ea}\\_\\mathsf{sk}$ for a voting round.\n+\n+Submission server\n+\n+: An untrusted server to which a voter delegates the construction and\n+  submission of Vote Reveal Proofs.\n+\n+Governance hotkey\n+: An Orchard key hierarchy, distinct from the holder's spending key,\n+  that provides the key material for all voting operations after\n+  delegation. The hotkey is generated on a general-purpose device\n+  capable of ZKP construction.\n+\n+Governance nullifier\n+\n+: An alternate nullifier (as defined in [^balance-proof]) scoped to the\n+  governance domain, published during delegation to prevent\n+  double-delegation of the same Orchard note within a voting round.\n+\n+VAN nullifier\n+\n+: A nullifier derived from a VAN commitment and published when the VAN\n+  is consumed (to cast a vote or delegate), preventing double-spending\n+  of voting authority.\n+\n+Share nullifier\n+\n+: A nullifier derived from a vote commitment and share index, published\n+  when a share is revealed, preventing double-counting.\n+\n+\n+# Abstract\n+\n+This ZIP specifies a shielded voting protocol that allows holders of\n+Orchard notes to cast stake-weighted votes on proposals without revealing\n+their identity, individual balances, or vote allocations.\n+\n+The protocol proceeds in three proving phases. First, a *delegation\n+proof* (building on the Orchard Proof-of-Balance [^balance-proof])\n+converts proven Orchard balance into a Vote Authority Note on a\n+purpose-built vote chain. Second, a *vote proof* consumes a VAN to\n+produce a Vote Commitment containing $N_s$ El Gamal-encrypted shares of\n+the voter's ballot count, split across vote options. Third, a *vote\n+reveal proof*, constructed by an untrusted submission server, opens\n+individual encrypted shares for homomorphic accumulation, without\n+revealing which Vote Commitment the share originated from.\n+\n+After the voting window closes, anyone can publicly aggregate the\n+revealed El Gamal ciphertexts per proposal option. Validators holding\n+Shamir shares of the election authority key cooperate to produce partial\n+decryptions; the results are combined via Lagrange interpolation and\n+the aggregate total is publicly verified. Individual vote amounts are\n+never revealed.\n+\n+\n+# Motivation\n+\n+Stake-weighted voting in privacy-preserving systems faces a fundamental\n+tension: demonstrating voting power requires proving a balance, but\n+linking that balance to a vote destroys the privacy that shielded\n+transactions provide.\n+\n+This ZIP addresses that tension for Zcash's Orchard shielded pool. The\n+Orchard Proof-of-Balance [^balance-proof] provides the foundational\n+primitive, proving note ownership without revealing standard nullifiers.\n+This ZIP builds on that primitive to specify a complete voting protocol\n+with the following properties:\n+\n+- **Unlinkable delegation.** A holder delegates voting power to a\n+  locally-generated hotkey via a zero-knowledge proof. The delegation\n+  is unlinkable to the holder's on-chain identity.\n+- **Private vote splitting.** Votes are decomposed into El Gamal-\n+  encrypted shares submitted independently, preventing balance\n+  reconstruction via timing or amount analysis.\n+- **Homomorphic tallying.** Encrypted shares are accumulated on-chain\n+  via component-wise point addition. Only the aggregate total per\n+  proposal option is ever decrypted.\n+\n+The protocol is motivated by coinholder governance in the Zcash\n+ecosystem, where participants vote on proposals weighted by their ZEC\n+holdings. The same mechanism applies to any stake-weighted polling system\n+over an Orchard-like shielded pool.\n+\n+\n+# Privacy Implications\n+\n+**Unlinkability to on-chain identity.** The delegation phase moves\n+voting authority from the holder's Orchard spending key to an unlinkable\n+governance hotkey. All subsequent voting transactions use this hotkey.\n+An observer who sees both the governance nullifiers (published during\n+delegation) and the standard nullifiers (published when notes are later\n+spent on-chain) cannot link them without knowledge of $\\mathsf{nk}$.\n+This follows from the alternate nullifier unlinkability property\n+established in [^balance-proof].\n+\n+**Balance hiding via vote splitting.** A voter's total ballot count is\n+decomposed into $N_s$ shares encrypted under the election authority's\n+public key. Each share is submitted independently (potentially via\n+different submission servers at randomized delays), preventing an\n+observer from reconstructing the voter's total weight from individual\n+submissions.\n+\n+**Individual vote amounts hidden.** Each share is an El Gamal ciphertext\n+whose plaintext value is never revealed. Only the aggregate total per\n+(proposal, decision) pair is decrypted at tally time.\n+\n+**Vote commitment unlinkability.** The Vote Reveal Proof proves\n+that a revealed share belongs to some valid Vote Commitment in the VCT\n+without revealing which one. Blinded per-share commitments prevent\n+observers from recomputing $\\mathsf{shares}\\_\\mathsf{hash}$ from on-chain\n+ciphertexts and linking revealed shares back to a specific VC.\n+\n+**Trust assumptions.** After the key ceremony, no single party holds\n+$\\mathsf{ea}\\_\\mathsf{sk}$; each validator holds only a Shamir share. An\n+adversary must compromise at least $t$ validators (where\n+$t = \\lceil n/2 \\rceil + 1$) to reconstruct the key and decrypt\n+individual share ciphertexts. Even with access to the full key, privacy\n+against the EA relies on vote splitting: the EA would see encrypted\n+shares but cannot link them to specific voters or vote commitments.\n+Submission servers learn the encrypted share ciphertext, blind factor,\n+and blinded share commitments for each share they submit, along with\n+the proposal identifier and vote decision, but cannot decrypt plaintext\n+amounts or link shares to voter identities. The\n+primary trust requirement on submission servers is not leaking timing\n+metadata; using multiple independent servers with randomized delays\n+for submission mitigates this risk. See [^submission-server].\n+\n+**Non-membership tree queries.** Obtaining exclusion proofs for the\n+nullifier non-membership tree during delegation requires querying a data\n+source. Private information retrieval mitigates this leakage;\n+see [^pir-governance].\n+\n+\n+# Requirements\n+\n+- A holder's on-chain identity (spending key, standard nullifiers) is\n+  not linkable to their voting activity.\n+- The number of user-facing signatures required for delegation is\n+  minimized.\n+- No double-delegation for the same note within a voting round.\n+- No double voting for the same voting share within the same proposal.\n+- Individual vote amounts are not revealed at any point; only aggregate\n+  totals per (proposal, decision) pair are recoverable.\n+- The aggregate tally is publicly verifiable: any party can confirm the\n+  homomorphic accumulation.\n+- The delegation phase is compatible with hardware wallets that support\n+  only the standard Orchard PCZT [^pczt] signing flow, without\n+  requiring firmware changes specific to the voting protocol.\n+\n+\n+# Non-requirements\n+\n+- The consensus mechanism and operational parameters of the vote chain.\n+- The election authority key ceremony (generation, threshold sharing,\n+  and distribution of $\\mathsf{ea}\\_\\mathsf{sk}$ shares) is specified separately\n+  in [^ea-ceremony].\n+- The operational process for conducting a coinholder vote (validator\n+  setup, poll creation, deadlines) is out of scope; it is specified\n+  in [^voting-setup].\n+- Post-quantum security of the El Gamal encryption layer is out of\n+  scope.\n+- Privacy-preserving retrieval of nullifier non-membership proofs is\n+  specified separately in [^pir-governance].\n+\n+\n+# High-level summary\n+\n+This section is non-normative.\n+\n+The protocol proceeds in five phases within a voting round.\n+\n+**Phase 1: Delegation.** A holder proves ownership of unspent Orchard\n+notes at a pool snapshot (using the Claim circuit from [^balance-proof])\n+and delegates voting authority to a locally-generated governance hotkey.\n+The delegation produces a Vote Authority Note (VAN) that is inserted\n+into the Vote Commitment Tree on the vote chain. Governance nullifiers\n+are published to prevent double-delegation.\n+\n+**Phase 2: Voting.** The governance hotkey consumes a VAN by publishing\n+its VAN nullifier, and produces two new VCT leaves: a replacement VAN\n+with the voted proposal's authority bit cleared, and a Vote Commitment\n+containing $N_s$ El Gamal-encrypted shares of the voter's ballot count.\n+\n+**Phase 3: Share submission.** The voter sends each encrypted share as\n+an independent payload to one or more submission\n+servers [^submission-server]. Each payload includes a client-chosen\n+$\\mathsf{submit\\_at}$ timestamp and the data necessary for the server\n+to construct a Vote Reveal Proof. If the voter is casting near the end\n+of the voting window (within the last-moment buffer defined\n+in [^submission-server]), the voter places the full ballot count into a\n+single share and requests immediate submission.\n+\n+**Phase 4: Share reveal.** Each submission server constructs a Vote\n+Reveal Proof (proving the share belongs to a valid VC in the VCT without\n+revealing which one) and submits it to the vote chain at the\n+client-specified time. The chain accumulates the revealed El Gamal\n+ciphertexts homomorphically.\n+\n+**Phase 5: Tally.** After the voting window closes, at least $t$\n+validators produce partial decryptions of the aggregate ciphertext per\n+(proposal, decision) pair. The partial decryptions are stored on-chain\n+and combined via Lagrange interpolation to recover the total ballot\n+count (via the bounded discrete-log recovery procedure defined\n+in [^ea-ceremony]). Correctness is publicly\n+verifiable: anyone can recompute the Lagrange combination from the\n+on-chain partial decryptions.\n+\n+\n+# Specification\n+\n+## El Gamal Encryption on Pallas\n+\n+The protocol uses additively homomorphic El Gamal encryption over the\n+Pallas curve to encrypt vote share amounts. The scheme — including\n+setup, encryption, additive homomorphism, and decryption — is defined\n+in [^ea-ceremony]. This ZIP uses the notation established there: $G$ is\n+the Pallas $\\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}$\n+generator [^protocol-concretespendauthsig],\n+$\\mathsf{ea}\\_\\mathsf{pk} = [\\mathsf{ea}\\_\\mathsf{sk}]\\, G$ is the election\n+authority's public key for the current voting round, and a ciphertext is\n+a pair of Pallas points $(C_1, C_2)$ where\n+$\\mathsf{Enc}(v, r) = \\bigl([r]\\, G,\\; [v]\\, G + [r]\\, \\mathsf{ea}\\_\\mathsf{pk}\\bigr)$.\n+\n+\n+## Data Structures\n+\n+### Voting Round Identifier\n+\n+Each voting round is identified by a 32-byte\n+$\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$, derived\n+deterministically from the round's setup parameters by Poseidon\n+hashing. Validators and verifiers compute it from the same inputs\n+and check that it matches the on-chain identifier.\n+\n+$$\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}} = \\mathsf{Poseidon}\\bigl(\\mathsf{snapshot}\\_\\mathsf{height},\\ \\mathsf{bh}\\_\\mathsf{lo},\\ \\mathsf{bh}\\_\\mathsf{hi},\\ \\mathsf{ph}\\_\\mathsf{lo},\\ \\mathsf{ph}\\_\\mathsf{hi},\\ \\mathsf{vote}\\_\\mathsf{end}\\_\\mathsf{time},\\ \\mathsf{nullifier}\\_\\mathsf{imt}\\_\\mathsf{root},\\ \\mathsf{nc}\\_\\mathsf{root}\\bigr)$$\n+\n+The hash uses the $\\mathsf{ConstantLength}\\langle 8 \\rangle$ variant\n+of the Poseidon instantiation specified in [Poseidon Instantiation].\n+\n+where:\n+\n+- $\\mathsf{snapshot}\\_\\mathsf{height} \\in \\{ 0 .. 2^{64}-1 \\}$ — the\n+  Zcash mainnet block height of the chosen snapshot, encoded as a\n+  Pallas base field element.\n+- $\\mathsf{bh}\\_\\mathsf{lo}, \\mathsf{bh}\\_\\mathsf{hi} \\in \\{ 0 .. 2^{128}-1 \\}$\n+  — the low and high 128-bit halves of\n+  $\\mathsf{snapshot}\\_\\mathsf{blockhash}$ in little-endian byte\n+  order, each encoded as a Pallas base field element.\n+- $\\mathsf{ph}\\_\\mathsf{lo}, \\mathsf{ph}\\_\\mathsf{hi} \\in \\{ 0 .. 2^{128}-1 \\}$\n+  — the low and high 128-bit halves of $\\mathsf{proposals}\\_\\mathsf{hash}$\n+  in little-endian byte order.\n+- $\\mathsf{vote}\\_\\mathsf{end}\\_\\mathsf{time} \\in \\{ 0 .. 2^{64}-1 \\}$\n+  — Unix timestamp (seconds) after which votes are no longer\n+  accepted, encoded as a Pallas base field element.\n+- $\\mathsf{nullifier}\\_\\mathsf{imt}\\_\\mathsf{root} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$\n+  — root of the nullifier non-membership IMT at the snapshot\n+  height. MUST be a canonical Pallas base field element.\n+- $\\mathsf{nc}\\_\\mathsf{root} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  Orchard note commitment tree root at the snapshot height. MUST\n+  be a canonical Pallas base field element.\n+\n+$\\mathsf{snapshot}\\_\\mathsf{blockhash}$ and\n+$\\mathsf{proposals}\\_\\mathsf{hash}$ are split into two 128-bit\n+limbs because they are not necessarily canonical Pallas field\n+elements. $\\mathsf{nullifier}\\_\\mathsf{imt}\\_\\mathsf{root}$ and\n+$\\mathsf{nc}\\_\\mathsf{root}$ are themselves Poseidon-derived in\n+their respective trees and are therefore already canonical.\n+\n+### Vote Authority Note (VAN)\n+\n+A VAN represents spendable voting authority on the vote chain. Its\n+commitment is computed in two layers:\n+\n+$$\\mathsf{van}\\_\\mathsf{core} = \\mathsf{Poseidon}\\bigl(\\mathsf{DOMAIN}\\_\\mathsf{VAN}, \\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}}, \\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}}, \\mathsf{num}\\_\\mathsf{ballots}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{proposal}\\_\\mathsf{authority}\\bigr)$$\n+\n+$$\\mathsf{van} = \\mathsf{Poseidon}\\bigl(\\mathsf{van}\\_\\mathsf{core}, \\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}}\\bigr)$$\n+\n+The first layer binds the structural fields; the second layer blinds\n+the commitment with randomness. These are two separate Poseidon\n+invocations ($\\mathsf{ConstantLength}\\langle 6 \\rangle$ then\n+$\\mathsf{ConstantLength}\\langle 2 \\rangle$), not a single 7-input\n+sponge absorption.\n+\n+where:\n+\n+- $\\mathsf{DOMAIN}\\_\\mathsf{VAN} = 0$ — domain tag distinguishing VANs from VCs\n+  in the shared VCT.\n+- $\\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  $\\mathsf{Extract}\\_{\\mathbb{P}}$ of the diversified base of the governance\n+  hotkey address.\n+- $\\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  $\\mathsf{Extract}\\_{\\mathbb{P}}$ of the diversified transmission key of the\n+  governance hotkey address.\n+- $\\mathsf{num}\\_\\mathsf{ballots} \\in \\{1 \\ldots 2^{30}\\}$ — total voting\n+  weight in ballots.\n+- $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — scopes this VAN to a specific voting round.\n+- $\\mathsf{proposal}\\_\\mathsf{authority} \\in \\{0 \\ldots 2^{16}-1\\}$ — bitmask\n+  encoding which proposals this VAN is authorized to vote on.\n+  $\\mathsf{proposal}\\_\\mathsf{id}$ values 1–15 map to bits 1–15; bit 0 is\n+  reserved (see [Why Proposal Identifiers Start at 1]). Full authority\n+  is $2^{16} - 1 = 65535$.\n+- $\\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  commitment randomness.\n+\n+**VAN nullifier.** When a VAN is consumed (to vote or delegate), its\n+nullifier is:\n+\n+$$\\mathsf{van}\\_\\mathsf{nullifier} = \\mathsf{Poseidon}\\bigl(\\mathsf{vsk.nk}, \\mathsf{tag}_{\\mathsf{van}}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{van}\\bigr)$$\n+\n+where $\\mathsf{tag}_{\\mathsf{van}}$ is the field-element encoding of the domain\n+separator `\"vote authority spend\"` and $\\mathsf{vsk.nk}$ is the nullifier\n+deriving key from the governance hotkey's full viewing key.\n+\n+A VAN MUST be created during delegation (Phase 1) and\n+consumed during voting (Phase 2), which MUST produce a replacement VAN\n+with updated $\\mathsf{proposal}\\_\\mathsf{authority}$.\n+\n+The VAN model is\n+designed to support future extensions such as partial delegation\n+(splitting $\\mathsf{num}\\_\\mathsf{ballots}$ across multiple delegates),\n+but this ZIP specifies only the delegation and voting operations.\n+\n+### Vote Commitment (VC)\n+\n+A VC commits to a vote on a specific proposal:\n+\n+$$\\mathsf{vc} = \\mathsf{Poseidon}\\bigl(\\mathsf{DOMAIN}\\_\\mathsf{VC}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{shares}\\_\\mathsf{hash}, \\mathsf{proposal}\\_\\mathsf{id}, \\mathsf{vote}\\_\\mathsf{decision}\\bigr)$$\n+\n+where:\n+\n+- $\\mathsf{DOMAIN}\\_\\mathsf{VC} = 1$ — domain tag.\n+- $\\mathsf{shares}\\_\\mathsf{hash} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — a hash\n+  over blinded commitments to all $N_s$ encrypted shares (see\n+  [Shares Hash]).\n+- $\\mathsf{proposal}\\_\\mathsf{id} \\in \\{1 \\ldots 15\\}$ — which proposal this\n+  vote targets.\n+- $\\mathsf{vote}\\_\\mathsf{decision} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the\n+  voter's choice (0-indexed into the proposal's declared options).\n+\n+A VC MUST be created during voting (Phase 2) and opened during share reveal (Phase 4/5).\n+\n+The VC hash MUST be posted on-chain as a public input of\n+the Vote Proof and inserted into the VCT.\n+\n+Its preimage fields\n+($\\mathsf{shares}\\_\\mathsf{hash}$ and $\\mathsf{vote}\\_\\mathsf{decision}$)\n+MUST be private witnesses in that proof.\n+\n+During share reveal, the Vote\n+Reveal Proof MUST prove membership in the VCT without exposing which VC\n+is being opened.\n+\n+### Vote Share\n+\n+A vote share is one of $N_s = 16$ encrypted portions of a voter's ballot\n+count within a VC. The value 16 is chosen as a sufficiently high number\n+to ensure amount privacy through share decomposition. This ZIP\n+constrains only that the shares sum to $\\mathsf{num}\\_\\mathsf{ballots}$\n+and that each share is in $[0, 2^{30})$; the strategy for how the\n+client decomposes the ballot count across shares (e.g., uniform random\n+vs. powers-of-two denominations) affects temporal mixing effectiveness\n+and is specified in [^submission-server].\n+\n+For share index $i \\in \\{0 \\ldots N_s - 1\\}$:\n+\n+- $\\mathsf{v}\\_\\mathsf{i} \\in \\{0 \\ldots 2^{30} - 1\\}$ — plaintext share\n+  amount in ballots (private, never revealed).\n+- $r_i$ — El Gamal encryption randomness (Pallas scalar, private).\n+- $\\mathsf{blind}\\_\\mathsf{i} \\in \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — per-share\n+  blind factor for the blinded share commitment (independent of $r_i$).\n+- $\\mathsf{enc}\\_{\\mathsf{share}\\_\\mathsf{i}} = \\mathsf{Enc}(\\mathsf{v}\\_\\mathsf{i}, r_i) = (C_{1,i}, C_{2,i})$\n+  — El Gamal ciphertext.\n+\n+**Blinded share commitment:**\n+\n+$$\\mathsf{share}\\_{\\mathsf{comm}\\_\\mathsf{i}} = \\mathsf{Poseidon}(\\mathsf{blind}\\_\\mathsf{i}, C_{1,i,x}, C_{2,i,x}, C_{1,i,y}, C_{2,i,y})$$\n+\n+where $C_{1,i,x}$, $C_{2,i,x}$ denote the $x$-coordinates and\n+$C_{1,i,y}$, $C_{2,i,y}$ denote the $y$-coordinates of the ciphertext\n+points. See [Why Share Commitments Bind Full Curve Points].\n+\n+### Shares Hash\n+\n+The shares hash aggregates all $N_s$ blinded share commitments:\n+\n+$$\\mathsf{shares}\\_\\mathsf{hash} = \\mathsf{Poseidon}(\\mathsf{share}\\_{\\mathsf{comm}\\_\\mathsf{0}}, \\mathsf{share}\\_{\\mathsf{comm}\\_\\mathsf{1}}, \\ldots, \\mathsf{share}\\_{\\mathsf{comm}_{N_s - 1}})$$\n+\n+This is an $N_s$-input Poseidon sponge hash.\n+\n+### Share Nullifier\n+\n+When a share is revealed, its nullifier is:\n+\n+$$\\mathsf{share}\\_\\mathsf{nullifier} = \\mathsf{Poseidon}\\bigl(\\mathsf{tag}_{\\mathsf{share}}, \\mathsf{vc}, \\mathsf{share}\\_\\mathsf{index}, \\mathsf{blind}\\bigr)$$\n+\n+where $\\mathsf{tag}_{\\mathsf{share}}$ is the field-element encoding of\n+`\"share spend\"`, $\\mathsf{vc}$ is the vote commitment (private), and\n+$\\mathsf{blind}$ is the blind factor for the revealed share.\n+\n+The share\n+nullifier MUST be posted on-chain as a public input of the Vote Reveal\n+Proof and added to the share nullifier set to prevent double-counting.\n+\n+### Vote Commitment Tree\n+\n+The vote chain MUST maintain a separate VCT per voting round. Each\n+round's VCT MUST be an incremental Merkle tree [^protocol-merkletree]\n+of depth $\\mathsf{MerkleDepth}^{\\mathsf{vct}} = 24$ that stores both\n+VANs and VCs as leaves. Leaves, roots, and tree state from one round\n+MUST NOT carry over into another. The tree MUST use the same\n+append-only data structure as the Orchard note commitment tree, but\n+with Poseidon over the Pallas scalar field for internal node hashing\n+instead of Sinsemilla (see [Poseidon Instantiation]).\n+\n+Domain separation between VANs and VCs is achieved structurally: the\n+first Poseidon input is $\\mathsf{DOMAIN}\\_\\mathsf{VAN} = 0$ for VANs and\n+$\\mathsf{DOMAIN}\\_\\mathsf{VC} = 1$ for VCs, making it impossible for a valid VAN\n+preimage to produce the same hash as a valid VC preimage.\n+\n+Leaves MUST be inserted in transaction order: a delegation transaction\n+inserts one VAN; a vote transaction inserts both a new VAN and a VC.\n+\n+### Nullifier Sets\n+\n+The vote chain MUST maintain three disjoint nullifier sets:\n+\n+1. **Governance nullifiers**: prevent double-delegation of mainchain\n+   Orchard notes within a voting round.\n+2. **VAN nullifiers**: prevent double-spending of voting authority.\n+3. **Share nullifiers**: prevent double-counting of revealed shares.\n+\n+Each set SHOULD BE append-only within a voting round. The vote chain MUST reject any transaction that publishes a nullifier already present in the corresponding set.\n+\n+\n+The protocol defines three tags:\n+\n+| Tag constant | String | Length |\n+|---|---|---|\n+| $\\mathsf{tag}\\_{\\mathsf{gov}}$ | `\"governance authorization\"` | 24 bytes |\n+| $\\mathsf{tag}\\_{\\mathsf{van}}$ | `\"vote authority spend\"` | 20 bytes |\n+| $\\mathsf{tag}\\_{\\mathsf{share}}$ | `\"share spend\"` | 11 bytes |\n+\n+\n+## Governance Hotkey\n+\n+The governance hotkey is a separate Orchard key hierarchy generated on\n+a general-purpose device (e.g., a mobile phone). It is distinct from\n+the holder's Orchard spending key, which may reside on a hardware\n+wallet. The hotkey provides the key material for all voting operations\n+after delegation.\n+\n+A governance hotkey consists of:\n+\n+- A fresh spend-authorizing key $\\mathsf{vsk}$, from which\n+  $\\mathsf{vsk.ak} = [\\mathsf{vsk}]\\, G$ is derived.\n+- A nullifier deriving key $\\mathsf{vsk.nk}$, derived from the hotkey's\n+  spending key via the standard Orchard key hierarchy.\n+- A CommitIvk trapdoor $\\mathsf{rivk}\\_\\mathsf{v}$, derived from the\n+  hotkey's spending key.\n+- A diversified address at a chosen diversifier index, consisting of\n+  the diversified base point and the diversified transmission key point,\n+  where the transmission key satisfies\n+  $[\\mathsf{ivk}\\_\\mathsf{v}]\\, \\mathsf{g}\\_{\\mathsf{d}}$\n+  with $\\mathsf{ivk}\\_\\mathsf{v} = \\mathsf{CommitIvk}\\_{\\mathsf{rivk}\\_\\mathsf{v}}\\!\\bigl(\\mathsf{Extract}\\_{\\mathbb{P}}(\\mathsf{vsk.ak}),\\; \\mathsf{vsk.nk}\\bigr)$.\n+  The VAN commitment (see [Vote Authority Note (VAN)]) stores the\n+  $x$-coordinates $\\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}} = \\mathsf{Extract}\\_{\\mathbb{P}}(\\mathsf{g}\\_\\mathsf{d})$\n+  and $\\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}} = \\mathsf{Extract}\\_{\\mathbb{P}}(\\mathsf{pk}\\_\\mathsf{d})$;\n+  the full curve points are used in the diversified address integrity\n+  check (see [Vote Proof]).\n+\n+The Delegation Proof does not constrain the hotkey address to match the\n+holder's key; the output address is bound to the delegation\n+transitively through the VAN commitment and the rho binding (see\n+[Delegation Proof]), which the holder's hardware wallet authenticates\n+via the spend authorization signature.\n+\n+The hotkey MUST be derived deterministically from a seed\n+$\\mathsf{seed}\\_\\mathsf{v}$. The wallet MUST generate\n+$\\mathsf{seed}\\_\\mathsf{v}$ by creating a fresh BIP 39\n+mnemonic [^bip39], converting it to a 64-byte BIP 39 seed, and\n+storing the mnemonic in local secure storage (e.g., the platform\n+keychain). The mnemonic MUST be generated independently of the\n+holder's wallet mnemonic and MUST NOT be exported or backed up;\n+it is needed only for the duration of the voting round.\n+\n+The wallet computes\n+\n+$$\\mathsf{sk}\\_\\mathsf{v} = \\mathsf{Blake2b}\\text{-}512(\\texttt{\"ZcashVotingHotKy\"},\\; \\mathsf{seed}\\_\\mathsf{v})$$\n+\n+interpreted as a Pallas scalar via $\\mathsf{FromUniformBytes}$, and\n+then derives $\\mathsf{vsk.ak}$, $\\mathsf{vsk.nk}$, and\n+$\\mathsf{rivk}\\_\\mathsf{v}$ from $\\mathsf{sk}\\_\\mathsf{v}$ following\n+§ 4.2.3 'Orchard Key Components' [^protocol-orchardkeycomponents].\n+See [Why Deterministic Hotkey Derivation].\n+\n+### Per-Vote Secret Derivation\n+\n+The El Gamal encryption randomness $r_i$, per-share blind factors\n+$\\mathsf{blind}\\_\\mathsf{i}$, and spend authorization randomizer\n+$\\alpha_v$ used in the Vote Proof MUST be derived deterministically\n+from the hotkey seed. This ensures that all per-vote secrets can be\n+reconstructed from the BIP 39 mnemonic if the app is terminated\n+between delegation and voting.\n+\n+For a vote on proposal $\\mathsf{proposal}\\_\\mathsf{id}$ consuming\n+VAN commitment $\\mathsf{van}\\_\\mathsf{old}$ in voting round\n+$\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$, the wallet MUST\n+derive a 64-byte per-vote root:\n+\n+$$\\mathsf{vote}\\_\\mathsf{root} = \\mathsf{Blake2b}\\text{-}512\\bigl(\\texttt{\"ZcashVoteSecret\"},\\; \\mathsf{sk}\\_\\mathsf{v} \\| \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}} \\| \\mathsf{proposal}\\_\\mathsf{id} \\| \\mathsf{van}\\_\\mathsf{old}\\bigr)$$\n+\n+where $\\|$ denotes concatenation of canonical little-endian encodings\n+(32 bytes for field elements, 1 byte for\n+$\\mathsf{proposal}\\_\\mathsf{id}$).\n+\n+From $\\mathsf{vote}\\_\\mathsf{root}$, the wallet MUST derive each\n+per-share secret:\n+\n+$$r_i = \\mathsf{FromUniformBytes}\\!\\bigl(\\mathsf{Blake2b}\\text{-}512(\\texttt{\"ZcashVoteElGam\"},\\; \\mathsf{vote}\\_\\mathsf{root} \\| i)\\bigr)$$\n+\n+$$\\mathsf{blind}\\_\\mathsf{i} = \\mathsf{FromUniformBytes}\\!\\bigl(\\mathsf{Blake2b}\\text{-}512(\\texttt{\"ZcashVoteBlind\"},\\; \\mathsf{vote}\\_\\mathsf{root} \\| i)\\bigr)$$\n+\n+where $i$ is the share index encoded as a single byte. The spend\n+authorization randomizer is:\n+\n+$$\\alpha_v = \\mathsf{FromUniformBytes}\\!\\bigl(\\mathsf{Blake2b}\\text{-}512(\\texttt{\"ZcashVoteAlpha\"},\\; \\mathsf{vote}\\_\\mathsf{root})\\bigr)$$\n+\n+All Blake2b invocations above use the first argument as the\n+personalization parameter (truncated or padded to 16 bytes per the\n+Blake2b spec) and the second argument as the input message.\n+$\\mathsf{FromUniformBytes}$ interprets a 64-byte input as a Pallas\n+scalar. [^protocol-pallasandvesta]\n+\n+See [Why Deterministic Hotkey Derivation].\n+\n+\n+## Ballot Scaling\n+\n+Orchard note values are denominated in zatoshi. The Delegation Proof\n+MUST convert zatoshi to ballots:\n+\n+$$\\mathsf{num}\\_\\mathsf{ballots} = \\left\\lfloor \\frac{\\sum v_i}{12{,}500{,}000} \\right\\rfloor$$\n+\n+where $v_i$ are the values of the delegated Orchard notes. One ballot\n+equals 0.125 ZEC.\n+\n+The prover MUST witness $\\mathsf{num}\\_\\mathsf{ballots}$ and a\n+remainder $r$. The Delegation Proof circuit MUST enforce:\n+\n+1. $\\mathsf{num}\\_\\mathsf{ballots} \\times 12{,}500{,}000 + r = \\sum v_i$\n+2. $0 \\leq r < 2^{24}$ (see [Why a 24-bit Remainder Range])\n+3. $\\mathsf{num}\\_\\mathsf{ballots} \\geq 1$\n+4. $\\mathsf{num}\\_\\mathsf{ballots} \\leq 2^{30}$\n+\n+The 30-bit upper bound accommodates up to $\\approx 134$ million ZEC,\n+well above the 21 million ZEC supply cap. The minimum of 1 ballot\n+ensures that holdings below 0.125 ZEC MUST NOT produce voting\n+authority.\n+\n+\n+## Delegation Phase\n+\n+### Delegation Proof\n+\n+The Delegation Proof establishes that a holder owns unspent Orchard\n+notes at a pool snapshot and converts the proven balance into a VAN on\n+the vote chain.\n+\n+The Delegation Proof circuit MUST enforce the same per-note ownership\n+checks (note commitment integrity, Merkle path validity, nullifier\n+derivation, diversified address integrity, and nullifier\n+non-membership) as the Batched Claim circuit defined\n+in [^balance-proof]. This section specifies only the conditions that\n+extend beyond the Balance Proof.\n+\n+#### Public Inputs\n+\n+Given a primary input:\n+\n+- $\\mathsf{signed}\\_{\\mathsf{note}\\_\\mathsf{nullifier}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  nullifier of the dummy signed note (for spend authorization binding).\n+- $\\mathsf{rk} ⦂ \\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}\\mathsf{.Public}$ —\n+  randomized spend authorization verification key.\n+- $\\mathsf{rt}^{\\mathsf{cm}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — Orchard note\n+  commitment tree root at the snapshot height.\n+- $\\mathsf{rt}^{\\mathsf{excl}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — nullifier\n+  non-membership tree root at the snapshot height.\n+- $\\mathsf{van} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the initial VAN\n+  commitment.\n+- $\\mathsf{gov}\\_{\\mathsf{null}\\_\\mathsf{1}}, \\ldots, \\mathsf{gov}\\_{\\mathsf{null}\\_\\mathsf{5}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  governance nullifiers for each note slot.\n+- $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$\n+- $\\mathsf{cmx}\\_\\mathsf{new} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — output note\n+  commitment to the governance hotkey address (PCZT scaffolding; not\n+  inserted into any on-chain tree).\n+\n+#### Auxiliary Inputs\n+\n+The prover knows, in addition to the per-note witnesses defined\n+in [^balance-proof]:\n+\n+- $\\mathsf{vpk} = (\\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}}, \\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}})$ —\n+  governance hotkey diversified address.\n+- $\\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}}$ — VAN commitment randomness.\n+- Signed note data: $\\mathsf{g}\\_\\mathsf{d}^{\\mathsf{signed}}, \\mathsf{pk}\\_\\mathsf{d}^{\\mathsf{signed}},\n+  \\text{ρ}^{\\mathsf{signed}}, \\text{ψ}^{\\mathsf{signed}},\n+  \\mathsf{rcm}^{\\mathsf{signed}}, \\mathsf{cm}^{\\mathsf{signed}}$ (value = 0).\n+- Output note data (PCZT scaffolding): $\\mathsf{g}\\_\\mathsf{d}^{\\mathsf{new}}, \\mathsf{pk}\\_\\mathsf{d}^{\\mathsf{new}},\n+  \\mathsf{v}^{\\mathsf{new}}, \\text{ρ}^{\\mathsf{new}},\n+  \\text{ψ}^{\\mathsf{new}}, \\mathsf{rcm}^{\\mathsf{new}}$ (address = hotkey).\n+  $\\mathsf{v}^{\\mathsf{new}}$ is not constrained by the circuit.\n+\n+#### Conditions\n+\n+**Per-note conditions (5 note slots, with padding).** For each note\n+$i \\in \\{1 \\ldots 5\\}$, the circuit MUST enforce the following\n+conditions from the Batched Claim circuit [^balance-proof]:\n+\n+- Note commitment integrity.\n+- Merkle path validity in $\\mathsf{rt}^{\\mathsf{cm}}$ (skipped for padded\n+  notes).\n+- Diversified address integrity (same $\\mathsf{ivk}$ MUST own all notes).\n+- Standard nullifier derivation (kept private).\n+- Nullifier non-membership in $\\mathsf{rt}^{\\mathsf{excl}}$ (skipped for padded\n+  notes).\n+- Padded notes MUST have value 0.\n+\n+**Governance nullifier derivation.** For each real note $i$, the\n+circuit MUST enforce:\n+\n+$$\\mathsf{gov}\\_{\\mathsf{null}\\_\\mathsf{i}} = \\mathsf{Poseidon}\\bigl(\\mathsf{nk}, \\mathsf{tag}_{\\mathsf{gov}}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{nf}^{\\mathsf{old}}_i\\bigr)$$\n+\n+where $\\mathsf{tag}_{\\mathsf{gov}}$ is the field-element encoding of\n+\"governance authorization\" and $\\mathsf{nf}^{\\mathsf{old}}_i$ is the note's\n+standard nullifier (computed in-circuit but never revealed). This is an\n+instantiation of the alternate nullifier derivation defined\n+in [^balance-proof], with $\\mathsf{tag} = \\mathsf{tag}_{\\mathsf{gov}}$ and\n+$\\mathsf{dom} = \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$.\n+\n+**Signed note integrity.** The signed note MUST be a dummy note with\n+value 0. The circuit MUST enforce that\n+$\\mathsf{cm}^{\\mathsf{signed}}$ is correctly constructed and that\n+$\\mathsf{signed}\\_{\\mathsf{note}\\_\\mathsf{nullifier}}$ is correctly derived from it.\n+\n+**Rho binding.** The circuit MUST enforce that\n+$\\text{ρ}^{\\mathsf{signed}}$ is deterministically bound to the\n+delegation context:\n+\n+$$\\text{ρ}^{\\mathsf{signed}} = \\mathsf{Poseidon}\\bigl(\\mathsf{cmx}\\_\\mathsf{1}, \\mathsf{cmx}\\_\\mathsf{2}, \\mathsf{cmx}\\_\\mathsf{3}, \\mathsf{cmx}\\_\\mathsf{4}, \\mathsf{cmx}\\_\\mathsf{5}, \\mathsf{van}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}\\bigr)$$\n+\n+This makes the spend authorization signature non-replayable and scoped\n+to the exact delegation context.\n+\n+**Spend authority.** The circuit MUST enforce that $\\mathsf{rk} = \\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}\\mathsf{.RandomizePublic}(\\alpha, \\mathsf{ak}^{\\mathbb{P}})$.\n+\n+**Diversified address integrity for signed note.** The circuit MUST\n+enforce that the signed note's address belongs to\n+$(\\mathsf{ak}, \\mathsf{nk})$.\n+\n+**Output note commitment.** The circuit MUST enforce that\n+$\\mathsf{cmx}\\_\\mathsf{new}$ is a correctly constructed note commitment\n+to an output note addressed to the governance hotkey. The output note\n+exists solely to satisfy the PCZT signing flow: a standard Orchard\n+Action requires exactly one spend and one output, so the governance\n+PCZT includes a minimal output to the hotkey address.\n+\n+The vote chain SHOULD NOT insert $\\mathsf{cmx}\\_\\mathsf{new}$ into any commitment tree\n+or use it beyond proof verification; it serves only to make the\n+hardware wallet's signed sighash commit to a complete Action structure.\n+\n+See [^balance-proof] for the PCZT construction details and\n+[Why a Dummy Signed Note] for the design rationale.\n+\n+**VAN integrity.** The circuit MUST enforce that the public VAN\n+commitment matches the claimed governance hotkey, ballot count, round,\n+and full proposal authority (using the two-layer construction defined\n+in [Vote Authority Note (VAN)]):\n+\n+$$\\mathsf{van}\\_\\mathsf{core} = \\mathsf{Poseidon}\\bigl(\\mathsf{DOMAIN}\\_\\mathsf{VAN}, \\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}}, \\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}}, \\mathsf{num}\\_\\mathsf{ballots}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{MAX}\\_{\\mathsf{PROPOSAL}\\_\\mathsf{AUTHORITY}}\\bigr)$$\n+\n+$$\\mathsf{van} = \\mathsf{Poseidon}\\bigl(\\mathsf{van}\\_\\mathsf{core}, \\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}}\\bigr)$$\n+\n+where $\\mathsf{MAX}\\_{\\mathsf{PROPOSAL}\\_\\mathsf{AUTHORITY}} = 2^{16} - 1$.\n+\n+**Ballot scaling.** The circuit MUST enforce that\n+$\\mathsf{num}\\_\\mathsf{ballots} = \\lfloor \\sum v_i / 12{,}500{,}000 \\rfloor$\n+with $\\mathsf{num}\\_\\mathsf{ballots} \\geq 1$, as defined in [Ballot Scaling].\n+\n+#### Out-of-Circuit Verification\n+\n+A verifier that receives a Delegation Proof $\\pi$ together with a spend\n+authorization signature $\\sigma$ MUST perform the following checks:\n+\n+1. Verify $\\pi$ against the public inputs.\n+2. Verify that $\\mathsf{sighash}\\_\\mathsf{del}$ is exactly 32 bytes.\n+3. Verify $\\sigma$ as a valid $\\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}$\n+   signature on $\\mathsf{sighash}\\_\\mathsf{del}$, under $\\mathsf{rk}$.\n+4. Verify that $\\mathsf{rt}^{\\mathsf{cm}}$ and $\\mathsf{rt}^{\\mathsf{excl}}$ correspond\n+   to the published pool snapshot for $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$.\n+5. Verify that no $\\mathsf{gov}\\_{\\mathsf{null}\\_\\mathsf{i}}$ appears in the governance\n+   nullifier set. If any does, reject as a double-delegation.\n+6. Verify that $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ matches an active round.\n+7. Insert $\\mathsf{van}$ into the VCT.\n+8. Add all $\\mathsf{gov}\\_{\\mathsf{null}\\_\\mathsf{i}}$ to the governance nullifier set.\n+\n+### Delegation Sighash\n+\n+The delegation sighash $\\mathsf{sighash}\\_\\mathsf{del}$ is a\n+client-provided 32-byte value included in the delegation message. For\n+hardware wallet flows, the client computes it as the ZIP 244 [^zip-244]\n+shielded transaction sighash of a governance PCZT [^pczt]; the\n+construction of this PCZT is specified\n+in [^balance-proof]. For software wallets, the client signs the\n+sighash directly without PCZT construction.\n+\n+The vote chain verifies the\n+$\\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}$ against this\n+client-provided sighash without recomputing it. The sighash does not\n+need to be independently reconstructible by the vote chain because the\n+Delegation Proof provides the governance data binding: the ZKP proves\n+that $\\mathsf{rk}$ is a valid rerandomization of the holder's spend\n+authorization key, that the holder owns the claimed notes, and that the\n+VAN commitment is correctly constructed. An attacker who substitutes a\n+different sighash cannot produce a valid signature under\n+$\\mathsf{rk}$ without knowledge of the holder's spending key.\n+\n+### Delegation Message\n+\n+A delegation transaction submitted to the vote chain MUST contain:\n+\n+| Field | Type | Description |\n+|---|---|---|\n+| $\\pi\\_\\mathsf{del}$ | Proof | The Delegation Proof |\n+| $\\sigma\\_\\mathsf{del}$ | Signature | SpendAuthSig under $\\mathsf{rk}$ |\n+| $\\mathsf{sighash}\\_\\mathsf{del}$ | 32 bytes | Client-computed sighash (see [Delegation Sighash]) |\n+| $\\mathsf{signed}\\_{\\mathsf{note}\\_\\mathsf{nullifier}}$ | Pallas scalar | Dummy note nullifier |\n+| $\\mathsf{rk}$ | Pallas point | Randomized verification key |\n+| $\\mathsf{rt}^{\\mathsf{cm}}$ | Pallas scalar | Note commitment tree root |\n+| $\\mathsf{rt}^{\\mathsf{excl}}$ | Pallas scalar | Non-membership tree root |\n+| $\\mathsf{van}$ | Pallas scalar | VAN commitment |\n+| $\\mathsf{gov}\\_{\\mathsf{null}\\_1} \\ldots \\mathsf{gov}\\_{\\mathsf{null}\\_5}$ | Pallas scalar each | Governance nullifiers |\n+| $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ | Pallas scalar | Round identifier |\n+| $\\mathsf{cmx}\\_\\mathsf{new}$ | Pallas scalar | Output note commitment |\n+\n+\n+## Vote Phase\n+\n+### Vote Proof\n+\n+The Vote Proof demonstrates that a holder of a valid VAN is casting a\n+vote: consuming the old VAN, producing a new VAN with decremented\n+proposal authority, and constructing a Vote Commitment that binds $N_s$\n+El Gamal-encrypted shares to the chosen proposal and decision. The\n+Vote Proof circuit MUST enforce all conditions specified below.\n+\n+#### Public Inputs\n+\n+Given a primary input:\n+\n+- $\\mathsf{van}\\_\\mathsf{nullifier} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  nullifier of the old VAN (prevents double-voting).\n+- $\\mathsf{r}\\_\\mathsf{vpk} ⦂ \\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}\\mathsf{.Public}$ —\n+  randomized voting public key.\n+- $\\mathsf{van}\\_{\\mathsf{new}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the new VAN\n+  commitment with decremented proposal authority.\n+- $\\mathsf{vc} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the vote commitment.\n+- $\\mathsf{rt}^{\\mathsf{vct}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — root of the\n+  Vote Commitment Tree.\n+- $\\mathsf{anchor}\\_\\mathsf{height} ⦂ \\mathbb{N}$ — VCT snapshot height.\n+- $\\mathsf{proposal}\\_\\mathsf{id} ⦂ \\{1 \\ldots 15\\}$ — which proposal.\n+- $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$\n+- $\\mathsf{ea}\\_\\mathsf{pk} ⦂ \\mathbb{P}^*$ — election authority public key\n+  (x and y coordinates).\n+\n+#### Auxiliary Inputs\n+\n+The prover knows:\n+\n+- $\\mathsf{vote}\\_\\mathsf{decision} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the voter's choice.\n+- $\\mathsf{vsk.ak} ⦂ \\mathbb{P}^*$ — voting spend authorization\n+  validating key.\n+- $\\mathsf{vsk.nk} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — nullifier\n+  deriving key from the governance hotkey's full viewing key.\n+- $\\mathsf{rivk}\\_\\mathsf{v} ⦂ \\mathsf{Commit}^{\\mathsf{ivk}}\\mathsf{.Trapdoor}$ —\n+  CommitIvk randomness for the voting key.\n+- $\\alpha_v$ — spend authorization randomizer for the voting hotkey.\n+- $\\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}} ⦂ \\mathbb{P}^*$ — diversified base point from the\n+  VAN (full point; $\\mathsf{Extract}\\_{\\mathbb{P}}$ applied for VAN integrity hash).\n+- $\\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}} ⦂ \\mathbb{P}^*$ — diversified transmission\n+  key point from the VAN (full point; $\\mathsf{Extract}\\_{\\mathbb{P}}$ applied for\n+  VAN integrity hash).\n+- $\\mathsf{num}\\_\\mathsf{ballots}$ — total voting weight in ballots.\n+- $\\mathsf{proposal}\\_{\\mathsf{authority}\\_\\mathsf{old}}$,\n+  $\\mathsf{proposal}\\_{\\mathsf{authority}\\_\\mathsf{new}}$ — old and new bitmasks.\n+- $\\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}}$ — VAN commitment randomness (shared\n+  between old and new VAN).\n+- $\\mathsf{path}^{\\mathsf{vct}}, \\mathsf{pos}^{\\mathsf{vct}}$ — Merkle proof for the\n+  old VAN in the VCT.\n+- $\\mathsf{van}_{\\mathsf{old}}$ — old VAN commitment.\n+- $\\mathsf{v}\\_\\mathsf{0}, \\ldots, \\mathsf{v}_{N_s - 1}$ — plaintext share values.\n+- $r_0, \\ldots, r_{N_s - 1}$ — El Gamal encryption randomness per share.\n+- $\\mathsf{blind}\\_\\mathsf{0}, \\ldots, \\mathsf{blind}_{N_s - 1}$ — per-share blind\n+  factors.\n+\n+#### Conditions\n+\n+##### VAN Ownership and Spending\n+\n+**Condition 1: Merkle tree membership.** The circuit MUST enforce that\n+the old VAN exists in the VCT:\n+$(\\mathsf{path}^{\\mathsf{vct}}, \\mathsf{pos}^{\\mathsf{vct}})$ MUST be\n+a valid Merkle path of depth $\\mathsf{MerkleDepth}^{\\mathsf{vct}}$\n+from $\\mathsf{van}_{\\mathsf{old}}$ to the anchor\n+$\\mathsf{rt}^{\\mathsf{vct}}$, using Poseidon for internal node hashing.\n+\n+**Condition 2: Old VAN integrity.** The circuit MUST enforce that\n+the old VAN commitment matches the claimed fields (using the two-layer\n+construction defined in [Vote Authority Note (VAN)]). The\n+$\\mathsf{vpk}$ values in the Poseidon input are x-coordinates, i.e.,\n+$\\mathsf{Extract}\\_{\\mathbb{P}}$ of the corresponding full points:\n+\n+$$\\mathsf{van}\\_{\\mathsf{core}\\_\\mathsf{old}} = \\mathsf{Poseidon}\\bigl(\\mathsf{DOMAIN}\\_\\mathsf{VAN}, \\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}}, \\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}}, \\mathsf{num}\\_\\mathsf{ballots}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{proposal}\\_{\\mathsf{authority}\\_\\mathsf{old}}\\bigr)$$\n+\n+$$\\mathsf{van}\\_\\mathsf{old} = \\mathsf{Poseidon}\\bigl(\\mathsf{van}\\_{\\mathsf{core}\\_\\mathsf{old}}, \\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}}\\bigr)$$\n+\n+**Condition 3: Diversified address integrity.** The circuit MUST\n+enforce that the VAN's address belongs to the voting key:\n+\n+$$\\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}} = [\\mathsf{ivk}\\_\\mathsf{v}]\\, \\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}}$$\n+\n+where $\\mathsf{ivk}\\_\\mathsf{v} = \\mathsf{CommitIvk}_{\\mathsf{rivk}\\_\\mathsf{v}}\\!\\bigl(\\mathsf{Extract}_{\\mathbb{P}}(\\mathsf{vsk.ak}),\\; \\mathsf{vsk.nk}\\bigr)$ [^protocol-concretecommitivk].\n+\n+**Condition 4: Spend authority.** The circuit MUST enforce that the\n+randomized voting public key is a valid rerandomization:\n+\n+$$\\mathsf{r}\\_\\mathsf{vpk} = \\mathsf{vsk.ak} + [\\alpha_v]\\, G$$\n+\n+**Condition 5: VAN nullifier.** The circuit MUST enforce that the\n+public $\\mathsf{van}\\_\\mathsf{nullifier}$ is correctly derived:\n+\n+$$\\mathsf{van}\\_\\mathsf{nullifier} = \\mathsf{Poseidon}\\bigl(\\mathsf{vsk.nk}, \\mathsf{tag}_{\\mathsf{van}}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{van}_{\\mathsf{old}}\\bigr)$$\n+\n+where $\\mathsf{tag}_{\\mathsf{van}}$ is the field-element encoding of\n+`\"vote authority spend\"`.\n+\n+See [Why VAN Nullifier Domain Separation].\n+\n+##### New VAN Construction\n+\n+**Condition 6: Proposal authority decrement.** The circuit MUST\n+enforce that bit $\\mathsf{proposal}\\_\\mathsf{id}$ is cleared in the\n+authority bitmask:\n+\n+- $\\mathsf{proposal}\\_{\\mathsf{authority}\\_\\mathsf{old}}$ MUST be decomposed into\n+  16 boolean wires $b_0, \\ldots, b_{15}$ that recompose to the original\n+  value.\n+- The bit at position $\\mathsf{proposal}\\_\\mathsf{id}$ MUST be 1 (the voter\n+  has authority for this proposal).\n+- $\\mathsf{proposal}\\_{\\mathsf{authority}\\_\\mathsf{new}}$ MUST be the recomposition\n+  with bit $\\mathsf{proposal}\\_\\mathsf{id}$ cleared; all other bits MUST be\n+  unchanged.\n+\n+**Condition 7: New VAN integrity.** The circuit MUST enforce that the\n+new VAN is correctly constructed (using the two-layer construction\n+defined in [Vote Authority Note (VAN)]):\n+\n+$$\\mathsf{van}\\_{\\mathsf{core}\\_\\mathsf{new}} = \\mathsf{Poseidon}\\bigl(\\mathsf{DOMAIN}\\_\\mathsf{VAN}, \\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}}, \\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}}, \\mathsf{num}\\_\\mathsf{ballots}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{proposal}\\_{\\mathsf{authority}\\_\\mathsf{new}}\\bigr)$$\n+\n+$$\\mathsf{van}\\_\\mathsf{new} = \\mathsf{Poseidon}\\bigl(\\mathsf{van}\\_{\\mathsf{core}\\_\\mathsf{new}}, \\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}}\\bigr)$$\n+\n+The new VAN MUST reuse the old VAN's diversified address and commitment\n+randomness; only $\\mathsf{proposal}\\_\\mathsf{authority}$ changes.\n+\n+##### Vote Commitment Construction\n+\n+**Condition 8: Shares sum correctness.** The circuit MUST enforce:\n+\n+$$\\sum_{i=0}^{N_s - 1} \\mathsf{v}\\_\\mathsf{i} = \\mathsf{num}\\_\\mathsf{ballots}$$\n+\n+**Condition 9: Shares range check.** The circuit MUST enforce that\n+each share is bounded:\n+\n+$$0 \\leq \\mathsf{v}\\_\\mathsf{i} < 2^{30} \\quad \\text{for each } i \\in \\{0 \\ldots N_s - 1\\}$$\n+\n+This bound is critical for two reasons: (1) it ensures the base-field\n+share sum and the scalar-field El Gamal encoding agree (no modular\n+reduction in either field), and (2) it keeps the aggregate discrete log\n+small enough for efficient recovery at tally time (see [^ea-ceremony]).\n+\n+**Condition 10: Shares hash integrity.** The circuit MUST enforce that\n+the blinded share commitments and their aggregate hash are correctly\n+computed:\n+\n+$$\\mathsf{share}\\_{\\mathsf{comm}\\_\\mathsf{i}} = \\mathsf{Poseidon}\\bigl(\\mathsf{blind}\\_\\mathsf{i}, C_{1,i,x}, C_{2,i,x}, C_{1,i,y}, C_{2,i,y}\\bigr) \\quad \\text{for each } i$$\n+\n+$$\\mathsf{shares}\\_\\mathsf{hash} = \\mathsf{Poseidon}\\bigl(\\mathsf{share}\\_{\\mathsf{comm}\\_\\mathsf{0}}, \\ldots, \\mathsf{share}\\_{\\mathsf{comm}_{N_s - 1}}\\bigr)$$\n+\n+**Condition 11: El Gamal encryption integrity.** The circuit MUST\n+enforce that each ciphertext is a valid encryption of its share under\n+$\\mathsf{ea}\\_\\mathsf{pk}$:\n+\n+$$C_{1,i} = [r_i]\\, G$$\n+$$C_{2,i} = [\\mathsf{v}\\_\\mathsf{i}]\\, G + [r_i]\\, \\mathsf{ea}\\_\\mathsf{pk}$$\n+\n+The circuit MUST constrain equality on both the $x$- and\n+$y$-coordinates of the computed and witnessed ciphertext points. The\n+$y$-coordinates are needed because they appear in the share commitment\n+(condition 10); the ECC gadget already produces full curve points, so\n+no additional decomposition cost is incurred.\n+See [Why Share Commitments Bind Full Curve Points].\n+\n+**Condition 12: Vote commitment integrity.** The circuit MUST enforce\n+that the public vote commitment matches the private vote details:\n+\n+$$\\mathsf{vc} = \\mathsf{Poseidon}\\bigl(\\mathsf{DOMAIN}\\_\\mathsf{VC}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{shares}\\_\\mathsf{hash}, \\mathsf{proposal}\\_\\mathsf{id}, \\mathsf{vote}\\_\\mathsf{decision}\\bigr)$$\n+\n+#### Out-of-Circuit Verification\n+\n+A verifier that receives a Vote Proof $\\pi$ together with a vote\n+spend authorization signature $\\sigma$ MUST perform the following\n+checks:\n+\n+1. Verify $\\pi$ against the public inputs.\n+2. Compute the vote sighash from the message fields\n+   (see [Vote Sighash]).\n+3. Verify $\\sigma$ as a valid $\\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}$\n+   signature on the vote sighash, under $\\mathsf{r}\\_\\mathsf{vpk}$.\n+4. Verify that $\\mathsf{van}\\_\\mathsf{nullifier}$ does not appear in the VAN\n+   nullifier set. If it does, reject as double-voting.\n+5. Verify that $\\mathsf{anchor}\\_\\mathsf{height}$ refers to a VCT state\n+   within the current voting round and that $\\mathsf{rt}^{\\mathsf{vct}}$\n+   matches the VCT root at that height.\n+6. Verify that $\\mathsf{proposal}\\_\\mathsf{id}$ is valid for the current round\n+   and within the voting window.\n+7. Verify that $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ matches an active round.\n+8. Verify that $\\mathsf{ea}\\_\\mathsf{pk}$ matches the published election\n+   authority public key for this round.\n+9. Insert $\\mathsf{van}_{\\mathsf{new}}$ and $\\mathsf{vc}$ into the VCT.\n+10. Add $\\mathsf{van}\\_\\mathsf{nullifier}$ to the VAN nullifier set.\n+\n+Note: $\\mathsf{vote}\\_\\mathsf{decision}$ is a private witness in the\n+Vote Proof and MUST NOT be validated out-of-circuit at this stage.\n+\n+It is validated when the share is revealed:\n+the Vote Reveal Proof makes\n+$\\mathsf{vote}\\_\\mathsf{decision}$ a public input, and the vote chain\n+checks its validity at that point (see [Vote Reveal Proof]\n+out-of-circuit step 6).\n+\n+### Vote Sighash\n+\n+The vote sighash is computed by the vote chain from the vote message\n+fields. Unlike the delegation sighash, it is not client-provided;\n+the governance hotkey is software-controlled and signs the same\n+chain-computable digest.\n+\n+The vote sighash is $\\mathsf{BLAKE2b\\text{-}256}(\\mathsf{vote}\\_\\mathsf{payload})$\n+using an unkeyed BLAKE2b-256 hash (no personalization parameter).\n+$\\mathsf{vote}\\_\\mathsf{payload}$ is the concatenation of a domain\n+prefix followed by the message fields, each zero-padded to 32 bytes:\n+\n+| Component | Encoding | Size |\n+|---|---|---|\n+| `\"SVOTE_CAST_VOTE_SIGHASH_V0\"` | raw ASCII bytes | 26 |\n+| $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ | field element, zero-padded to 32 | 32 |\n+| $\\mathsf{r}\\_\\mathsf{vpk}$ | compressed point, zero-padded to 32 | 32 |\n+| $\\mathsf{van}\\_\\mathsf{nullifier}$ | field element, zero-padded to 32 | 32 |\n+| $\\mathsf{van}\\_{\\mathsf{new}}$ | field element, zero-padded to 32 | 32 |\n+| $\\mathsf{vc}$ | field element, zero-padded to 32 | 32 |\n+| $\\mathsf{proposal}\\_\\mathsf{id}$ | 4-byte LE integer, zero-padded to 32 | 32 |\n+| $\\mathsf{anchor}\\_\\mathsf{height}$ | 8-byte LE integer, zero-padded to 32 | 32 |\n+\n+Total payload: 250 bytes. Pallas field elements and compressed points\n+use their canonical little-endian\n+encoding [^protocol-pallasandvesta], right-padded with zero bytes to\n+fill 32 bytes when shorter. Integer fields are encoded as unsigned\n+little-endian and right-padded with zero bytes to 32 bytes.\n+\n+The voter's client MUST compute the same digest and sign it with\n+the governance hotkey's spend-authorizing key (rerandomized by\n+$\\alpha_v$) before submitting the vote message.\n+\n+### Vote Message\n+\n+A vote transaction submitted to the vote chain MUST contain:\n+\n+| Field | Type | Description |\n+|---|---|---|\n+| $\\pi_{\\text{vote}}$ | Proof | The Vote Proof |\n+| $\\sigma_{\\text{vote}}$ | Signature | SpendAuthSig under $\\mathsf{r}_{\\mathsf{vpk}}$ |\n+| $\\mathsf{van}\\_\\mathsf{nullifier}$ | Pallas scalar | Old VAN nullifier |\n+| $\\mathsf{r}_{\\mathsf{vpk}}$ | Pallas point | Randomized voting public key |\n+| $\\mathsf{van}_{\\mathsf{new}}$ | Pallas scalar | New VAN commitment |\n+| $\\mathsf{vc}$ | Pallas scalar | Vote commitment |\n+| $\\mathsf{rt}^{\\mathsf{vct}}$ | Pallas scalar | VCT root |\n+| $\\mathsf{anchor}\\_\\mathsf{height}$ | integer | VCT anchor height |\n+| $\\mathsf{proposal}\\_\\mathsf{id}$ | $\\{1 \\ldots 15\\}$ | Proposal identifier |\n+| $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ | Pallas scalar | Round identifier |\n+| $\\mathsf{ea}_{\\mathsf{pk}}$ | Pallas point | EA public key |\n+\n+\n+## Share Reveal Phase\n+\n+### Vote Reveal Proof\n+\n+The Vote Reveal Proof opens a single encrypted share from a Vote\n+Commitment, revealing the El Gamal ciphertext for homomorphic\n+accumulation, without revealing the plaintext amount or which Vote\n+Commitment the share came from. This proof is constructed by the\n+submission server, not the voter. The Vote Reveal Proof circuit MUST\n+enforce all conditions specified below.\n+\n+#### Public Inputs\n+\n+Given a primary input:\n+\n+- $\\mathsf{share}\\_\\mathsf{nullifier} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ —\n+  prevents double-counting.\n+- $C_{1,x}, C_{1,y}, C_{2,x}, C_{2,y} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the\n+  $x$- and $y$-coordinates of the El Gamal ciphertext points $(C_1, C_2)$\n+  for this share.\n+- $\\mathsf{proposal}\\_\\mathsf{id} ⦂ \\{1 \\ldots 15\\}$ — which proposal.\n+- $\\mathsf{vote}\\_\\mathsf{decision} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the voter's choice.\n+- $\\mathsf{rt}^{\\mathsf{vct}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — root of the\n+  Vote Commitment Tree.\n+- $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$\n+\n+#### Auxiliary Inputs\n+\n+The prover (submission server) knows:\n+\n+- $\\mathsf{vc} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$ — the vote commitment\n+  being opened (hidden from the verifier).\n+- $\\mathsf{path}^{\\mathsf{vct}}, \\mathsf{pos}^{\\mathsf{vct}}$ — Merkle proof for the VC\n+  in the VCT.\n+- $\\mathsf{shares}\\_\\mathsf{hash} ⦂ \\{ 0 .. q_{\\mathbb{P}}-1 \\}$\n+- $\\mathsf{share}\\_\\mathsf{index} \\in \\{0, 1, \\ldots, N_s - 1\\}$ — which share is being revealed.\n+- $\\mathsf{share}\\_{\\mathsf{comm}\\_0} \\ldots \\mathsf{share}\\_{\\mathsf{comm}\\_{N_s - 1}}$ —\n+  all $N_s$ blinded share commitments (to recompute\n+  $\\mathsf{shares}\\_\\mathsf{hash}$).\n+- $\\mathsf{blind}$ — the blind factor for the revealed share\n+  (at position $\\mathsf{share}\\_\\mathsf{index}$).\n+\n+#### Conditions\n+\n+##### Vote Commitment Membership\n+\n+**Condition 1: Merkle tree membership.** The circuit MUST enforce that\n+the VC exists in the VCT:\n+$(\\mathsf{path}^{\\mathsf{vct}}, \\mathsf{pos}^{\\mathsf{vct}})$ MUST be\n+a valid Merkle path from $\\mathsf{vc}$ to $\\mathsf{rt}^{\\mathsf{vct}}$,\n+without revealing which leaf. The VC value is a private witness.\n+\n+**Condition 2: Vote commitment integrity.** The circuit MUST enforce\n+that the VC is correctly constructed from its components:\n+\n+$$\\mathsf{vc} = \\mathsf{Poseidon}\\bigl(\\mathsf{DOMAIN}\\_\\mathsf{VC}, \\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}, \\mathsf{shares}\\_\\mathsf{hash}, \\mathsf{proposal}\\_\\mathsf{id}, \\mathsf{vote}\\_\\mathsf{decision}\\bigr)$$\n+\n+This binds the public $\\mathsf{proposal}\\_\\mathsf{id}$ and\n+$\\mathsf{vote}\\_\\mathsf{decision}$ to the private VC, ensuring that the\n+revealed share is attributed to the correct proposal and decision.\n+\n+##### Share Opening\n+\n+**Condition 3: Shares hash integrity.** The circuit MUST enforce that\n+$\\mathsf{shares}\\_\\mathsf{hash}$ is recomputed from the witness share\n+commitments:\n+\n+$$\\mathsf{shares}\\_\\mathsf{hash} = \\mathsf{Poseidon}\\bigl(\\mathsf{share}\\_{\\mathsf{comm}\\_\\mathsf{0}}, \\ldots, \\mathsf{share}\\_{\\mathsf{comm}_{N_s - 1}}\\bigr)$$\n+\n+The recomputed $\\mathsf{shares}\\_\\mathsf{hash}$ MUST equal the one inside\n+the VC (via condition 2). The share commitments are blinded\n+(see [Why Blinded Share Commitments]), so they do not reveal the\n+ciphertexts or blind factors of other shares to the prover.\n+\n+**Condition 4: Share membership.** The circuit MUST enforce that the\n+commitment derived from the public ciphertext coordinates\n+$C_{1,x}, C_{2,x}, C_{1,y}, C_{2,y}$ and the witness blind factor\n+matches the share commitment at position\n+$\\mathsf{share}\\_\\mathsf{index}$:\n+\n+$$\\mathsf{Poseidon}\\bigl(\\mathsf{blind}_{\\mathsf{share}\\_\\mathsf{index}}, C_{1,x}, C_{2,x}, C_{1,y}, C_{2,y}\\bigr) = \\mathsf{share}\\_\\mathsf{comms}[\\mathsf{share}\\_\\mathsf{index}]$$\n+\n+The circuit MUST encode $\\mathsf{share}\\_\\mathsf{index}$ as a one-hot\n+selector vector over $N_s$ positions. The mux MUST extract the\n+corresponding $\\mathsf{share}\\_\\mathsf{comm}$ via a dot product and\n+constrain equality with the commitment derived from the public\n+ciphertext coordinates and the witness blind factor. Only the blind\n+factor for the revealed share is needed; the remaining $N_s - 1$ share\n+commitments used in condition 3 are opaque witnesses that do not expose\n+their underlying ciphertexts or blind factors.\n+\n+##### Nullifier\n+\n+**Condition 5: Share nullifier.** The circuit MUST enforce that the\n+public $\\mathsf{share}\\_\\mathsf{nullifier}$ is correctly derived:\n+\n+$$\\mathsf{share}\\_\\mathsf{nullifier} = \\mathsf{Poseidon}\\bigl(\\mathsf{tag}_{\\mathsf{share}}, \\mathsf{vc}, \\mathsf{share}\\_\\mathsf{index}, \\mathsf{blind}\\bigr)$$\n+\n+where $\\mathsf{tag}_{\\mathsf{share}}$ is the field-element encoding of\n+`\"share spend\"` and $\\mathsf{blind}$ is the blind factor for the\n+revealed share. The VC and blind are private, making the nullifier\n+unlinkable to a specific VC without knowledge of these witnesses.\n+\n+#### Out-of-Circuit Verification\n+\n+A verifier that receives a Vote Reveal Proof $\\pi$ MUST perform the\n+following checks:\n+\n+1. Verify $\\pi$ against the public inputs.\n+2. Verify that $\\mathsf{share}\\_\\mathsf{nullifier}$ does not appear in the\n+   share nullifier set. If it does, reject as double-counting.\n+3. Verify that $\\mathsf{rt}^{\\mathsf{vct}}$ matches a published VCT root.\n+4. Verify that $\\mathsf{proposal}\\_\\mathsf{id}$ is valid for the current round.\n+5. Verify that $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ matches an active round.\n+6. Verify that $\\mathsf{vote}\\_\\mathsf{decision}$ is a valid option for the\n+   proposal identified by $\\mathsf{proposal}\\_\\mathsf{id}$ in this round.\n+7. Add $\\mathsf{share}\\_\\mathsf{nullifier}$ to the share nullifier set.\n+8. Accumulate $\\mathsf{enc}\\_\\mathsf{share}$ into the aggregate ciphertext for\n+   $(\\mathsf{proposal}\\_\\mathsf{id}, \\mathsf{vote}\\_\\mathsf{decision})$:\n+\n+$$\\mathsf{agg}[\\mathsf{proposal}\\_\\mathsf{id}][\\mathsf{vote}\\_\\mathsf{decision}] \\mathrel{+}= \\mathsf{enc}\\_\\mathsf{share}$$\n+\n+where $+$ denotes component-wise Pallas point addition.\n+\n+### Share Reveal Message\n+\n+A share reveal transaction submitted to the vote chain MUST contain:\n+\n+| Field | Type | Description |\n+|---|---|---|\n+| $\\pi_{\\text{reveal}}$ | Proof | The Vote Reveal Proof |\n+| $\\mathsf{share}\\_\\mathsf{nullifier}$ | Pallas scalar | Share nullifier |\n+| $\\mathsf{enc}\\_\\mathsf{share}$ | $(C_1, C_2)$ | El Gamal ciphertext (two Pallas points) |\n+| $\\mathsf{proposal}\\_\\mathsf{id}$ | $\\{1 \\ldots 15\\}$ | Proposal identifier |\n+| $\\mathsf{vote}\\_\\mathsf{decision}$ | Pallas scalar | Vote decision |\n+| $\\mathsf{rt}^{\\mathsf{vct}}$ | Pallas scalar | VCT root |\n+| $\\mathsf{voting}\\_{\\mathsf{round}\\_\\mathsf{id}}$ | Pallas scalar | Round identifier |\n+\n+Note: the Vote Reveal Proof has no spend authorization signature\n+because it is constructed by the submission server, not the voter.\n+\n+### Share Submission Payload\n+\n+The voter sends each share to a submission server as an off-chain\n+payload. For share $i$, the payload MUST contain:\n+\n+| Field | Description |\n+|---|---|\n+| $\\mathsf{vc}$ | The vote commitment |\n+| VCT position | Position of $\\mathsf{vc}$ in the VCT |\n+| $\\mathsf{shares}\\_\\mathsf{hash}$ | Hash of all blinded share commitments |\n+| $\\mathsf{proposal}\\_\\mathsf{id}$ | Proposal identifier |\n+| $\\mathsf{vote}\\_\\mathsf{decision}$ | Vote decision |\n+| $\\mathsf{share}\\_\\mathsf{index}$ | Which share to reveal (0-indexed) |\n+| $\\mathsf{enc}\\_\\mathsf{share}$ | El Gamal ciphertext $(C_1, C_2)$ for this share |\n+| $\\mathsf{blind}$ | Blind factor for this share |\n+| $\\mathsf{share}\\_{\\mathsf{comm}\\_0} \\ldots \\mathsf{share}\\_{\\mathsf{comm}\\_{N_s - 1}}$ | All $N_s$ blinded share commitments |\n+| $\\mathsf{submit\\_at}$ | Unix timestamp (seconds) for when the server should submit the share reveal transaction. 0 means immediate (last-moment mode). See [^submission-server] |\n+\n+The server receives only the ciphertext and blind factor for the\n+single share it is responsible for revealing. The remaining $N_s - 1$\n+shares are sent to other servers, each of which receives only its own\n+share's raw data. To recompute $\\mathsf{shares}\\_\\mathsf{hash}$ in\n+condition 3 of the Vote Reveal Proof, the server uses the blinded\n+share commitments, which do not expose the ciphertexts or blind\n+factors of the other shares (see [Why Per-Server Share Isolation]).\n+\n+Voters MUST distribute shares across multiple independent servers to\n+further limit any single server's view of their voting activity.\n+Server selection, temporal mixing, and communication protocols are\n+specified in [^submission-server].\n+\n+\n+## Tally\n+\n+After the voting window closes, the vote chain MUST decrypt the per-\n+$(\\mathsf{proposal}\\_\\mathsf{id}, \\mathsf{vote}\\_\\mathsf{decision})$\n+aggregate ciphertexts using the threshold decryption procedure\n+specified in [^ea-ceremony].\n+\n+\n+## Vote Chain\n+\n+The vote chain MUST maintain the following state per voting round:\n+\n+- The **Vote Commitment Tree** as defined in [Vote Commitment Tree].\n+- Three disjoint **nullifier sets** as defined in [Nullifier Sets].\n+- A **per-$(\\mathsf{proposal}\\_\\mathsf{id}, \\mathsf{vote}\\_\\mathsf{decision})$\n+  encrypted share accumulator**: the running component-wise sum of\n+  revealed El Gamal ciphertexts.\n+\n+For each transaction type, the vote chain MUST verify the\n+corresponding proof and perform the out-of-circuit checks specified in\n+[Delegation Proof], [Vote Proof], or [Vote Reveal Proof] respectively.\n+\n+The vote chain's consensus mechanism, block structure, transaction\n+encoding, and API are out of scope for this ZIP.\n+\n+### Poseidon Instantiation\n+\n+All Poseidon hashes in this protocol use the same instantiation as\n+the nullifier non-membership tree defined in [^balance-proof]:\n+$\\mathsf{P128Pow5T3}$ over $\\mathbb{F}_{q_{\\mathbb{P}}}$ (the Pallas\n+base field), with S-box $x^5$, width $t = 3$, rate $r = 2$, targeting\n+128-bit security, using the standard parameter generation procedure\n+from [^poseidon].\n+\n+Hashes with $L$ inputs use $\\mathsf{ConstantLength}\\langle L \\rangle$\n+mode (absorbing $L$ field elements with length padding), absorbing\n+two elements per permutation.\n+\n+The following table lists every Poseidon call site in this protocol:\n+\n+| Hash | Inputs ($L$) | Mode | Permutations |\n+|---|---|---|---|\n+| Voting round identifier | 8 | $\\mathsf{ConstantLength}\\langle 8 \\rangle$ | 4 |\n+| VAN core | 6 | $\\mathsf{ConstantLength}\\langle 6 \\rangle$ | 3 |\n+| VAN blinding | 2 | $\\mathsf{ConstantLength}\\langle 2 \\rangle$ | 1 |\n+| VAN nullifier | 4 | $\\mathsf{ConstantLength}\\langle 4 \\rangle$ | 2 |\n+| Vote commitment | 5 | $\\mathsf{ConstantLength}\\langle 5 \\rangle$ | 3 |\n+| Blinded share commitment | 5 | $\\mathsf{ConstantLength}\\langle 5 \\rangle$ | 3 |\n+| Shares hash | 16 | $\\mathsf{ConstantLength}\\langle 16 \\rangle$ | 8 |\n+| Share nullifier | 4 | $\\mathsf{ConstantLength}\\langle 4 \\rangle$ | 2 |\n+| VCT internal node | 2 | $\\mathsf{ConstantLength}\\langle 2 \\rangle$ | 1 |\n+| Governance nullifier | 4 | $\\mathsf{ConstantLength}\\langle 4 \\rangle$ | 2 |\n+| Rho binding | 7 | $\\mathsf{ConstantLength}\\langle 7 \\rangle$ | 4 |\n+\n+### Domain Separator Tags\n+\n+Several constructions in this protocol use a domain separator tag\n+encoded as a Pallas scalar. Each tag is defined by a fixed ASCII\n+string. To convert a tag string to a field element, interpret its\n+byte representation as an unsigned little-endian integer:\n+\n+$$\\mathsf{tag} = \\sum_{j=0}^{\\ell-1} b_j \\cdot 256^j$$\n+\n+where $b_0, \\ldots, b_{\\ell-1}$ are the ASCII byte values of the string\n+and $\\ell$ is its length. All tag strings in this protocol are shorter\n+than 32 bytes, so the resulting integer is less than $2^{256}$ and fits\n+in $\\mathbb{F}_{q_{\\mathbb{P}}}$ without reduction.\n+\n+# Rationale\n+\n+## Why a Separate Vote Chain\n+\n+Voting transactions (delegation proofs, vote proofs, share reveals) are\n+not standard Zcash shielded transactions. They require a new commitment\n+tree (the VCT), new nullifier sets, and an encrypted share accumulator\n+with homomorphic aggregation. Implementing these as a sidechain avoids\n+modifying the Zcash consensus layer and keeps the governance mechanism\n+independent of mainchain upgrade cycles.\n+\n+## Why Poseidon for the VCT\n+\n+The Orchard note commitment tree uses Sinsemilla [^protocol-concretesinsemilla]\n+for Merkle hashing. The VCT uses Poseidon instead because all three ZKPs\n+in this protocol require VCT Merkle membership proofs, and Poseidon\n+operates natively on field elements, making it significantly more\n+efficient inside Halo 2 [^halo2] arithmetic circuits than Sinsemilla\n+(which is optimized for bitstring\n+inputs). Since the VCT is new infrastructure with no backwards-\n+compatibility constraint, the more circuit-efficient primitive is\n+appropriate. This is the same rationale as for the nullifier\n+non-membership tree in [^balance-proof].\n+\n+## Why Ballot Scaling\n+\n+Expressing vote values in ballots ($\\lfloor \\text{zatoshi} / 12{,}500{,}000 \\rfloor$) rather\n+than raw zatoshi reduces bit-width throughout the protocol: El Gamal\n+scalar multiplications are faster, range checks are tighter, and the\n+discrete-log recovery at tally time [^ea-ceremony] has a smaller\n+search space.\n+The 0.125 ZEC minimum also prevents dust delegations from bloating vote\n+chain state.\n+\n+## Why a 24-bit Remainder Range\n+\n+The remainder in the ballot scaling constraint is range-checked to\n+24 bits ($< 16{,}777{,}216$), which is wider than the divisor\n+($12{,}500{,}000$). A prover can set $r > 12{,}500{,}000$, effectively\n+shorting themselves one ballot. This is harmless:\n+$\\mathsf{num}\\_\\mathsf{ballots}$ does not appear in any governance\n+nullifier, so the only effect is the prover voting with slightly less\n+weight than they could. The wider check avoids a custom\n+non-power-of-two range check in circuit.\n+\n+## Why Delegation to a Hotkey\n+\n+The protocol requires voters to produce Halo 2 zero-knowledge proofs\n+(delegation proof, vote proof) and construct vote commitments, operations that demand general-purpose computation on private key\n+material. Hardware wallets that custody Orchard spending keys cannot\n+perform these operations: they support signature generation but not\n+arbitrary-circuit ZKP construction.\n+\n+Delegation to a governance hotkey resolves this by separating spend\n+authorization from vote execution. The hardware wallet signs a single\n+$\\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}$ during delegation, authorizing the\n+transfer of voting power to a software-controlled hotkey. All subsequent\n+voting operations (VAN consumption, vote commitment construction, share\n+submission) use the hotkey's key material and run on a general-purpose\n+device.\n+\n+Without this separation, hardware wallet users would need to either\n+export spending keys to a software environment, negating the security\n+benefit of hardware custody, or forgo participation in governance\n+entirely. Delegation preserves the hardware wallet's role as the sole\n+custodian of spending keys while enabling full participation in the\n+voting protocol.\n+\n+## Why Deterministic Hotkey Derivation\n+\n+The voting flow spans multiple app sessions: delegation (ZKP #1),\n+one or more votes (ZKP #2), and vote signing. Each step requires\n+the hotkey's spend-authorizing key. The per-vote secrets ($r_i$,\n+$\\mathsf{blind}\\_\\mathsf{i}$, $\\alpha_v$) are also derived\n+deterministically from the hotkey seed via a domain-separated PRF\n+(see [Per-Vote Secret Derivation]), so crash recovery extends to\n+all secrets needed for vote construction.\n+\n+Deriving every secret from a single seed means the wallet stores\n+only a BIP 39 mnemonic in its keychain. If the app is terminated\n+between delegation and voting, or between proposals, all key\n+material is reconstructed from the mnemonic. Randomly sampled keys\n+would require securely persisting each component ($\\mathsf{vsk}$,\n+$\\mathsf{vsk.nk}$, $\\mathsf{rivk}\\_\\mathsf{v}$, and all per-vote\n+$r_i$ and $\\mathsf{blind}\\_\\mathsf{i}$ values) independently,\n+and any storage failure would be unrecoverable.\n+\n+## Why VAN Nullifier Domain Separation\n+\n+The VAN nullifier uses $\\mathsf{vsk.nk}$ (the governance hotkey's\n+nullifier deriving key) as the Poseidon key, while the governance\n+nullifier uses $\\mathsf{nk}$ (the holder's nullifier deriving key).\n+When the hotkey is a separate key from the holder's, these are distinct\n+field elements, and cross-circuit collision resistance follows from the\n+key difference alone. When the hotkey reuses the holder's key hierarchy\n+(e.g., in an all-software flow without hardware wallet separation),\n+$\\mathsf{vsk.nk} = \\mathsf{nk}$ and collision resistance relies on\n+the domain tags being distinct: `\"vote authority spend\"` and\n+`\"governance authorization\"` differ in both byte length and content,\n+producing distinct field elements. The domain tags provide\n+defense-in-depth in both cases.\n+\n+## Why 5 Notes per Delegation\n+\n+The Delegation Proof fixes the note slot count at 5 (with padding for\n+holders who have fewer notes). This choice balances wallet coverage\n+against proof cost: empirical analysis of the Orchard shielded pool\n+shows that over 90% of wallets hold 5 or fewer notes, so most holders\n+can delegate their full balance with a single user-facing signature.\n+\n+Each additional note slot adds a full set of per-note constraints to\n+the circuit, increasing proving time. A higher\n+slot count would cover marginally more wallets at a disproportionate\n+cost in prover resources. Holders with more than 5 notes can perform multiple delegations, each covering up to 5 notes.\n+\n+## Why a Dummy Signed Note\n+\n+The Delegation Proof includes a dummy signed note (value 0) whose rho is\n+deterministically bound to the delegation context. This mechanism exists\n+to obtain a $\\mathsf{SpendAuthSig}^{\\mathsf{Orchard}}$ from hardware\n+wallets that support only the standard Orchard PCZT signing flow,\n+without requiring firmware changes specific to governance.\n+\n+The dummy signed note construction, PCZT signing flow, and design\n+rationale (1-zatoshi display value, rho binding for non-replayability,\n+ZIP 244 sighash reuse, and the trade-offs of a future custom signing\n+protocol) are specified in [^balance-proof].\n+\n+## Why $N_s$ Shares Per Vote\n+\n+Splitting a vote into $N_s$ shares serves two purposes. First, it\n+provides temporal unlinkability: shares are submitted independently at\n+client-chosen times spread across the voting window, preventing an\n+observer from attributing all shares to a single voter by timing\n+correlation. Second, it limits the election authority's view: even if\n+the EA decrypts individual ciphertexts, it sees only individual shares,\n+not a voter's complete ballot allocation.\n+\n+When a voter casts near the end of the voting window, the protocol\n+falls back to single-share mode: the full ballot count is placed in one\n+share and submitted immediately. This sacrifices both benefits above but\n+ensures the vote is counted — each share requires the server to\n+construct a computationally expensive Vote Reveal Proof, and with\n+insufficient time remaining the server may not complete all $N_s$\n+proofs before the deadline. See [^submission-server] for the\n+last-moment buffer definition and timing details.\n+\n+## Why Blinded Share Commitments\n+\n+Each share commitment includes a random blind factor:\n+$\\mathsf{share}\\_{\\mathsf{comm}\\_\\mathsf{i}} = \\mathsf{Poseidon}(\\mathsf{blind}\\_\\mathsf{i}, C_{1,i,x}, C_{2,i,x}, C_{1,i,y}, C_{2,i,y})$.\n+Without blinding, an observer could compute\n+$\\mathsf{Poseidon}(C_{1,i,x}, C_{2,i,x}, C_{1,i,y}, C_{2,i,y})$ for each on-chain ciphertext\n+and compare against the $\\mathsf{shares}\\_\\mathsf{hash}$ values committed in\n+VCs, linking revealed shares back to specific vote commitments. The\n+blind factor makes this reverse computation infeasible.\n+\n+## Why Share Commitments Bind Full Curve Points\n+\n+On the Pallas curve, every $x$-coordinate has two valid $y$-values:\n+$P$ and $-P$. If the share commitment bound only the $x$-coordinates\n+of the ciphertext points, a malicious block proposer could negate\n+$C_1$ and $C_2$ by flipping the sign bits in their compressed\n+encodings (2 bit flips) without changing the $x$-coordinates. The\n+negated ciphertext encrypts $-v$ instead of $v$ under the same\n+El Gamal key, so the tally would accumulate $\\mathsf{Enc}(-v)$ instead\n+of $\\mathsf{Enc}(v)$, corrupting the election result. The Vote Reveal\n+Proof would still verify because the share commitment — binding only\n+$x$-coordinates — would be unchanged.\n+\n+Including both $x$- and $y$-coordinates in the share commitment hash binds each commitment to\n+the exact curve point. Flipping a sign bit changes the $y$-coordinate,\n+producing a different $\\mathsf{share}\\_\\mathsf{comm}$, which cascades\n+through $\\mathsf{shares}\\_\\mathsf{hash} \\to \\mathsf{vc} \\to$ Merkle\n+root, invalidating the proof.\n+\n+Full $y$-coordinates are used rather than 1-bit sign values because\n+the $y$-cells are already available from the ECC gadget output in the\n+Vote Proof circuit; extracting parity bits in-circuit would require a\n+255-bit field decomposition gadget, adding substantial constraint cost\n+for no security benefit.\n+\n+## Why Per-Server Share Isolation\n+\n+Each submission server receives only the ciphertext and blind factor\n+for the single share it reveals, plus the blinded share commitments\n+for all $N_s$ shares. It does not receive the raw ciphertexts or blind\n+factors of shares assigned to other servers. This limits the data any\n+single server can observe: even a compromised server learns only one\n+share's plaintext-encrypted ciphertext and blind, not the full set.\n+The blinded share commitments are sufficient for the server to\n+recompute $\\mathsf{shares}\\_\\mathsf{hash}$ in the proof (condition 3),\n+while the blinding prevents the server from correlating other shares'\n+ciphertexts with their commitments.\n+\n+## Why Server-Delegated Share Reveal\n+\n+The Vote Reveal Proof is constructed by the submission server\n+rather than the voter's client for two reasons: mobile devices are\n+unreliable for background ZKP computation, and server-side construction\n+enables temporal mixing of shares from many voters. The trust\n+requirement on the server is minimal: it learns encrypted shares and\n+vote decisions but cannot decrypt amounts or link shares to identities.\n+\n+## Why Reusing VAN Address and Randomness\n+\n+When a vote consumes a VAN and produces a new one, the new VAN reuses\n+the old VAN's diversified address ($\\mathsf{vpk}\\_{\\mathsf{g}\\_\\mathsf{d}}$,\n+$\\mathsf{vpk}\\_{\\mathsf{pk}\\_\\mathsf{d}}$) and commitment randomness\n+($\\mathsf{gov}\\_{\\mathsf{comm}\\_\\mathsf{rand}}$). Only\n+$\\mathsf{proposal}\\_\\mathsf{authority}$ changes. This is safe because VAN\n+commitments are blinded Poseidon hashes - the shared fields are never\n+externally observable (both old and new commitments appear as opaque\n+field elements in the VCT), so address rotation would provide no\n+additional unlinkability.\n+\n+## Why Proposal Identifiers Start at 1\n+\n+The $\\mathsf{proposal}\\_\\mathsf{authority}$ bitmask is 16 bits wide, but\n+$\\mathsf{proposal}\\_\\mathsf{id}$ values start at 1, yielding 15 usable\n+proposal slots rather than 16. Bit 0 is reserved.\n+\n+This follows from how lookup arguments work in Halo 2. A lookup table\n+that validates $(\\mathsf{proposal}\\_\\mathsf{id}, 2^{\\mathsf{proposal}\\_\\mathsf{id}})$\n+must include a default row that satisfies the lookup when the selector\n+is inactive. The circuit uses the identity row $(0, 1)$ for this\n+purpose: when the selector $q = 0$, the lookup input evaluates to\n+$(0, 1)$, which must be present in the table for the proof to verify.\n+Because every inactive row matches this entry, $\\mathsf{proposal}\\_\\mathsf{id} = 0$\n+cannot be used as a valid proposal identifier: a prover could trivially\n+satisfy the lookup with $\\mathsf{proposal}\\_\\mathsf{id} = 0$ even when no\n+authority check is intended. An additional non-zero gate\n+($\\mathsf{proposal}\\_\\mathsf{id} \\cdot \\mathsf{proposal}\\_\\mathsf{id}^{-1} = 1$)\n+provides defense-in-depth by rejecting $\\mathsf{proposal}\\_\\mathsf{id} = 0$\n+on active rows.\n+\n+## Why Threshold Secret Sharing\n+\n+See [^ea-ceremony] for the rationale behind threshold secret sharing.\n+\n+## Why a Send-Based VAN Model\n+\n+The Vote Proof consumes the old VAN and produces a new one with the\n+voted proposal's authority bit cleared. This is a UTXO-style\n+\"send\": each vote appends two leaves to the VCT (new VAN + VC) and\n+requires the voter to hold a current Merkle path.\n+\n+An alternative design was considered in which the VAN is eliminated\n+entirely and delegation is performed by out-of-band sharing of a\n+private key (hash-committed to on-chain). Under this model the Vote\n+Proof would not consume or re-create a VAN, removing the proposal\n+authority decrement step and the corresponding VCT growth. The primary\n+appeal is reduced client sync overhead: voters would not need to track\n+VAN re-insertions or maintain up-to-date Merkle paths for their own\n+VANs.\n+\n+The send-based model is retained because it preserves the ability to\n+add partial delegation in a future extension, splitting\n+$\\mathsf{num}\\_\\mathsf{ballots}$ across multiple delegates, each\n+receiving a fraction of the holder's voting weight. The VAN's explicit\n+ballot count and proposal authority bitmask are the data model that\n+would enable this; a future VAN-to-VAN delegation proof could consume\n+one VAN and produce two with subdivided ballot counts. Partial\n+delegation is not specified in this ZIP but the VAN model keeps the\n+design space open. Under the keysharing alternative, the delegate holds\n+the full key and therefore the full voting weight; there is no\n+in-protocol mechanism to subdivide it, and adding one later would\n+require a fundamentally different data model.\n+\n+The sync savings of the alternative are also smaller than they first\n+appear: even without VAN re-creation, clients still need to update\n+VCT Merkle paths for their Vote Commitments (which the submission\n+server requires for the Vote Reveal Proof). The VAN model adds\n+incremental path-update overhead but does not introduce a new\n+category of sync obligation. If a future design change eliminated the\n+need for clients to track VCT paths entirely (for example by moving\n+Merkle path retrieval fully to the submission server), the tradeoff\n+would shift in favor of removing the VAN. See [Open issues].\n+\n+## Why Classical El Gamal Rather Than Post-Quantum Encryption\n+\n+See [^ea-ceremony] for the rationale behind using classical El Gamal\n+rather than post-quantum encryption. The key consequence for this\n+protocol is that vote-amount privacy has a finite horizon tied to\n+quantum computing timelines, while voter *identity* is unaffected\n+(alternate nullifier unlinkability relies on Poseidon preimage\n+resistance, not on El Gamal). Vote splitting across $N_s$ shares\n+provides additional mitigation: a quantum adversary would recover\n+individual shares rather than complete ballot allocations unless it\n+also breaks the Poseidon-based blinded share commitments.\n+\n+## Why VCT Depth 24\n+\n+The Orchard note commitment tree uses depth 32, supporting $2^{32}$\n+(~4.3 billion) leaves. Governance voting produces far fewer leaves:\n+each voter generates one VAN per delegation and two leaves (a new VAN\n+plus a VC) per vote. Even 10,000 voters each voting on 50 proposals\n+produce roughly 1 million leaves, well within the $2^{24}$\n+(~16.7 million) capacity of a depth-24 tree. Because each voting round\n+maintains its own VCT, this budget applies per round rather than\n+accumulating across rounds.\n+\n+The reduced depth saves constraint rows in every circuit that performs\n+a Merkle membership proof (Vote Proof and Vote Reveal Proof), since\n+each level adds a Poseidon hash region. Moving from 32 to 24 levels\n+removes 8 hash regions per proof, reducing prover cost and verification\n+time without any practical capacity risk.\n+\n+\n+## Why Domain Tags in the VCT\n+\n+Both VANs and VCs are leaves in the same Merkle tree. The domain tags\n+($\\mathsf{DOMAIN}\\_\\mathsf{VAN} = 0$, $\\mathsf{DOMAIN}\\_\\mathsf{VC} = 1$) as the first\n+Poseidon input make it structurally impossible for a valid VAN preimage\n+to produce the same hash as a valid VC preimage, regardless of the\n+remaining inputs.\n+\n+\n+# Deployment\n+\n+This ZIP does not specify a consensus change to the Zcash mainchain.\n+Deployment considerations are specific to the vote chain and will be\n+addressed in the operational voting process ZIP.\n+\n+construction.\n+\n+\n+# Reference implementation\n+\n+- [^ref-circuits] — Halo 2 circuits for the Delegation Proof, Vote\n+  Proof, and Vote Reveal Proof.\n+- [^ref-vote-sdk] — Cosmos SDK vote chain implementing the VCT,\n+  nullifier sets, encrypted share accumulator, ceremony, tally, and\n+  submission server.\n+- [^ref-nullifier-pir] — PIR server and client for privately retrieving\n+  nullifier non-membership proofs.\n+- [^ref-librustvoting] — Client-side Rust library for proof generation,\n+  vote construction, tree synchronization, and governance PCZT\n+  construction.\n+\n+\n+# Open issues\n+\n+- A future custom signing protocol purpose-built for proof-of-balance\n+  could simplify the Delegation Proof circuit by removing the dummy\n+  signed note scaffolding and enable further improvements described in\n+  [^balance-proof].\n+- Partial delegation (a VAN-to-VAN delegation proof that consumes\n+  one VAN and produces two with subdivided $\\mathsf{num}\\_\\mathsf{ballots}$)\n+  is enabled by the send-based VAN model but not specified in this\n+  ZIP. Specifying the circuit and transaction type would allow a\n+  holder to distribute voting weight across multiple delegates.\n+  See [Why a Send-Based VAN Model].\n+- A simplified non-send VAN model, replacing VAN consumption and\n+  re-creation with out-of-band key delegation, would remove the\n+  proposal authority decrement step from the Vote Proof and reduce\n+  per-vote VCT growth. This is currently not adopted because it\n+  forecloses partial delegation and clients still need VCT Merkle path\n+  updates regardless. If future design changes remove the client's need\n+  to track VCT paths (e.g., full server-side path retrieval), this\n+  tradeoff should be revisited.\n+  See [Why a Send-Based VAN Model].\n+- Open issues related to the EA key ceremony are tracked in [^ea-ceremony].\n+- Open issues related to the submission server (share decomposition\n+  strategy, client confirmation via PIR, balance amendment, decision\n+  encryption) are tracked in [^submission-server].\n+- Open issues related to the balance proof are tracked in [^balance-proof].\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later](protocol/protocol.pdf)\n+\n+[^protocol-concretespendauthsig]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.7.1: Spend Authorization Signature (Orchard)](protocol/protocol.pdf#concretespendauthsig)\n+\n+[^protocol-orchardkeycomponents]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.2.3: Orchard Key Components](protocol/protocol.pdf#orchardkeycomponents)\n+\n+[^bip39]: [M. Palatinus, P. Rusnak, A. Voisine, and S. Bowe, \"BIP 39: Mnemonic code for generating deterministic keys\", 2013](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)\n+\n+[^poseidon]: [Poseidon: A New Hash Function for Zero-Knowledge Proof Systems](https://eprint.iacr.org/2019/458)\n+\n+[^balance-proof]: [Orchard Proof-of-Balance](draft-valargroup-orchard-balance-proof)\n+\n+[^pir-governance]: [Private Information Retrieval for Nullifier Exclusion Proofs](draft-valargroup-nullifier-pir)\n+\n+[^ea-ceremony]: [Election Authority Key Ceremony](draft-valargroup-ea-key-ceremony)\n+\n+[^voting-setup]: [Zcash Shielded Coinholder Voting](draft-valargroup-shielded-voting-setup)\n+\n+[^submission-server]: [Vote Share Submission Server](draft-valargroup-submission-server)\n+\n+[^halo2]: [S. Bowe, J. Grigg, and D. Hopwood, \"Recursive Proof Composition without a Trusted Setup\", 2019](https://eprint.iacr.org/2019/1021)\n+\n+[^protocol-merkletree]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.8: Note Commitment Trees](protocol/protocol.pdf#merkletree)\n+\n+[^protocol-concretesinsemilla]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.1.9: Sinsemilla Hash Function](protocol/protocol.pdf#concretesinsemillahash)\n+\n+[^protocol-concretecommitivk]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.9.4: CommitIvk](protocol/protocol.pdf#concretecommitivk)\n+\n+[^protocol-pallasandvesta]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.9.6: Pallas and Vesta](protocol/protocol.pdf#pallasandvesta)\n+\n+[^zip-244]: [ZIP 244: Transaction Identifier and Signature Validation for v5 Transactions](zip-0244)\n+\n+[^pczt]: [zcash/zips issue #693: Standardize a protocol for creating shielded transactions offline (PCZT)](https://github.com/zcash/zips/issues/693)\n+\n+[^ref-circuits]: [valargroup/voting-circuits: Halo 2 ZKP circuits for shielded voting](https://github.com/valargroup/voting-circuits)\n+\n+[^ref-vote-sdk]: [valargroup/vote-sdk: Cosmos SDK vote chain for shielded voting](https://github.com/valargroup/vote-sdk)\n+\n+[^ref-nullifier-pir]: [valargroup/vote-nullifier-pir: PIR system for nullifier non-membership proofs](https://github.com/valargroup/vote-nullifier-pir)\n+\n+[^ref-librustvoting]: [valargroup/librustvoting: Client-side voting library for proof generation and vote construction](https://github.com/valargroup/librustvoting)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-valargroup-shielded-voting.md","title":"Shielded Voting Protocol","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"high-level-summary","text":"High-level summary"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"el-gamal-encryption-on-pallas","text":"El Gamal Encryption on Pallas"},{"level":2,"id":"data-structures","text":"Data Structures"},{"level":3,"id":"voting-round-identifier","text":"Voting Round Identifier"},{"level":3,"id":"vote-authority-note-van","text":"Vote Authority Note (VAN)"},{"level":3,"id":"vote-commitment-vc","text":"Vote Commitment (VC)"},{"level":3,"id":"vote-share","text":"Vote Share"},{"level":3,"id":"shares-hash","text":"Shares Hash"},{"level":3,"id":"share-nullifier","text":"Share Nullifier"},{"level":3,"id":"vote-commitment-tree","text":"Vote Commitment Tree"},{"level":3,"id":"nullifier-sets","text":"Nullifier Sets"},{"level":2,"id":"governance-hotkey","text":"Governance Hotkey"},{"level":3,"id":"per-vote-secret-derivation","text":"Per-Vote Secret Derivation"},{"level":2,"id":"ballot-scaling","text":"Ballot Scaling"},{"level":2,"id":"delegation-phase","text":"Delegation Phase"},{"level":3,"id":"delegation-proof","text":"Delegation Proof"},{"level":4,"id":"public-inputs","text":"Public Inputs"},{"level":4,"id":"auxiliary-inputs","text":"Auxiliary Inputs"},{"level":4,"id":"conditions","text":"Conditions"},{"level":4,"id":"out-of-circuit-verification","text":"Out-of-Circuit Verification"},{"level":3,"id":"delegation-sighash","text":"Delegation Sighash"},{"level":3,"id":"delegation-message","text":"Delegation Message"},{"level":2,"id":"vote-phase","text":"Vote Phase"},{"level":3,"id":"vote-proof","text":"Vote Proof"},{"level":4,"id":"public-inputs-1","text":"Public Inputs"},{"level":4,"id":"auxiliary-inputs-1","text":"Auxiliary Inputs"},{"level":4,"id":"conditions-1","text":"Conditions"},{"level":4,"id":"out-of-circuit-verification-1","text":"Out-of-Circuit Verification"},{"level":3,"id":"vote-sighash","text":"Vote Sighash"},{"level":3,"id":"vote-message","text":"Vote Message"},{"level":2,"id":"share-reveal-phase","text":"Share Reveal Phase"},{"level":3,"id":"vote-reveal-proof","text":"Vote Reveal Proof"},{"level":4,"id":"public-inputs-2","text":"Public Inputs"},{"level":4,"id":"auxiliary-inputs-2","text":"Auxiliary Inputs"},{"level":4,"id":"conditions-2","text":"Conditions"},{"level":4,"id":"out-of-circuit-verification-2","text":"Out-of-Circuit Verification"},{"level":3,"id":"share-reveal-message","text":"Share Reveal Message"},{"level":3,"id":"share-submission-payload","text":"Share Submission Payload"},{"level":2,"id":"tally","text":"Tally"},{"level":2,"id":"vote-chain","text":"Vote Chain"},{"level":3,"id":"poseidon-instantiation","text":"Poseidon Instantiation"},{"level":3,"id":"domain-separator-tags","text":"Domain Separator Tags"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"why-a-separate-vote-chain","text":"Why a Separate Vote Chain"},{"level":2,"id":"why-poseidon-for-the-vct","text":"Why Poseidon for the VCT"},{"level":2,"id":"why-ballot-scaling","text":"Why Ballot Scaling"},{"level":2,"id":"why-a-24-bit-remainder-range","text":"Why a 24-bit Remainder Range"},{"level":2,"id":"why-delegation-to-a-hotkey","text":"Why Delegation to a Hotkey"},{"level":2,"id":"why-deterministic-hotkey-derivation","text":"Why Deterministic Hotkey Derivation"},{"level":2,"id":"why-van-nullifier-domain-separation","text":"Why VAN Nullifier Domain Separation"},{"level":2,"id":"why-5-notes-per-delegation","text":"Why 5 Notes per Delegation"},{"level":2,"id":"why-a-dummy-signed-note","text":"Why a Dummy Signed Note"},{"level":2,"id":"why-ns-shares-per-vote","text":"Why Ns Shares Per Vote"},{"level":2,"id":"why-blinded-share-commitments","text":"Why Blinded Share Commitments"},{"level":2,"id":"why-share-commitments-bind-full-curve-points","text":"Why Share Commitments Bind Full Curve Points"},{"level":2,"id":"why-per-server-share-isolation","text":"Why Per-Server Share Isolation"},{"level":2,"id":"why-server-delegated-share-reveal","text":"Why Server-Delegated Share Reveal"},{"level":2,"id":"why-reusing-van-address-and-randomness","text":"Why Reusing VAN Address and Randomness"},{"level":2,"id":"why-proposal-identifiers-start-at-1","text":"Why Proposal Identifiers Start at 1"},{"level":2,"id":"why-threshold-secret-sharing","text":"Why Threshold Secret Sharing"},{"level":2,"id":"why-a-send-based-van-model","text":"Why a Send-Based VAN Model"},{"level":2,"id":"why-classical-el-gamal-rather-than-post-quantum-encryption","text":"Why Classical El Gamal Rather Than Post-Quantum Encryption"},{"level":2,"id":"why-vct-depth-24","text":"Why VCT Depth 24"},{"level":2,"id":"why-domain-tags-in-the-vct","text":"Why Domain Tags in the VCT"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1243,"title":"ZIP: Structured Attestation Protocol for Application-Layer Lifecycle Events (ZAP1)","author":"Zk-nd3r","createdAt":"2026-03-28T20:14:43Z","updatedAt":"2026-07-29T21:49:54Z","url":"https://github.com/zcash/zips/pull/1243","diffUrl":"https://github.com/zcash/zips/pull/1243.diff","headRef":"draft-frontiercompute-nsm1","headRepo":"Zk-nd3r/zips","headSha":"0537ce5606e1e1700b1278285cdceef8ffbd8397","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This PR proposes a draft ZIP for the ZAP1 (Zcash Attestation Protocol v1) structured memo protocol used for application-layer attestation. Reference implementations and supporting artifacts: - zap1: https://github.com/Frontier-Compute/zap1 - zap1-verify: https://github.com/Frontier-Compute/zap1-veri","zipRefs":[],"draftRefs":["draft-frontiercompute-zap1"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-frontiercompute-zap1.md","status":"added","additions":317,"deletions":0,"patch":"@@ -0,0 +1,317 @@\n+ZIP: ???\n+Title: Structured Attestation Protocol for Application-Layer Lifecycle Events (ZAP1)\n+Owners: Frontier Compute <ops@frontiercompute.io>\n+Credits: Zk-nd3r\n+Status: Draft\n+Category: Standards Track\n+Created: 2026-03-28\n+License: MIT\n+Discussions-To: <https://github.com/zcash/zips/pull/1243>\n+Pull-Request: <https://github.com/zcash/zips/pull/1243>\n+\n+## Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"REQUIRED\", \"SHALL\", \"SHALL NOT\",\n+\"SHOULD\", \"SHOULD NOT\", \"RECOMMENDED\", \"NOT RECOMMENDED\", \"MAY\", and\n+\"OPTIONAL\" in this document are to be interpreted as described in BCP 14\n+when, and only when, they appear in all capitals, as shown here.\n+\n+## Abstract\n+\n+This ZIP specifies an application-layer attestation format for lifecycle events\n+committed to Zcash shielded transactions. It defines event typing, BLAKE2b hash\n+construction rules, Merkle tree aggregation, and a verification procedure for\n+on-chain commitments that keep participant-identifying data off-chain.\n+\n+This draft is based on the deployed `ZAP1` attestation protocol (formerly `NSM1`). In the reference deployment, event payloads\n+are hashed with BLAKE2b-256 using the personalization string\n+`NordicShield_`, inserted into an append-only Merkle tree, and periodically\n+anchored to Zcash using a transaction with type byte `0x09`.\n+\n+## Relationship to ZIP 302\n+\n+This ZIP defines application-layer attestation semantics. The carrier format\n+is specified separately by ZIP 302 [^zip-0302] (Structured Memos, PR #638). When\n+ZIP 302 is deployed, ZAP1 attestation payloads SHOULD be encoded as a ZIP 302\n+part type. Until then, the binary layout below serves as a transitional\n+encoding within the raw 512-byte shielded memo field.\n+\n+The parts of this ZIP that are independent of the container are: the event type\n+registry, hash construction rules, Merkle tree aggregation, and the\n+verification procedure.\n+\n+## Motivation\n+\n+Zcash shielded memos can carry structured application commitments, but the\n+ecosystem lacks a convention for typed lifecycle events that can be\n+independently recomputed by external verifiers.\n+\n+Exposing Zcash state and actions to agents is straightforward. A REST or\n+GraphQL endpoint can be wrapped as an MCP server in a small amount of code, and\n+wallet-layer MCP implementations can expose balances, signing workflows, sync\n+state, and transaction construction. These surfaces share one property: the\n+consumer must trust the server response. If the backend is wrong, compromised,\n+or unavailable later, the agent has no independent receipt it can hand to a\n+third party for verification.\n+\n+ZAP1 closes that gap. It exposes no balances and moves no value. It produces a\n+receipt: a typed event committed to a BLAKE2b Merkle tree, with the root\n+anchored in Zcash memo data, verifiable from the public schema, proof material,\n+and chain anchor without trusting the issuer's server.\n+\n+The distinction is categorical. Tool servers answer \"what does this backend say\n+right now?\" ZAP1 answers \"what happened, provable later to another verifier?\"\n+The protocol exists for the proof artifact, not for tool exposure.\n+\n+Applications that track ownership, deployment, billing, transfer, and exit\n+events need:\n+\n+- deterministic hash construction\n+- stable event typing\n+- a payload format that can be parsed without application-specific heuristics\n+- a way to commit large event histories without writing full plaintext records\n+  to the chain\n+\n+This ZIP standardizes the event commitment rules used by one such deployment\n+so that other builders can implement compatible tooling, verification, and\n+future extensions.\n+\n+## Requirements\n+\n+An implementation of this ZIP:\n+\n+- MUST treat the attestation payload as a binary structure before any wallet-specific\n+  shielded memo encoding.\n+- MUST encode integers in big-endian byte order.\n+- MUST NOT place participant PII directly in the attestation payload.\n+- MUST derive event payload hashes deterministically from the underlying event\n+  fields.\n+- MUST preserve event insertion order when deriving Merkle roots.\n+- MUST allow verifiers to recompute both the event leaf hash and the anchored\n+  root from public artifacts plus application-provided witness data.\n+\n+## Specification\n+\n+### Binary Payload Layout\n+\n+Before shielded memo encoding (or ZIP 302 part encoding), the binary attestation payload is:\n+\n+```text\n+byte 0      : version            = 0x01\n+byte 1      : type               = 0x01..0x0f\n+bytes 2..5  : cohort_id          = u32 big-endian\n+bytes 6..37 : payload_hash       = 32 bytes\n+bytes 38..45: timestamp          = u64 big-endian unix seconds\n+bytes 46..77: serial_hash        = 32 bytes, or 32 zero bytes when unused\n+bytes 78..n : label              = UTF-8 human-readable label, optional\n+```\n+\n+For human-readable transport, the payload SHOULD be rendered as:\n+\n+```text\n+ZAP1:{type}:{payload}\n+```\n+\n+where:\n+\n+- `ZAP1` is the protocol marker\n+- `{type}` is the two-digit lowercase hexadecimal event type\n+- `{payload}` is the hexadecimal encoding of the full binary layout above\n+\n+### Event Types\n+\n+This draft defines the following event type assignments:\n+\n+| Type | Name | Payload definition | Status |\n+| --- | --- | --- | --- |\n+| `0x01` | `PROGRAM_ENTRY` | `BLAKE2b_32(wallet_hash)` | Deployed |\n+| `0x02` | `OWNERSHIP_ATTEST` | `BLAKE2b_32(wallet_hash || serial_number)` | Deployed |\n+| `0x03` | `CONTRACT_ANCHOR` | `BLAKE2b_32(serial_number || contract_sha256)` | Deployed |\n+| `0x04` | `DEPLOYMENT` | `BLAKE2b_32(serial_number || facility_id || timestamp_be)` | Deployed |\n+| `0x05` | `HOSTING_PAYMENT` | `BLAKE2b_32(serial_number || month_be || year_be)` | Deployed |\n+| `0x06` | `SHIELD_RENEWAL` | `BLAKE2b_32(wallet_hash || year_be)` | Deployed |\n+| `0x07` | `TRANSFER` | `BLAKE2b_32(old_wallet || new_wallet || serial_number)` | Deployed |\n+| `0x08` | `EXIT` | `BLAKE2b_32(wallet_hash || serial_number || timestamp_be)` | Deployed |\n+| `0x09` | `MERKLE_ROOT` | raw 32-byte Merkle root | Deployed |\n+| `0x0A` | `STAKING_DEPOSIT` | `BLAKE2b_32(wallet_hash || amount_zat_be || validator_id)` | Reserved |\n+| `0x0B` | `STAKING_WITHDRAW` | `BLAKE2b_32(wallet_hash || amount_zat_be || validator_id)` | Reserved |\n+| `0x0C` | `STAKING_REWARD` | `BLAKE2b_32(wallet_hash || amount_zat_be || epoch_be)` | Reserved |\n+| `0x0D` | `GOVERNANCE_PROPOSAL` | `BLAKE2b_32(wallet_hash || proposal_id || proposal_hash)` | Reserved |\n+| `0x0E` | `GOVERNANCE_VOTE` | `BLAKE2b_32(wallet_hash || proposal_id || vote_commitment)` | Reserved |\n+| `0x0F` | `GOVERNANCE_RESULT` | `BLAKE2b_32(wallet_hash || proposal_id || result_hash)` | Reserved |\n+\n+Implementations of the deployed `ZAP1` flow currently use the first nine event\n+types in production. The staking and governance event types are reserved and\n+MUST NOT be assumed stable until separately activated.\n+\n+### Hash Construction\n+Field sizes for hash inputs:\n+\n+- `wallet_hash`: 32 bytes (application-derived identifier)\n+- `serial_number`: 32 bytes\n+- `contract_sha256`: 32 bytes\n+- `facility_id`: 32 bytes (zero-padded if shorter)\n+- `timestamp_be`: u64 big-endian (8 bytes)\n+- `month_be`: u16 big-endian (2 bytes)\n+- `year_be`: u16 big-endian (2 bytes)\n+- `amount_zat_be`: u64 big-endian (8 bytes)\n+- `epoch_be`: u32 big-endian (4 bytes)\n+- `validator_id`: 32 bytes\n+- `proposal_id`: 32 bytes\n+- `proposal_hash`, `vote_commitment`, `result_hash`: 32 bytes each\n+- `old_wallet`, `new_wallet`: 32 bytes each\n+\n+Unless otherwise specified, event payload hashes use BLAKE2b with:\n+\n+- digest length: 32 bytes\n+- personalization: `NordicShield_` (13 bytes, zero-padded to 16 bytes per BLAKE2b spec)\n+\n+The hash input for each event type is defined in the table above. The\n+`MERKLE_ROOT` event is a special case whose payload hash is the 32-byte Merkle\n+root itself, without an additional BLAKE2b compression step.\n+\n+For event types that carry a serial number, the `serial_hash` payload field MUST\n+be:\n+\n+```text\n+BLAKE2b_32(serial_number)\n+```\n+\n+with the same personalization string `NordicShield_`.\n+\n+If no serial number is applicable, `serial_hash` MUST be 32 zero bytes.\n+\n+### Merkle Tree Commitments\n+\n+Applications using this attestation protocol SHOULD aggregate event payload hashes into\n+an append-only binary Merkle tree. For the deployed `ZAP1` protocol:\n+\n+- each event produces one leaf\n+- leaves are ordered by insertion sequence\n+- parent nodes are `BLAKE2b_32(left || right)`\n+- Merkle node personalization is `NordicShield_MRK`\n+- if a level has odd cardinality, the final node is duplicated\n+\n+An inclusion proof consists of:\n+\n+- leaf hash\n+- sibling hashes\n+- sibling positions\n+- derived Merkle root\n+- anchor transaction identifier\n+- anchor block height\n+\n+### On-Chain Anchoring\n+\n+When anchoring a Merkle root to Zcash:\n+\n+- the event type MUST be `0x09`\n+- the payload hash MUST be the raw current Merkle root\n+- the shielded memo field SHOULD be encoded as `ZAP1:09:{payload}`\n+\n+The deployed `ZAP1` implementation broadcasts shielded memo commitments using\n+`zingo-cli`. The current deployment anchors every 10 events or every 24 hours,\n+whichever occurs first.\n+\n+### Verification Procedure\n+\n+To verify a committed event, a verifier:\n+\n+1. recomputes the event payload hash from the application fields\n+2. reconstructs the attestation payload and event leaf hash\n+3. walks the Merkle proof path to derive the root\n+4. retrieves the referenced anchor transaction\n+5. confirms that the shielded memo field contains the same `0x09` root commitment\n+6. confirms that the transaction is mined on the intended chain\n+\n+## Rationale\n+\n+This design separates protocol semantics from wallet transport.\n+\n+The binary layout is fixed-width for all critical fields so parsers can extract\n+event type, cohort identifier, and proof-relevant material without needing to\n+understand any free-form label text. BLAKE2b-256 is used for compactness and\n+performance. Personalization strings provide domain separation between event\n+payload hashing and Merkle internal-node hashing.\n+\n+Anchoring Merkle roots rather than full event records minimizes chain load while\n+preserving independent verifiability. The shielded memo field remains short and constant-sized\n+for the root-commitment case even as the underlying event history grows.\n+\n+## Privacy Considerations\n+\n+This protocol avoids writing participant-identifying plaintext\n+data to chain. Implementations MUST ensure that `wallet_hash`, `serial_hash`,\n+contract digests, and other payload inputs are derived values rather than directly\n+identifying customer records.\n+\n+The optional `label` field may leak application metadata if misused. Protocols\n+using this ZIP SHOULD either omit the `label` field or constrain it to operational\n+strings that do not identify participants.\n+\n+## Security Considerations\n+\n+This protocol provides integrity and auditability, not confidentiality of the\n+underlying application database. Security depends on:\n+\n+- correct construction of event hashes\n+- correct Merkle insertion ordering\n+- correct association between proof bundles and the anchored root that covers\n+  them\n+- correct wallet-side retrieval of the mined shielded-memo-bearing transaction\n+\n+This ZIP does not address:\n+\n+- fraudulent off-chain data entry before hashing\n+- theft of the key used to authorize anchor transactions\n+- application-level authorization of transfers or exits\n+\n+The deployed `ZAP1` stack currently uses single-operator anchor signing, with a\n+separate FROST migration design under development for threshold authorization of\n+future root anchors.\n+\n+## Backwards Compatibility\n+\n+This ZIP codifies the deployed version-`0x01` `ZAP1` format. Existing proof\n+bundles remain valid as long as verifiers preserve the original event hash\n+construction, Merkle hashing rules, and anchor transaction references.\n+\n+Future incompatible payload layouts MUST use a new version byte and SHOULD use a\n+distinct human-readable protocol marker.\n+\n+## Reference Implementation\n+\n+Reference implementations are available at:\n+\n+- [Frontier-Compute/zap1](https://github.com/Frontier-Compute/zap1), which implements the deployed `ZAP1` attestation protocol, Merkle tree maintenance, proof bundle generation, and root anchoring flow.\n+- [Frontier-Compute/zap1-verify](https://github.com/Frontier-Compute/zap1-verify) [^zap1-verify], which provides a standalone Rust and WASM verifier for ZAP1 leaf hashes and Merkle proofs.\n+\n+In the deployed `zap1` implementation:\n+\n+- event payload hashes are computed with BLAKE2b-256 and personalization `NordicShield_`\n+- Merkle internal nodes use personalization `NordicShield_MRK`\n+- root anchors are transmitted as `ZAP1:09:{root}`\n+\n+## Test Vectors\n+\n+A companion test vector package SHOULD provide:\n+\n+- one event hash vector for each deployed event type `0x01` through `0x09`\n+- the exact input fields used to derive the payload hash\n+- the expected `leaf_hash`\n+- the hash personalization strings used\n+\n+The deployed `ZAP1` implementation [^zap1] publishes those vectors separately as:\n+\n+- [Frontier-Compute/zap1/TEST_VECTORS.md](https://github.com/Frontier-Compute/zap1/blob/main/TEST_VECTORS.md)\n+- [TEST_VECTORS.md](https://github.com/Frontier-Compute/zap1/blob/main/TEST_VECTORS.md)\n+\n+## References\n+\n+[^BCP14]: [Information on BCP 14 -- \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^zip-0302]: [ZIP 302: Standardized Memo Field Format](https://zips.z.cash/zip-0302)\n+\n+[^zap1]: [Frontier-Compute/zap1: ZAP1 attestation protocol reference implementation](https://github.com/Frontier-Compute/zap1)\n+\n+[^zap1-verify]: [Frontier-Compute/zap1-verify: standalone Rust and WASM verifier for ZAP1 proofs](https://github.com/Frontier-Compute/zap1-verify)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-frontiercompute-zap1.md","title":"ZIP: Structured Attestation Protocol for Application-Layer Lifecycle Events (ZAP1)","status":"Draft","headings":[{"level":2,"id":"terminology","text":"Terminology"},{"level":2,"id":"abstract","text":"Abstract"},{"level":2,"id":"relationship-to-zip-302","text":"Relationship to ZIP 302"},{"level":2,"id":"motivation","text":"Motivation"},{"level":2,"id":"requirements","text":"Requirements"},{"level":2,"id":"specification","text":"Specification"},{"level":3,"id":"binary-payload-layout","text":"Binary Payload Layout"},{"level":3,"id":"event-types","text":"Event Types"},{"level":3,"id":"hash-construction","text":"Hash Construction"},{"level":3,"id":"merkle-tree-commitments","text":"Merkle Tree Commitments"},{"level":3,"id":"on-chain-anchoring","text":"On-Chain Anchoring"},{"level":3,"id":"verification-procedure","text":"Verification Procedure"},{"level":2,"id":"rationale","text":"Rationale"},{"level":2,"id":"privacy-considerations","text":"Privacy Considerations"},{"level":2,"id":"security-considerations","text":"Security Considerations"},{"level":2,"id":"backwards-compatibility","text":"Backwards Compatibility"},{"level":2,"id":"reference-implementation","text":"Reference Implementation"},{"level":2,"id":"test-vectors","text":"Test Vectors"},{"level":2,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1298,"title":"Replace production rendering pipeline with `nix build` flow.","author":"shielded-nate","createdAt":"2026-06-15T03:18:08Z","updatedAt":"2026-06-26T08:27:41Z","url":"https://github.com/zcash/zips/pull/1298","diffUrl":"https://github.com/zcash/zips/pull/1298.diff","headRef":"add-flake-rendering-outputs","headRepo":"shielded-nate/zips","headSha":"f3ff9b0a02528e717940da2793e2869567be39d4","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"The goal of this PR is to replace the production rendering pipeline to use `nix build`. **Motivation:** As a nix-supremacy conspirator, I view all software as on a continuum where the desired end-point is that it is packaged so that `nix build` with the default target gives me everything I'd ever wa","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":"flake.nix","status":"modified","additions":58,"deletions":31,"patch":"@@ -63,40 +63,67 @@\n           # Tests require additional fixtures not included in the PyPI tarball\n           doCheck = false;\n         };\n+\n+        buildInputs = [\n+          # Core dependencies for render.sh\n+          rst2html5 # rst2html5 2.0.1 (PyPI)\n+          pkgs.python3Packages.pygments # syntax highlighting for code blocks\n+          pkgs.pandoc # pandoc markdown renderer\n+          mmd # multimarkdown renderer (zcash fork)\n+          pkgs.perl # perl for text processing\n+\n+          # Build system dependencies\n+          pkgs.gnumake # make command for building\n+          pkgs.git # required by Makefile for safe.directory\n+\n+          # LaTeX dependencies for protocol PDF generation\n+          (pkgs.texlive.combine { inherit (pkgs.texlive) scheme-full; })\n+\n+          # Python dependencies for links_and_dests.py\n+          pkgs.python3\n+          pkgs.python3Packages.beautifulsoup4\n+          pkgs.python3Packages.html5lib\n+          pkgs.python3Packages.certifi\n+\n+          # Standard utilities (usually available, but ensuring they're present)\n+          pkgs.coreutils\n+          pkgs.bash\n+          pkgs.gnused\n+          pkgs.gnugrep\n+          pkgs.diffutils\n+          pkgs.findutils\n+        ];\n+\n+        all = pkgs.stdenv.mkDerivation {\n+          pname = \"zcash-zips-rendered-all\";\n+          version = \"0.0.1\";\n+          src = ./.;\n+\n+          inherit buildInputs;\n+\n+          buildPhase = ''\n+            # Subprocess for bash set flags scope, especially -x so that we\n+            # don't see very verbose nix cleanup traces on errors:\n+            (\n+              set -efuxo pipefail\n+              # Ewww... looks like `kpathsea` mutates the user's home.\n+              # Create a fake home dir inside the buildir:\n+              fake_home='./messy-fake-home'\n+              mkdir \"$fake_home\"\n+              HOME=\"$fake_home\" make all\n+            )\n+          '';\n+        };\n       in\n       {\n+        packages = {\n+          inherit all;\n+\n+          default = all;\n+        };\n+\n         devShells.default = pkgs.mkShell {\n-          buildInputs = [\n-            # Core dependencies for render.sh\n-            rst2html5 # rst2html5 2.0.1 (PyPI)\n-            pkgs.python3Packages.pygments # syntax highlighting for code blocks\n-            pkgs.pandoc # pandoc markdown renderer\n-            mmd # multimarkdown renderer (zcash fork)\n-            pkgs.perl # perl for text processing\n-\n-            # Build system dependencies\n-            pkgs.gnumake # make command for building\n-            pkgs.git # required by Makefile for safe.directory\n-\n-            # LaTeX dependencies for protocol PDF generation\n-            (pkgs.texlive.combine {\n-              inherit (pkgs.texlive) scheme-full;\n-            })\n-\n-            # Python dependencies for links_and_dests.py\n-            pkgs.python3\n-            pkgs.python3Packages.beautifulsoup4\n-            pkgs.python3Packages.html5lib\n-            pkgs.python3Packages.certifi\n-\n-            # Standard utilities (usually available, but ensuring they're present)\n-            pkgs.coreutils\n-            pkgs.bash\n-            pkgs.gnused\n-            pkgs.gnugrep\n-            pkgs.diffutils\n-            pkgs.findutils\n-          ];\n+          inherit buildInputs;\n \n           shellHook = ''\n             echo \"ZIP documentation rendering environment\""}],"filesTruncated":false,"preview":null},{"number":1216,"title":"Crosslink ZIPs","author":"shielded-nate","createdAt":"2026-03-17T16:55:40Z","updatedAt":"2026-06-03T18:52:34Z","url":"https://github.com/zcash/zips/pull/1216","diffUrl":"https://github.com/zcash/zips/pull/1216.diff","headRef":"zip-crosslink-first-draft","headRepo":"shielded-nate/zips","headSha":"d9ac34322c27b4502cc93ebdccfc9229e448ced8","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"👋 I'd like to get a placeholder ZIP for Crosslink. What else needs to happen here before an incomplete stub can be merged? - Copy the `zip-template.md` for crosslink; change some headers with provisional crosslink info.","zipRefs":[],"draftRefs":["draft-shieldedlabs-crosslink-construction","draft-shieldedlabs-crosslink-ledger-state","draft-shieldedlabs-crosslink-overview"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":7,"deletions":2,"patch":"@@ -208,10 +208,11 @@ written.\n     <tr> <td>400</td> <td class=\"left\"><a href=\"zips/zip-0400.rst\">Wallet.dat format</a></td> <td>Draft</td> <td class=\"left\"></td>\n     <tr> <td><span class=\"reserved\">402</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0402.rst\">New Wallet Database Format</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/365\">zips#365</a></td>\n     <tr> <td><span class=\"reserved\">403</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0403.rst\">Verification Behaviour of zcashd</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/404\">zips#404</a></td>\n-    <tr> <td><span class=\"reserved\">416</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0416.rst\">Support for Unified Addresses in zcashd</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/503\">zips#503</a></td>\n+    <tr> <td><span class=\"reserved\">416</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0416.rst\">Spending Key Derivation in the `zcashd` wallet</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1175\">zips#1175</a></td>\n     <tr> <td>2002</td> <td class=\"left\"><a href=\"zips/zip-2002.rst\">Explicit Fees</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/803\">zips#803</a></td>\n     <tr> <td>2003</td> <td class=\"left\"><a href=\"zips/zip-2003.rst\">Disallow version 4 transactions</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/825\">zips#825</a></td>\n     <tr> <td>2004</td> <td class=\"left\"><a href=\"zips/zip-2004.rst\">Remove the dependency of consensus on note encryption</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/917\">zips#917</a></td>\n+    <tr> <td>CROSSLINK</td> <td class=\"left\"><a href=\"zips/zip-CROSSLINK.md\">The Crosslink Consensus Protocol</a></td> <td>Draft</td> <td class=\"left\"></td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n     <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Template for new ZIPs}</a></td> <td>Draft</td> <td class=\"left\"></td>\n@@ -233,6 +234,9 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-deploy-nu7</td> <td class=\"left\"><a href=\"zips/draft-arya-deploy-nu7.md\">Deployment of the NU7 Network Upgrade</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/839\">zips#839</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1230\">zips#1230</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-shieldedlabs-crosslink-construction</td> <td class=\"left\"><a href=\"zips/draft-shieldedlabs-crosslink-construction.rst\">CCC-SL: Crosslink Consensus Construction from Shielded Labs</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-shieldedlabs-crosslink-ledger-state</td> <td class=\"left\"><a href=\"zips/draft-shieldedlabs-crosslink-ledger-state.rst\">RSM-SL-v1: Crosslink Ledger State and Ledger Mutations</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-shieldedlabs-crosslink-overview</td> <td class=\"left\"><a href=\"zips/draft-shieldedlabs-crosslink-overview.rst\">Shielded Labs Crosslink v1: Protocol Overview and Architecture</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1229\">zips#1229</a></td>\n   </table></embed>\n \n@@ -368,7 +372,7 @@ Index of ZIPs\n     <tr> <td>401</td> <td class=\"left\"><a href=\"zips/zip-0401.rst\">Addressing Mempool Denial-of-Service</a></td> <td>Active</td>\n     <tr> <td><span class=\"reserved\">402</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0402.rst\">New Wallet Database Format</a></td> <td>Reserved</td>\n     <tr> <td><span class=\"reserved\">403</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0403.rst\">Verification Behaviour of zcashd</a></td> <td>Reserved</td>\n-    <tr> <td><span class=\"reserved\">416</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0416.rst\">Support for Unified Addresses in zcashd</a></td> <td>Reserved</td>\n+    <tr> <td><span class=\"reserved\">416</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0416.rst\">Spending Key Derivation in the `zcashd` wallet</a></td> <td>Reserved</td>\n     <tr> <td><strike>1001</strike></td> <td class=\"left\"><strike><a href=\"zips/zip-1001.rst\">Keep the Block Distribution as Initially Defined — 90% to Miners</a></strike></td> <td>Obsolete</td>\n     <tr> <td><strike>1002</strike></td> <td class=\"left\"><strike><a href=\"zips/zip-1002.rst\">Opt-in Donation Feature</a></strike></td> <td>Obsolete</td>\n     <tr> <td><strike>1003</strike></td> <td class=\"left\"><strike><a href=\"zips/zip-1003.rst\">20% Split Evenly Between the ECC and the Zcash Foundation, and a Voting System Mandate</a></strike></td> <td>Obsolete</td>\n@@ -390,6 +394,7 @@ Index of ZIPs\n     <tr> <td>2003</td> <td class=\"left\"><a href=\"zips/zip-2003.rst\">Disallow version 4 transactions</a></td> <td>Draft</td>\n     <tr> <td>2004</td> <td class=\"left\"><a href=\"zips/zip-2004.rst\">Remove the dependency of consensus on note encryption</a></td> <td>Draft</td>\n     <tr> <td>2005</td> <td class=\"left\"><a href=\"zips/zip-2005.md\">Orchard Quantum Recoverability</a></td> <td>Proposed</td>\n+    <tr> <td>CROSSLINK</td> <td class=\"left\"><a href=\"zips/zip-CROSSLINK.md\">The Crosslink Consensus Protocol</a></td> <td>Draft</td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td>\n     <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Template for new ZIPs}</a></td> <td>Draft</td>"},{"filename":"zips/draft-shieldedlabs-crosslink-construction.rst","status":"added","additions":646,"deletions":0,"patch":"@@ -0,0 +1,646 @@\n+::\n+\n+  ZIP: Unassigned {numbers are assigned by ZIP editors}\n+  Title: CCC-SL: Crosslink Consensus Construction from Shielded Labs\n+  Owners: Nate Wilcox <nate@shieldedlabs.com>\n+  Credits: Daira-Emma Hopwood\n+           Jack Grigg\n+           Kris Nuttycombe\n+  Status: Draft\n+  Category: Consensus\n+  Created: 2026-04-08\n+  License: MIT\n+  Discussions-To: TBD (no specific discussion thread yet)\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [#BCP14]_ when, and only\n+when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as\n+defined in the Zcash protocol specification [#protocol-networks]_.\n+\n+The terms below are to be interpreted as follows:\n+\n+CCC-SL (Crosslink Consensus Construction from Shielded Labs)\n+  The hybrid consensus construction specified by this ZIP, integrating the\n+  Zcash PoW best-chain protocol with ``CFP-SL`` to produce a protocol with\n+  both availability and Crosslink Finality. CCC-SL is the core of Shielded\n+  Labs Crosslink v1 and is closely based on CCC-TFLv2 from the TFL book\n+  [#tfl-book]_.\n+\n+CCC-TFLv2 (Crosslink Consensus Construction from the TFL Book, version 2)\n+  The abstract hybrid consensus construction documented in the Zcash TFL book\n+  [#tfl-book]_. CCC-SL is the Shielded Labs adaptation and extension of this\n+  construction for the Zcash PoW+TFL deployment.\n+\n+CFP-SL (Crosslink Finalization Protocol from Shielded Labs)\n+  The BFT Proof-of-Stake finalization protocol used as the BFT subprotocol\n+  within CCC-SL. CFP-SL satisfies the interface requirements (``Final\n+  Agreement`` and related properties) defined in this ZIP.\n+\n+Best-chain subprotocol (Π\\ :sub:`bc`)\n+  The Zcash Proof-of-Work best-chain protocol, modified by CCC-SL to\n+  participate in the hybrid construction. Blocks in this protocol are called\n+  \"bc-blocks\" and chains are \"bc-chains\".\n+\n+Original best-chain subprotocol (Π\\ :sub:`origbc`)\n+  The \"off-the-shelf\" PoW best-chain protocol before CCC-SL modifications.\n+  For the Zcash deployment, this corresponds to the Zcash NU5/NU6 consensus\n+  protocol.\n+\n+BFT subprotocol (Π\\ :sub:`bft`)\n+  The BFT protocol (instantiated by ``CFP-SL``) modified to participate in\n+  CCC-SL. Blocks in this protocol are called \"bft-blocks\" and finality is\n+  called \"bft-finality\".\n+\n+Original BFT subprotocol (Π\\ :sub:`origbft`)\n+  The \"off-the-shelf\" BFT protocol before CCC-SL adaptation modifications.\n+  CFP-SL is a specific instance of this with Zcash-specific adaptations.\n+\n+bc-confirmation-depth (σ)\n+  A protocol parameter specifying the number of bc-block confirmations required\n+  before a bc-block may be included in the snapshot referenced by a bft-block.\n+  This is also the default depth used by nodes when computing the locally\n+  finalized chain.\n+\n+Finalization gap bound (L)\n+  A protocol parameter specifying the maximum number of unfinalized bc-blocks\n+  permitted before CCC-SL enforces Stalled Mode. MUST satisfy ``L ≥ 2σ``.\n+\n+Bounded-availability depth (μ)\n+  A per-node choice of confirmation depth for the locally bounded-available\n+  chain, where ``0 < μ ≤ σ``.\n+\n+Locally finalized chain (``localfin``)\n+  Each node's monotonically advancing view of the bc-chain that has been\n+  finalized by ``CFP-SL``. Once a bc-block is in a node's locally finalized\n+  chain, it will never be removed from it.\n+\n+Locally bounded-available chain (``localba``)\n+  Each node's view of the confirmed (but not necessarily finalized) bc-chain,\n+  computed using confirmation depth μ.\n+\n+Finality gap\n+  At a given time, the number of bc-blocks in the bc-best-chain that have not\n+  yet been finalized by ``CFP-SL``.\n+\n+Stalled Mode\n+  A mode enforced by CCC-SL when the finality gap exceeds ``L``, during which\n+  bc-block producers are constrained to produce only stalled blocks.\n+\n+Stalled block\n+  A bc-block produced during Stalled Mode, constrained by the ``isStalledBlock``\n+  predicate (e.g., not including new user transactions that spend into the\n+  unfinalized zone).\n+\n+Final Agreement\n+  The property of the BFT subprotocol (``CFP-SL``) that all bft-valid blocks\n+  in honest view are consistent: for any two such blocks, one must be an\n+  ancestor of the other.\n+\n+Prefix Consistency\n+  The property of the best-chain subprotocol (Zcash PoW) that any two honest\n+  nodes' confirmed best-chains are consistent up to their confirmed depths.\n+\n+Crosslink Finality\n+  The finality property provided by CCC-SL, as defined in the zebra-crosslink\n+  design documentation [#zebra-crosslink-terminology]_. Formally: for any two\n+  honest nodes ``i`` and ``j`` at any times ``t`` and ``u``, if both\n+  ``localfin_i(t)`` and ``localfin_j(u)`` are non-genesis then one is a\n+  prefix of the other (``localfin_i(t) ≼_bc localfin_j(u)`` or\n+  ``localfin_j(u) ≼_bc localfin_i(t)``). See the Security Analysis section.\n+\n+``≼_bc`` (bc-prefix relation)\n+  ``A ≼_bc B`` holds iff the bc-chain with tip ``A`` is a prefix of the\n+  bc-chain with tip ``B``; equivalently, ``A`` is an ancestor of ``B`` or\n+  ``A = B``. The notation ``≼`` (without subscript) is used as a shorthand\n+  for ``≼_bc`` when the context is unambiguous.\n+\n+``C.truncate_bc(k)``\n+  For a bc-chain ``C`` and natural number ``k``: the bc-chain obtained by\n+  removing the last ``k`` blocks from ``C``. The tip of ``C.truncate_bc(k)``\n+  is the block at depth ``k`` in ``C``. If ``len(C) ≤ k``, the result is the\n+  genesis block ``Origin_bc``. When applied to a block ``H`` (shorthand for\n+  the chain ending at ``H``), ``H.truncate_bc(k)`` denotes the block at\n+  depth ``k`` from ``H``.\n+\n+``lastCommonAncestor(A, B)``\n+  For two bc-blocks ``A`` and ``B``: the most recent (deepest) bc-block that\n+  is an ancestor of both ``A`` and ``B``. Equivalently, the tip of the longest\n+  common prefix of the bc-chains ending at ``A`` and ``B``. If one block is an\n+  ancestor of the other, ``lastCommonAncestor(A, B) = min(A, B)`` by depth.\n+\n+``bftLastFinal(B)``\n+  For a bft-block ``B``, the function returning the last bft-block that is\n+  considered final in the context of ``B`` (including ``B`` itself, if\n+  applicable). This function is defined by ``CFP-SL``.\n+\n+``snapshot(B)``\n+  For a bft-block or bft-proposal ``B``, the bc-block at the base of the\n+  bc-chain snapshot committed to by ``B``. Formally: if ``B.headers_bc`` is\n+  null (genesis), ``snapshot(B) = Origin_bc``; otherwise\n+  ``snapshot(B) = B.headers_bc[0].truncate_bc(1)``.\n+\n+``LF(H)``\n+  For a bc-block ``H``, the last final bft-block in the context of ``H``:\n+  ``LF(H) = bftLastFinal(H.context_bft)``.\n+\n+``candidate(H)``\n+  For a bc-block ``H``, the candidate finalization point:\n+  ``candidate(H) = lastCommonAncestor(snapshot(LF(H)), H.truncate_bc(σ))``.\n+\n+\n+Abstract\n+========\n+\n+This ZIP specifies **CCC-SL** (the Crosslink Consensus Construction from\n+Shielded Labs), the hybrid consensus protocol at the core of Shielded Labs\n+Crosslink v1 [#zip-crosslink-overview]_. CCC-SL integrates the Zcash\n+Proof-of-Work best-chain protocol with the ``CFP-SL`` BFT finalization\n+protocol to provide both the availability properties of the PoW chain and the\n+Crosslink Finality of a BFT protocol.\n+\n+CCC-SL is closely based on the ``CCC-TFLv2`` construction from the Zcash TFL\n+book [#tfl-book]_. The key features of CCC-SL are:\n+\n+1. Cross-referencing fields in both bc-blocks and bft-blocks/proposals that\n+   allow each chain to objectively verify properties of the other.\n+\n+2. A locally finalized chain (``localfin``) maintained by each node that\n+   advances monotonically and never rolls back.\n+\n+3. A locally bounded-available chain (``localba``) providing a\n+   confirmed-but-not-necessarily-finalized view of the PoW chain.\n+\n+4. A Stalled Mode mechanism implementing bounded availability when the\n+   finality gap exceeds the threshold ``L``.\n+\n+This ZIP is accompanied by ZIP [#zip-crosslink-overview]_, which provides the\n+design motivation and architecture overview for the full Shielded Labs\n+Crosslink v1 protocol suite.\n+\n+\n+Motivation\n+==========\n+\n+See ZIP [#zip-crosslink-overview]_ for the full motivation. In brief:\n+\n+* Zcash's current PoW-only consensus provides only probabilistic finality,\n+  creating friction for exchanges, bridges, and other services.\n+\n+* Integrating ``CFP-SL`` as a finality layer via CCC-SL preserves PoW\n+  availability and security while adding Crosslink Finality.\n+\n+* CCC-SL (following CCC-TFLv2) improves on the Snap-and-Chat construction\n+  [#ebb-and-flow]_ by eliminating the need for transaction-log sanitization\n+  and by implementing bounded availability.\n+\n+* The cross-referencing design of CCC-SL enables each chain to provide\n+  objective validity checks on the other, strengthening the security of the\n+  hybrid construction.\n+\n+\n+Requirements\n+============\n+\n+CCC-SL MUST satisfy:\n+\n+* **Crosslink Finality**: If both subprotocols satisfy their safety assumptions,\n+  the locally finalized chain of any two honest nodes must be consistent —\n+  neither node's locally finalized chain can conflict with the other's.\n+\n+* **Local Finalization Linearity**: For each honest node, the locally finalized\n+  chain is monotonically non-decreasing: it only ever advances forward, never\n+  rolls back.\n+\n+* **Prefix Consistency** (inherited from Π\\ :sub:`bc`): Two honest nodes'\n+  confirmed best-chains must be consistent.\n+\n+* **Bounded Availability**: The finality depth of any non-stalled bc-block\n+  MUST NOT exceed ``L`` while honest bc-block producers follow the protocol.\n+  (Stalled blocks may exist at finality depth greater than ``L``; the bound\n+  applies to transaction-carrying blocks only.) When the finality depth at the\n+  chain tip exceeds ``L``, bc-block producers MUST enter Stalled Mode.\n+\n+* **Objective Validity**: The validity of any bc-block MUST be determinable\n+  from the block itself and its ancestors in both chains, with no external\n+  context beyond the two chains.\n+\n+* **Minimal Subprotocol Modification**: The modifications to the PoW and\n+  ``CFP-SL`` subprotocols SHOULD be as minimal as possible while satisfying\n+  the above requirements.\n+\n+\n+Non-requirements\n+================\n+\n+The following are out of scope for this ZIP:\n+\n+* The specific BFT algorithm used internally by ``CFP-SL``. This ZIP specifies\n+  only the interface requirements (Final Agreement, etc.) that\n+  Π\\ :sub:`origbft` must satisfy. The CFP-SL specification is a separate ZIP.\n+\n+* The staking mechanics, Finalizer registration, delegation, and slashing\n+  rules (specified in the RSM-SL-v1 ZIP).\n+\n+* Reward distribution between PoW miners and Finalizers (a separate funding\n+  ZIP).\n+\n+* The specific activation height(s) for the network upgrade.\n+\n+\n+Specification\n+=============\n+\n+Parameters\n+----------\n+\n+CCC-SL is parameterized by:\n+\n+* **bc-confirmation-depth** ``σ ∈ ℕ⁺``: the number of bc-block headers\n+  included in each bft-block's ``headers_bc`` field, representing the depth\n+  at which a bc-block is considered confirmed for snapshotting purposes. This\n+  is also the default confirmation depth for the locally finalized chain.\n+\n+* **Finalization gap bound** ``L ∈ ℕ⁺``, where ``L`` is significantly greater\n+  than ``σ`` (the minimum is ``L ≥ 2σ``): the maximum finality gap before\n+  Stalled Mode is enforced.\n+\n+The precise values of ``σ`` and ``L`` for Mainnet and Testnet deployment are\n+to be determined through a network upgrade process ZIP, taking into account\n+measured CFP-SL latency and desired time-to-finality targets.\n+\n+Subprotocol Interface Requirements\n+------------------------------------\n+\n+CCC-SL places the following requirements on Π\\ :sub:`origbft` (which is\n+instantiated as ``CFP-SL`` in Shielded Labs Crosslink v1):\n+\n+* Π\\ :sub:`origbft` MUST be a protocol with an identified set of blocks\n+  forming a DAG with a defined genesis block ``Origin_bft``.\n+\n+* Π\\ :sub:`origbft` MUST define a function\n+  ``bftValid(B) → Boolean`` determining whether a bft-block ``B`` is valid\n+  in the context of the bft-chain.\n+\n+* Π\\ :sub:`origbft` MUST define a function\n+  ``bftLastFinal(B) → BftBlock`` returning the last bft-block that is\n+  \"final\" in the context of bft-block ``B``, where ``bftLastFinal(Origin_bft)\n+  = Origin_bft``.\n+\n+* Π\\ :sub:`origbft` MUST satisfy **Final Agreement**: in any execution where\n+  safety assumptions hold, for all bft-valid blocks ``C`` in honest view and\n+  all times ``t``, ``bftLastFinal(C)`` must be an ancestor of every other\n+  bft-valid block that honest nodes have finalized.\n+\n+CCC-SL places the following requirements on Π\\ :sub:`origbc` (the Zcash PoW\n+protocol):\n+\n+* Π\\ :sub:`origbc` MUST be a best-chain protocol with bc-blocks forming a tree\n+  rooted at ``Origin_bc``.\n+\n+* Π\\ :sub:`origbc` MUST define a fork-choice function selecting the \"best\n+  chain\" among all bc-valid chains known to a node.\n+\n+* Π\\ :sub:`origbc` MUST satisfy **Prefix Consistency** (Common Prefix): in any\n+  execution where assumptions hold, for any two honest nodes' confirmed chains\n+  ``C1`` and ``C2``, one is a prefix of the other.\n+\n+Structural Additions\n+--------------------\n+\n+CCC-SL extends the block formats of both subprotocols with cross-referencing\n+fields:\n+\n+bc-block structural additions\n+  Each bc-block header MUST include, in addition to all\n+  Π\\ :sub:`origbc`-block fields:\n+\n+  * ``context_bft``: a hash identifying a bft-block. This commits the\n+    bc-block producer to their view of the ``CFP-SL`` chain at the time the\n+    block was produced. The referenced bft-block MUST be a bft-valid block in\n+    the bc-block producer's view at the time of production.\n+\n+bft-proposal structural additions\n+  Each non-genesis bft-proposal MUST include, in addition to all\n+  Π\\ :sub:`origbft`-proposal fields:\n+\n+  * ``headers_bc``: a sequence of exactly ``σ`` bc-block headers (zero-indexed,\n+    deepest first, i.e., ``headers_bc[0]`` is deepest and\n+    ``headers_bc[σ-1]`` is the most recent). These headers MUST form a valid\n+    chain in Π\\ :sub:`bc` (each header's parent hash must match the previous\n+    header's hash). The genesis bft-block has ``headers_bc = null``.\n+\n+bft-block structural additions\n+  Each non-genesis bft-block MUST include, in addition to all\n+  Π\\ :sub:`origbft`-block fields:\n+\n+  * ``headers_bc``: the same sequence of ``σ`` bc-block headers as in the\n+    corresponding bft-proposal. The genesis bft-block has ``headers_bc = null``.\n+\n+Derived Functions\n+-----------------\n+\n+Based on the structural additions above, define:\n+\n+``snapshot(B)``\n+  For a bft-block or bft-proposal ``B``::\n+\n+    snapshot(B) :=\n+      if B.headers_bc == null:\n+        Origin_bc\n+      else:\n+        B.headers_bc[0].truncate_bc(1)\n+\n+  That is, the bc-block one step before the deepest header in the snapshot,\n+  i.e., the bc-block at depth ``σ+1`` relative to the tip of the snapshot\n+  chain. [#note-snapshot]_\n+\n+``LF(H)``\n+  For a bc-block ``H``::\n+\n+    LF(H) := bftLastFinal(H.context_bft)\n+\n+``candidate(H)``\n+  For a bc-block ``H``::\n+\n+    candidate(H) := lastCommonAncestor(snapshot(LF(H)), H.truncate_bc(σ))\n+\n+  When ``H`` is the tip of a node's bc-best-chain, ``candidate(H)`` gives the\n+  candidate finalization point (subject to the local finalization update rule\n+  below). It represents the deepest bc-block that both (a) has been snapshotted\n+  by the last final bft-block in context, and (b) is at least ``σ`` deep in\n+  the bc-best-chain.\n+\n+Locally Finalized Chain\n+-----------------------\n+\n+Each node ``i`` maintains a locally finalized bc-chain ``localfin_i``,\n+initialized at ``Origin_bc``. The chain state MUST NOT be exposed to external\n+clients of the node until the node has synced (see `Syncing and Checkpoints`_).\n+\n+When node ``i``'s bc-best-chain view is updated from ``ch_i(s)`` to\n+``ch_i(t)``::\n+\n+  UpdateLocalFin(localfin_i, ch_i(t), s):\n+    let N = candidate(ch_i(t))\n+    if N is a descendant of localfin_i(s) (i.e., localfin_i(s) ≼ N):\n+      localfin_i(t) ← N\n+    else:\n+      localfin_i(t) ← localfin_i(s)\n+      if N conflicts with localfin_i(s) (i.e., neither is a prefix of the other):\n+        record a finalization safety hazard\n+\n+A finalization safety hazard record SHOULD include ``ch_i(t)`` and the history\n+of ``localfin_i`` updates including and since the last update that was an\n+ancestor of ``N``.\n+\n+**Lemma (Local Finalization Linearity)**: The time series of bc-blocks\n+``localfin_i(t)`` for any honest node ``i`` is bc-linear: for all times\n+``s ≤ t``, ``localfin_i(s) ≼_bc localfin_i(t)``.\n+\n+*Proof*: By the update rule, ``localfin_i(t)`` is either ``localfin_i(s)`` or\n+a descendant of it, never a conflicting chain or an ancestor.\n+\n+**Lemma (Local Fin-Depth)**: For any honest node ``i`` at time ``t``, there\n+exists a time ``r ≤ t`` such that\n+``localfin_i(t) ≼_bc ch_i(r).truncate_bc(σ)``.\n+\n+*Proof*: By definition of ``candidate``, ``candidate(H) ≼ H.truncate_bc(σ)``\n+for all bc-blocks ``H``. Let ``r ≤ t`` be the last time ``localfin_i(t)``\n+changed, or genesis time ``0`` if it has never changed.\n+\n+Locally Bounded-Available Chain\n+---------------------------------\n+\n+Each node ``i`` chooses a bounded-availability depth ``μ`` where\n+``0 < μ ≤ σ``. The locally bounded-available chain is::\n+\n+  localba(μ)_i(t) := ch_i(t).truncate_bc(μ)\n+\n+.. warning::\n+   Choosing ``μ < σ`` is at the node's own risk and may increase susceptibility\n+   to rollback attacks. The default SHOULD be ``μ = σ``.\n+\n+Syncing and Checkpoints\n+------------------------\n+\n+When a node first syncs or re-syncs, the locally finalized chain state MUST\n+NOT be exposed to external clients until the node has processed the full bc-chain\n+and bft-chain up to a recent point. The node MAY use checkpoints embedded in\n+the software (committing to a known bc-block and bft-block at a given height)\n+to accelerate syncing.\n+\n+Checkpoints MUST be chosen conservatively to avoid committing to blocks that\n+could be reorganized or that have not been finalized by ``CFP-SL``. A\n+checkpoint at bc-height ``h`` SHOULD only be used if ``CFP-SL`` has finalized\n+bc-blocks up to height ``h - σ`` or deeper.\n+\n+Π\\ :sub:`bft` (CFP-SL) Changes from Π\\ :sub:`origbft`\n+--------------------------------------------------------\n+\n+BFT block and proposal validity\n+  In addition to Π\\ :sub:`origbft` validity rules, a bft-proposal ``P`` is\n+  bft-valid only if:\n+\n+  1. ``P.headers_bc`` is a sequence of exactly ``σ`` bc-block headers forming\n+     a valid bc-chain (headers are correctly linked by parent hashes and each\n+     satisfies the bc-block header validity rules, including proof-of-work and\n+     difficulty adjustment).\n+\n+  2. For each header ``h`` in ``P.headers_bc``, the proof-of-work MUST be\n+     valid (checking this requires only the headers, enabling DoS mitigation).\n+\n+  3. ``P.headers_bc[σ-1]`` (the most recent header) MUST be at block height\n+     at least as great as the snapshot committed to in the bft-parent of\n+     ``P``, to prevent proposers from referencing stale bc-chain snapshots.\n+\n+  4. The bc-chain committed to by ``P.headers_bc`` MUST be consistent with\n+     (i.e., a descendant of) the snapshot committed to in the bft-parent of\n+     ``P``.\n+\n+BFT honest proposal rule (CFP-SL)\n+  An honest CFP-SL Finalizer proposing a bft-block at time ``t`` MUST set\n+  ``headers_bc`` to the sequence of the most recent ``σ`` bc-block headers\n+  in the node's bc-best-chain at time ``t``. The Finalizer MUST only propose\n+  if they have a bc-best-chain that is at least ``σ`` blocks deep.\n+\n+BFT honest voting rule (CFP-SL)\n+  An honest CFP-SL Finalizer voting on a bft-proposal ``P`` MUST verify that\n+  ``P.headers_bc`` satisfies the proposal validity rules above. In particular,\n+  it MUST verify proof-of-work for all headers in ``P.headers_bc`` before\n+  accepting the proposal.\n+\n+  If ``P.headers_bc`` connects to the Finalizer's known bc-chain, the\n+  Finalizer SHOULD download and fully validate any bc-blocks it has not yet\n+  seen before voting. If the headers do not connect to any known bc-chain, the\n+  Finalizer SHOULD assign lower priority to validating the proposal (to limit\n+  DoS exposure) and MAY drop it.\n+\n+Π\\ :sub:`bc` (Zcash PoW) Changes from Π\\ :sub:`origbc`\n+---------------------------------------------------------\n+\n+BC block validity (structural)\n+  In addition to Π\\ :sub:`origbc` validity rules, a bc-block ``H`` is\n+  bc-valid only if:\n+\n+  1. ``H.context_bft`` is a hash identifying a bft-valid block in\n+     Π\\ :sub:`bft`.\n+\n+  2. ``H.context_bft`` refers to a bft-block that is an ancestor of, or\n+     equal to, the bft-block referred to by ``H``'s bc-parent's\n+     ``context_bft`` field. (Context can only move forward in the bft-chain.)\n+\n+BC contextual validity (finality gap)\n+  A bc-block ``H`` is contextually valid only if the finality gap constraint\n+  is satisfied. Let ``fin_height(H)`` be the bc-height of ``candidate(H)``\n+  and ``tip_height(H)`` be the bc-height of ``H``. The finality gap is\n+  ``tip_height(H) - fin_height(H)``.\n+\n+  If the finality gap at ``H`` exceeds ``L``, then ``H`` MUST be a stalled\n+  block (i.e., ``isStalledBlock(H)`` MUST return ``true``).\n+\n+BC honest block production\n+  An honest bc-block producer creating bc-block ``H`` at time ``t`` MUST:\n+\n+  1. Set ``H.context_bft`` to the hash of the most recent bft-valid block\n+     in the producer's CFP-SL node view at time ``t``.\n+\n+  2. Check the finality gap: if the finality gap would exceed ``L`` for the\n+     new block, the producer MUST apply the stalled-block policy.\n+\n+  3. Not include ``H.context_bft`` referencing a bft-block whose\n+     ``headers_bc[σ-1]`` refers to a bc-block that is not a prefix of\n+     ``H``'s bc-ancestor chain (consistency check).\n+\n+Stalled Mode Policy\n+--------------------\n+\n+The stalled-block predicate ``isStalledBlock(H) → Boolean`` determines whether\n+a bc-block is a stalled block. The specific definition of ``isStalledBlock``\n+for the Zcash deployment is deferred to a subsequent consensus ZIP, but MUST\n+satisfy:\n+\n+1. A stalled block MUST NOT include transactions that spend outputs created in\n+   bc-blocks that are not in the locally finalized chain (i.e., unfinalized\n+   outputs). This prevents users from having their funds locked in a deep\n+   unfinalized zone.\n+\n+2. A stalled block MAY include coinbase transactions for miner rewards.\n+\n+3. The validity of ``isStalledBlock(H)`` MUST be objectively determinable from\n+   ``H`` and its bc-ancestors (it requires no external state beyond the chain).\n+\n+4. Nodes MUST enforce the stalled block policy: a non-stalled block with a\n+   finality gap exceeding ``L`` MUST be rejected as bc-invalid.\n+\n+Open questions\n+''''''''''''''\n+\n+* Should stalled blocks be permitted to include transactions spending finalized\n+  outputs only, or should they be entirely empty of user transactions? The\n+  tradeoff is between some throughput during a stall versus specification\n+  simplicity.\n+\n+* What is the correct definition of \"unfinalized outputs\" in the context of\n+  Zcash shielded transactions, where output ownership is not publicly visible?\n+\n+TODO: Define the specific encoding of ``context_bft`` in Zcash block headers,\n+including the exact byte representation of the bft-block hash reference.\n+\n+TODO: Define the specific encoding of ``headers_bc`` in bft-proposals and\n+bft-blocks, including the exact byte layout.\n+\n+TODO: Specify the precise values of ``σ`` and ``L`` for Mainnet and Testnet.\n+\n+Security Analysis\n+-----------------\n+\n+The following properties are claimed for CCC-SL under the stated assumptions.\n+\n+Crosslink Finality\n+''''''''''''''''''\n+\n+**Property**: If ``CFP-SL`` satisfies Final Agreement and the Zcash PoW\n+protocol satisfies Prefix Consistency, then for any two honest nodes ``i`` and\n+``j`` at any times ``t`` and ``u``, their locally finalized chains are\n+consistent: either ``localfin_i(t) ≼_bc localfin_j(u)`` or\n+``localfin_j(u) ≼_bc localfin_i(t)``.\n+\n+*Proof sketch*: By the ``UpdateLocalFin`` rule, ``localfin_i(t)`` is always\n+equal to ``candidate(ch_i(r))`` for some time ``r ≤ t``. By the definition of\n+``candidate``, this is the last common ancestor of ``snapshot(LF(ch_i(r)))``\n+and ``ch_i(r).truncate_bc(σ)``.\n+\n+By Final Agreement of ``CFP-SL``, ``LF(ch_i(r))`` and ``LF(ch_j(s))`` (for\n+any two honest nodes and times) must be consistent: one is an ancestor of the\n+other in the bft-chain. By the monotonic inclusion of snapshots in bft-ancestor\n+chains and Prefix Consistency of the PoW protocol, ``candidate(ch_i(r))`` and\n+``candidate(ch_j(s))`` are consistent bc-blocks. Since both\n+``localfin_i(t)`` and ``localfin_j(u)`` are derived from ``candidate`` values\n+of consistent finalization points and consistent bc-chains, they must be\n+consistent.\n+\n+Bounded Availability\n+''''''''''''''''''''\n+\n+**Property**: If all honest bc-block producers follow the stalled-block policy,\n+then every non-stalled bc-block accepted by honest full nodes has finality depth\n+at most ``L``. (Stalled blocks may have finality depth greater than ``L``; the\n+bound applies only to transaction-carrying, non-stalled blocks.)\n+\n+*Proof sketch*: By the BC contextual validity rule, a bc-block whose finality\n+gap exceeds ``L`` that is not a stalled block is rejected as bc-invalid. Since\n+honest bc-block producers only produce stalled blocks when the gap would exceed\n+``L``, and honest full nodes reject non-stalled blocks with\n+``tip_height(H) - fin_height(H) > L``, all non-stalled blocks in any honest\n+full node's view of the bc-chain satisfy ``tip_height(H) - fin_height(H) ≤ L``.\n+Stalled blocks may accumulate arbitrarily, but they do not carry new\n+unfinalized-output-spending transactions, so the set of pending unfinalized\n+user transactions is bounded.\n+\n+Resilience of Finality to PoW Reorgs\n+''''''''''''''''''''''''''''''''''''''\n+\n+**Property**: A rollback of the bc-best-chain shorter than ``σ`` blocks does\n+not cause the locally finalized chain to roll back.\n+\n+*Proof sketch*: The candidate finalization point ``candidate(H)`` for a chain\n+tip ``H`` is at depth ``σ`` or deeper in the bc-chain (by the Local Fin-Depth\n+lemma). A reorg of fewer than ``σ`` blocks cannot affect the bc-block at depth\n+``σ``. Furthermore, the ``UpdateLocalFin`` rule only advances the locally\n+finalized chain, never rolling it back.\n+\n+\n+Reference Implementation\n+========================\n+\n+A prototype implementation of CCC-SL is being developed in the\n+``zebra-crosslink`` component of [#zebra-crosslink]_ and integrated in the\n+``crosslink_monolith`` repository [#crosslink-monolith]_. These implementations\n+are experimental and should not be used in production.\n+\n+Reference documentation for CCC-SL's theoretical foundations is available in\n+the Zcash TFL design book [#tfl-book]_, specifically the \"Crosslink 2\n+Construction\" chapter (which corresponds to CCC-TFLv2, the basis for CCC-SL).\n+\n+\n+References\n+==========\n+\n+.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2022.3.8 or later <protocol/protocol.pdf>`_\n+.. [#protocol-networks] `Zcash Protocol Specification, Version 2022.3.8. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#zip-0200] `ZIP 200: Network Upgrade Mechanism <zip-0200.rst>`_\n+.. [#zip-crosslink-overview] `ZIP [Unassigned]: Shielded Labs Crosslink v1: Protocol Overview and Architecture <draft-shieldedlabs-crosslink-overview.rst>`_\n+.. [#tfl-book] `Zcash Trailing Finality Layer Design Book — The Crosslink 2 Construction <https://electric-coin-company.github.io/tfl-book/design/crosslink/construction.html>`_\n+.. [#ebb-and-flow] `Ebb-and-Flow Protocols: A Resolution of the Availability-Finality Dilemma. David Neu, Joachim Neu, Ertem Nusret Tas, David Tse. <https://eprint.iacr.org/2020/1091.pdf>`_\n+.. [#zebra-crosslink] `ShieldedLabs zebra-crosslink repository <https://github.com/ShieldedLabs/zebra-crosslink>`_\n+.. [#crosslink-monolith] `ShieldedLabs crosslink_monolith repository <https://github.com/ShieldedLabs/crosslink_monolith>`_\n+.. [#zebra-crosslink-terminology] `Shielded Labs zebra-crosslink design book: Terminology <https://github.com/ShieldedLabs/zebra-crosslink/blob/main/book/src/design/terminology.md>`_\n+\n+.. [#note-snapshot] The snapshot function returns the bc-block at depth 1 from the deepest header, i.e., the parent of ``headers_bc[0]``. This means the snapshot is a bc-block at depth ``σ+1`` relative to the tip of the snapshotted chain. This ensures that a bft-block referring to a snapshot provides evidence (via the ``σ`` headers) that the snapshot was confirmed at the time of the proposal."},{"filename":"zips/draft-shieldedlabs-crosslink-ledger-state.rst","status":"added","additions":261,"deletions":0,"patch":"@@ -0,0 +1,261 @@\n+::\n+\n+  ZIP: Unassigned {numbers are assigned by ZIP editors}\n+  Title: RSM-SL-v1: Crosslink Ledger State and Ledger Mutations\n+  Owners: Nate Wilcox <nate@shieldedlabs.com>\n+  Credits: Daira-Emma Hopwood\n+           Jack Grigg\n+           Kris Nuttycombe\n+  Status: Draft\n+  Category: Consensus\n+  Created: 2026-06-03\n+  License: MIT\n+  Discussions-To: TBD (no specific discussion thread yet)\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this\n+text are to be interpreted as described in BCP 14 [#BCP14]_ when, and only\n+when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as\n+defined in the Zcash protocol specification [#protocol-networks]_.\n+\n+The terms below are to be interpreted as follows:\n+\n+RSM-SL-v1\n+  The Shielded Labs v1 instantiation of the roster state module referenced by\n+  the Crosslink Overview ZIP [#zip-crosslink-overview]_.\n+\n+Idealized ledger state\n+  A point-in-time abstract data structure used for specification and consensus\n+  reasoning. Implementations MAY use equivalent internal layouts, but MUST\n+  produce exactly the same externally verifiable state transitions as if they\n+  had updated this idealized structure directly.\n+\n+Bond\n+  A delegated ZEC amount associated with a Finalizer identifier and tracked by\n+  this specification's ledger state rules.\n+\n+Candidate Roster\n+  A deterministic mapping from Finalizer identifiers to voting weights,\n+  computed by summing all currently effective bonds for each Finalizer.\n+\n+Active Finalizer set\n+  The top ``K`` Finalizers by voting weight in the Candidate Roster, where\n+  ``K = 100``.\n+\n+Most-recent-final ancestor\n+  For any PoW tip block, the unique most recent ancestor on that tip's chain\n+  that is finalized under CCC-SL [#zip-ccc-sl]_.\n+\n+\n+Abstract\n+========\n+\n+This ZIP specifies the Crosslink ledger-state changes required by RSM-SL-v1.\n+It defines the idealized point-in-time ledger state extension, and the ledger\n+mutation rules that update this state, including issuance distribution and\n+roster mutations.\n+\n+The specification is layered:\n+\n+* abstract Proof-of-Stake actions over ledger state, and\n+* concrete consensus extensions to Zcash transaction fields and semantics.\n+\n+\n+Motivation\n+==========\n+\n+The Crosslink Overview ZIP defines RSM-SL-v1 conceptually but leaves the\n+consensus state and mutation details to a companion specification. Nodes need a\n+single, deterministic definition of:\n+\n+* how Finalizer voting weights are derived from on-chain bonds,\n+* how the active BFT roster is selected, and\n+* how roster-related state evolves as blocks are applied and reverted.\n+\n+Without this ZIP, implementations may diverge on ledger state interpretation,\n+producing incompatible active rosters and finality inputs.\n+\n+\n+Requirements\n+============\n+\n+This ZIP MUST specify the following:\n+\n+* An idealized ledger state extension over the existing Zcash ledger state,\n+  including bonds, Finalizer identifiers, and cryptographic material needed for\n+  self-authenticating roster membership.\n+* Candidate Roster construction by summing currently effective bonds per\n+  Finalizer, and Active Finalizer set selection as the top ``K = 100`` by\n+  voting weight.\n+* Dual-state interpretation in which ledger processing distinguishes finalized\n+  state from tip state, with each tip having an objectively verifiable\n+  most-recent-final ancestor.\n+* Ledger mutation rules for issuance distribution and roster mutations.\n+* A two-layer mutation definition:\n+\n+  1. abstract PoS actions, and\n+  2. concrete Zcash transaction-field and transaction-semantic extensions.\n+\n+\n+Non-requirements\n+================\n+\n+The following are out of scope for this ZIP:\n+\n+* Pre-existing Zcash ledger state, except where it is extended or requires new\n+  considerations due to Crosslink.\n+* The Crosslink mechanism itself (including chain selection and finality-status\n+  verification), specified in CCC-SL [#zip-ccc-sl]_.\n+* Internal BFT protocol design, except that the Active Finalizer set is an\n+  input provided by this ZIP.\n+* Networking behavior outside transaction format and transaction semantics.\n+* Topics designated as separate ZIPs in the Crosslink overview\n+  [#zip-crosslink-overview]_.\n+\n+\n+Specification\n+=============\n+\n+Idealized Ledger State Extension\n+--------------------------------\n+\n+Let ``S_zec`` denote the existing Zcash ledger state. This ZIP defines an\n+extended idealized state ``S_xl`` that behaves as:\n+\n+* ``S_xl = (S_zec, S_roster, S_finality_views, S_issuance)``.\n+* ``S_roster`` includes at minimum:\n+\n+  * bond records keyed by bond identifier,\n+  * Finalizer identity records,\n+  * cryptographic verification keys and status metadata,\n+  * deterministic activation/deactivation metadata needed for roster\n+    computation.\n+\n+* ``S_finality_views`` tracks finalized and tip-relative views so that, for each\n+  accepted tip, the corresponding most-recent-final ancestor is objectively\n+  derivable from block data and finalized commitments.\n+* ``S_issuance`` tracks distribution state required to apply issuance rules that\n+  involve Finalizers.\n+\n+Consensus behavior MUST be equivalent to transitions over ``S_xl`` even when an\n+implementation does not materialize ``S_xl`` as one in-memory object.\n+\n+Candidate Roster and Active Finalizers\n+--------------------------------------\n+\n+For each state ``S_xl`` at a given chain point:\n+\n+* Candidate voting weight for Finalizer ``f`` is the sum of all currently\n+  effective bond amounts targeting ``f``.\n+* Candidate Roster computation MUST be deterministic for every honest node given\n+  the same chain history.\n+* The Active Finalizer set is the top ``K = 100`` Finalizers by candidate voting\n+  weight.\n+* Tie-breaking for equal weights MUST be deterministic and based only on\n+  consensus-visible data.\n+* The active roster output MUST be suitable as an input to CFP-SL voting-weight\n+  selection.\n+\n+Finalized and Tip State Relationship\n+------------------------------------\n+\n+Ledger processing MUST expose both:\n+\n+* a finalized state view anchored at finalized history, and\n+* a tip state view for each accepted PoW tip.\n+\n+For every accepted tip, nodes MUST be able to derive an objectively verifiable\n+most-recent-final ancestor from consensus data. Reorganizations MUST preserve\n+this invariant and recompute dependent tip state deterministically.\n+\n+Ledger Mutations: Abstract Action Layer\n+---------------------------------------\n+\n+This ZIP defines abstract actions that mutate ``S_xl``:\n+\n+* ``BondCreate``: create a bond linking stake to a Finalizer identifier.\n+* ``BondAdjust``: increase or decrease an existing bond according to consensus\n+  constraints.\n+* ``BondDeactivate``: transition stake out of active weighting, including\n+  unbonding behavior.\n+* ``FinalizerRegister`` and ``FinalizerUpdate``: manage Finalizer identity and\n+  associated cryptographic keys.\n+* ``FinalizerDeactivate``: remove a Finalizer from future roster eligibility.\n+* ``Slash``: apply deterministic penalties for provable misbehavior.\n+* ``DistributeIssuance``: apply issuance distribution updates affecting miner\n+  and Finalizer-related allocation state.\n+\n+Each action MUST define preconditions, state transition effects, and reversion\n+behavior under chain rollback.\n+\n+Ledger Mutations: Concrete Zcash Transaction Semantics\n+------------------------------------------------------\n+\n+Consensus transaction rules are extended so that specific transaction patterns\n+encode the abstract actions above. This layer MUST define:\n+\n+* the concrete transaction fields and encodings used for roster-related actions,\n+* validity checks for signatures, authorization, and amount constraints,\n+* ordering and interaction rules when multiple roster actions appear in one\n+  block,\n+* rejection behavior for malformed, unauthorized, or semantically conflicting\n+  actions.\n+\n+Applying a valid block MUST induce exactly the same abstract action sequence on\n+``S_xl`` at all honest nodes.\n+\n+Issuance Distribution and Roster Interaction\n+--------------------------------------------\n+\n+Issuance distribution rules covered by this ZIP MUST specify:\n+\n+* which issuance components are affected by roster/finalizer state,\n+* when issuance state updates occur relative to other per-block mutations, and\n+* deterministic accounting behavior under normal progression and reorgs.\n+\n+Detailed policy values (for example, exact allocation percentages) MAY be set by\n+separate process or funding ZIPs, but this ZIP defines the consensus state\n+transition hooks that enforce whichever policy is activated.\n+\n+\n+Rationale\n+=========\n+\n+Separating this specification from CCC-SL keeps concerns modular:\n+\n+* CCC-SL specifies the hybrid-consensus construction and finality properties.\n+* This ZIP specifies the ledger-state machine that supplies active roster input\n+  and applies roster-related mutations inside Zcash consensus processing.\n+\n+This split keeps each ZIP auditable while preserving deterministic integration.\n+\n+\n+Deployment\n+==========\n+\n+This ZIP is intended to activate through the Zcash network-upgrade mechanism\n+[#zip-0200]_ alongside the other required Crosslink ZIPs.\n+\n+\n+Reference Implementation\n+========================\n+\n+A prototype implementation is under development in the Shielded Labs\n+``zebra-crosslink`` and ``crosslink_monolith`` repositories, referenced from\n+ZIP [#zip-crosslink-overview]_.\n+\n+\n+References\n+==========\n+\n+.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2022.3.8 or later <protocol/protocol.pdf>`_\n+.. [#protocol-networks] `Zcash Protocol Specification, Version 2022.3.8. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#zip-0200] `ZIP 200: Network Upgrade Mechanism <zip-0200.rst>`_\n+.. [#zip-ccc-sl] `ZIP [Unassigned]: CCC-SL: Crosslink Consensus Construction from Shielded Labs <draft-shieldedlabs-crosslink-construction.rst>`_\n+.. [#zip-crosslink-overview] `ZIP [Unassigned]: Shielded Labs Crosslink v1: Protocol Overview and Architecture <draft-shieldedlabs-crosslink-overview.rst>`_"},{"filename":"zips/draft-shieldedlabs-crosslink-overview.rst","status":"added","additions":726,"deletions":0,"patch":"@@ -0,0 +1,726 @@\n+::\n+\n+  ZIP: Unassigned {numbers are assigned by ZIP editors}\n+  Title: Shielded Labs Crosslink v1: Protocol Overview and Architecture\n+  Owners: Nate Wilcox <nate@shieldedlabs.com>\n+  Credits: Daira-Emma Hopwood\n+           Jack Grigg\n+           Kris Nuttycombe\n+  Status: Draft\n+  Category: Informational\n+  Created: 2026-04-08\n+  License: MIT\n+  Discussions-To: TBD (no specific discussion thread yet)\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [#BCP14]_ when, and only\n+when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as\n+defined in the Zcash protocol specification [#protocol-networks]_.\n+\n+The terms below are to be interpreted as follows:\n+\n+Zcash Trailing Finality Layer (TFL)\n+  An initiative by the former Electric Coin Company (ECC) to augment the\n+  Zcash Proof-of-Work consensus with a Proof-of-Stake finality layer,\n+  documented in the TFL book [#tfl-book]_. The TFL initiative produced the\n+  theoretical design basis for Shielded Labs Crosslink v1.\n+\n+Crosslink Consensus Construction from the TFL Book (CCC-TFLv2)\n+  The abstract hybrid consensus construction documented in the TFL book\n+  [#tfl-book]_, version 2. CCC-TFLv2 describes how to integrate a best-chain\n+  (PoW) protocol with a BFT (PoS) protocol and provides a formal security\n+  analysis of the resulting hybrid. This construction is the direct theoretical\n+  basis for ``CCC-SL``.\n+\n+Shielded Labs Crosslink v1\n+  The complete protocol and working implementation produced by the Shielded\n+  Labs non-profit, extending CCC-TFLv2 with additional practical and\n+  theoretical design details necessary for a production deployment. Shielded\n+  Labs Crosslink v1 is a superset of the TFL in several respects: it includes\n+  a fully prototyped working implementation, and it extends the older TFL\n+  design with additional components (``CFP-SL``, ``RSM-SL-v1``, ``CNTP-SL``)\n+  that are not fully specified in the TFL book. This ZIP and its companions\n+  specify the protocols of Shielded Labs Crosslink v1.\n+\n+Crosslink Consensus Construction from Shielded Labs (CCC-SL)\n+  The abstract hybrid consensus construction that is the core of Shielded Labs\n+  Crosslink v1, inspired by CCC-TFLv2. CCC-SL treats the Zcash PoW protocol\n+  and ``CFP-SL`` as somewhat opaque subprotocols, each performing their own\n+  consensus, while requiring specific cross-referencing modifications to each.\n+  CCC-SL is specified in ZIP [#zip-ccc-sl]_.\n+\n+Crosslink Finalization Protocol from Shielded Labs (CFP-SL)\n+  The Proof-of-Stake BFT protocol used as the finalization subprotocol in\n+  Shielded Labs Crosslink v1, with \"adaptation modifications\" to interface\n+  with ``CCC-SL``. ``CFP-SL`` provides Crosslink Finality by running Byzantine\n+  Fault Tolerant consensus over snapshots of the Zcash PoW chain.\n+\n+Roster State Module (RSM)\n+  An abstract interface required by ``CFP-SL``. The RSM provides the latest\n+  ``Active Roster Mapping`` from cryptographically self-authenticating vote\n+  verifier identities to their associated voting weights. The Active Roster\n+  Mapping also defines ``Roster Evolution Rules``, which are a subset of\n+  ledger state evolution rules composing Zcash's ledger state consensus.\n+\n+RSM-SL-v1\n+  The specific instantiation of the Roster State Module for Shielded Labs\n+  Crosslink v1. RSM-SL-v1 defines all staking rules that users interact with\n+  directly: staking ZEC to a Finalizer identity, unbonding periods, amount\n+  quantization, slashing conditions, and related rules.\n+\n+Crosslink Network Transport Protocol from Shielded Labs (CNTP-SL)\n+  The network transport layer for Shielded Labs Crosslink v1, enabling\n+  ``CFP-SL`` to safely and efficiently disseminate finality updates.\n+  ``CNTP-SL`` provides pragmatic protections against network threats (DoS,\n+  message tampering, etc.) and improvements over the status quo Zcash p2p\n+  protocol, including transport-layer encryption, faster block and ledger\n+  syncing, and firewall traversal.\n+\n+Active Roster Mapping\n+  A mapping from Finalizer vote-verifier public keys to their associated\n+  voting weights, maintained by the Roster State Module. This mapping defines\n+  which parties are authorized to participate in ``CFP-SL`` voting at a given\n+  point in the ledger history.\n+\n+Finalizer\n+  A participant in the ``CFP-SL`` subprotocol who holds an active entry in\n+  the Active Roster Mapping and participates in BFT consensus to finalize\n+  PoW blocks.\n+\n+Roster Evolution Rules\n+  The subset of ledger state evolution rules (within Zcash's consensus) that\n+  govern how the Active Roster Mapping changes over time: new stakers joining,\n+  unstaking and unbonding, slashing, and similar events.\n+\n+Crosslink Finality\n+  The finality property provided by Shielded Labs Crosslink v1, as defined in\n+  the zebra-crosslink design documentation [#zebra-crosslink-terminology]_.\n+  Crosslink Finality has five key characteristics:\n+\n+  1. **Accountable**: Safety violations can be attributed to specific\n+     Finalizers, enabling slashing penalties.\n+  2. **Irreversible**: Once a block achieves Crosslink Finality, it cannot be\n+     reverted by the protocol under any circumstances permitted by the security\n+     assumptions.\n+  3. **Objectively verifiable**: Any third party with access to the chain data\n+     can independently verify whether a given block has achieved Crosslink\n+     Finality without trusting any particular node.\n+  4. **Globally consistent**: All honest nodes that agree on the chain data\n+     agree on which blocks have achieved Crosslink Finality.\n+  5. **Asymmetric cost-of-attack defense**: An adversary seeking to violate\n+     Crosslink Finality must expend significantly more resources than honest\n+     participants spend to maintain it.\n+\n+Trailing Finality\n+  A protocol property wherein transactions become final some time after first\n+  appearing in PoW blocks.\n+\n+Bounded Availability\n+  The property that the best-chain ledger does not grow unboundedly ahead of\n+  the finalized ledger during a finality stall, preventing indefinite\n+  accumulation of unfinalized transactions.\n+\n+Finality Gap\n+  The number of best-chain (PoW) blocks that have not yet been finalized by\n+  ``CFP-SL`` at a given time.\n+\n+Stalled Mode\n+  A mode entered when the finality gap exceeds a threshold ``L``, indicating\n+  an exceptional or emergency condition. In Stalled Mode, PoW block producers\n+  are constrained by the ``CCC-SL`` rules to produce only stalled blocks.\n+\n+\n+Abstract\n+========\n+\n+This ZIP describes the high-level design goals, motivations, and architecture\n+of **Shielded Labs Crosslink v1**, a complete protocol and implementation\n+produced by the Shielded Labs non-profit to add Crosslink Finality to the\n+Zcash network.\n+\n+Shielded Labs Crosslink v1 builds on the Zcash Trailing Finality Layer (TFL)\n+initiative and its ``CCC-TFLv2`` hybrid construction, extending it into a\n+deployable system with a fully specified finalization protocol (``CFP-SL``),\n+staking rules (``RSM-SL-v1``), and network transport layer (``CNTP-SL``). The\n+core hybrid consensus mechanism is ``CCC-SL``, which is formally specified in\n+ZIP [#zip-ccc-sl]_.\n+\n+This document is intended as the top-level overview for the entire Shielded\n+Labs Crosslink v1 protocol suite. It does not itself specify consensus rules,\n+but provides the rationale, component definitions, architecture diagram, and\n+cross-references needed to understand and evaluate the companion ZIPs.\n+\n+\n+Background: The TFL Initiative and CCC-TFLv2\n+=============================================\n+\n+The Zcash Trailing Finality Layer (TFL) was an initiative by the former\n+Electric Coin Company (ECC) to introduce Crosslink Finality to Zcash through\n+a hybrid Proof-of-Work / Proof-of-Stake consensus design. The TFL design book\n+[#tfl-book]_ documents this initiative and produces the Crosslink Consensus\n+Construction (version 2, ``CCC-TFLv2``), which provides the theoretical\n+framework and security analysis for integrating a PoW chain with a BFT\n+finality layer.\n+\n+Shielded Labs Crosslink v1 is a successor to the TFL initiative, produced by\n+the Shielded Labs non-profit. While ``CCC-TFLv2`` provides the abstract\n+consensus foundation, many practical design and implementation details were\n+left unspecified in the TFL book. Shielded Labs has extended the TFL work in\n+several important ways:\n+\n+* **CCC-SL**: A fully specified consensus construction closely following\n+  CCC-TFLv2 but with practical adaptations and Zcash-specific details.\n+\n+* **CFP-SL**: A complete specification and prototype implementation of the\n+  BFT finalization protocol (the ``Π_bft`` component left abstract in\n+  CCC-TFLv2).\n+\n+* **RSM-SL-v1**: A full specification of the staking rules and roster\n+  management (the \"PoS staking mechanics\" left unspecified in CCC-TFLv2).\n+\n+* **CNTP-SL**: A network transport layer for finality updates (entirely new\n+  relative to CCC-TFLv2, which does not address the network layer).\n+\n+* **Working prototype**: A complete prototype implementation based on Zebra\n+  [#zebra-crosslink]_ demonstrating all components working together.\n+\n+Readers who wish to understand the theoretical foundations of the consensus\n+construction are encouraged to read the TFL book [#tfl-book]_ alongside these\n+ZIPs, with the understanding that Shielded Labs Crosslink v1 may use different\n+terminology and make different design trade-offs in some areas.\n+\n+\n+Motivation\n+==========\n+\n+Probabilistic vs. Crosslink Finality\n+--------------------------------------\n+\n+The current Zcash consensus protocol uses Nakamoto-style Proof-of-Work, which\n+provides only *probabilistic* finality: a transaction confirmed at depth ``k``\n+can theoretically be reversed by an adversary with sufficient hash rate.\n+Although deep reorgs are costly and increasingly unlikely as depth increases,\n+they are not impossible. This limitation has several practical consequences:\n+\n+* **Exchange and service delays**: Exchanges and other services must wait for\n+  many confirmations before crediting deposits, increasing friction for users.\n+\n+* **Bridge trust assumptions**: Cross-chain bridges and atomic swaps must\n+  account for reorg risk, often requiring conservative confirmation depths or\n+  additional trust assumptions.\n+\n+* **Wallet complexity**: Wallets must communicate uncertainty about\n+  transaction finality to users, complicating UX.\n+\n+Crosslink Finality — the property that a finalized transaction *cannot* be\n+reversed by the protocol — removes these limitations. Any system that can\n+rely on Crosslink Finality can offer faster, safer service with simpler\n+reasoning about transaction status.\n+\n+Why Proof-of-Stake for Finality?\n+---------------------------------\n+\n+Proof-of-Stake (PoS) consensus protocols using Byzantine Fault Tolerant (BFT)\n+algorithms are well-established approaches for providing Crosslink Finality.\n+Tendermint, PBFT, Casper, and related protocols all achieve this goal with\n+strong theoretical foundations. Integrating a BFT PoS layer into Zcash as a\n+*finality layer* — rather than replacing PoW entirely — allows the ecosystem to:\n+\n+1. **Retain PoW security**: The PoW chain continues to provide its well-understood\n+   liveness and censorship-resistance properties.\n+\n+2. **Add PoS staking rewards**: ZEC holders can participate in network security\n+   by staking ZEC (directly or by delegating to a Finalizer), earning protocol\n+   rewards.\n+\n+3. **Minimize disruption**: The transition preserves existing wallets, services,\n+   and tooling with minimal required changes.\n+\n+4. **Enable future evolution**: A successful hybrid PoW/PoS transition builds\n+   confidence and infrastructure for potential future full PoS transitions.\n+\n+Why CCC-SL Rather Than Snap-and-Chat?\n+---------------------------------------\n+\n+An earlier candidate for the hybrid construction was the Snap-and-Chat\n+protocol from the Ebb-and-Flow paper [#ebb-and-flow]_. The TFL initiative\n+identified several issues with Snap-and-Chat [#tfl-book]_, motivating the\n+development of CCC-TFLv2 (and by extension CCC-SL):\n+\n+* Snap-and-Chat's \"sanitization\" step complicates transaction finality\n+  semantics in ways that interact poorly with Zcash's shielded transaction\n+  model.\n+\n+* Snap-and-Chat does not implement bounded availability, which is a safety\n+  property important for protecting users during finality stalls.\n+\n+* The security analysis of CCC-TFLv2 / CCC-SL is more rigorous and complete\n+  than what is available for Snap-and-Chat.\n+\n+The Case for Bounded Availability\n+-----------------------------------\n+\n+A key design goal of CCC-SL is *bounded availability*: preventing the PoW\n+chain from growing unboundedly ahead of the finalized chain during a finality\n+stall. Without this property, a period of ``CFP-SL`` failure could allow\n+unbounded accumulation of unfinalized transactions. When ``CFP-SL`` recovered,\n+users would face uncertainty about which transactions would ultimately be\n+finalized and which would be reversed.\n+\n+Bounded availability addresses this by entering \"Stalled Mode\" when the\n+finality gap exceeds a threshold ``L``. In Stalled Mode, PoW block producers\n+are constrained to produce only \"stalled blocks\" — blocks that do not include\n+new user transactions spending into the unfinalized zone. This limits user\n+exposure during finalization failures.\n+\n+\n+Requirements\n+============\n+\n+Shielded Labs Crosslink v1 MUST satisfy the following requirements:\n+\n+Finality Requirements\n+---------------------\n+\n+* The protocol MUST provide Crosslink Finality for transactions included in\n+  finalized blocks.\n+\n+* The expected time-to-finality SHOULD be below 30 minutes under normal\n+  network conditions.\n+\n+* The protocol MUST maintain Local Finalization Linearity: from any single\n+  node's perspective, the locally-finalized chain can only grow forward, never\n+  roll back.\n+\n+Safety Requirements\n+--------------------\n+\n+* The cost-of-attack for a 1-hour rollback of the PoW chain MUST NOT be\n+  reduced compared to the current Zcash consensus protocol, given a reasonably\n+  rigorous security argument.\n+\n+* The cost-of-attack to halt the chain MUST be larger than the 24-hour revenue\n+  of PoW mining rewards, given a reasonably rigorous security argument.\n+\n+Backward Compatibility Requirements\n+-------------------------------------\n+\n+* All currently supported wallet operations (addresses, payment flow, ZEC\n+  transfers, backup/restore, etc.) MUST continue to function without change\n+  through the protocol transition, assuming wallets rely on the lightwalletd\n+  protocol or a full-node API.\n+\n+* Mining pool operators relying on zcashd-compatible ``GetBlockTemplate``\n+  MUST NOT be required to make software changes, with the narrow exception of\n+  software that hardcodes block reward amounts rather than deriving them from\n+  ``GetBlockTemplate``.\n+\n+* The ZEC supply cap and issuance schedule MUST be preserved.\n+\n+* Block explorers that display transaction and block information MUST continue\n+  to function with the same user experience through the transition.\n+\n+Bounded Availability Requirements\n+-----------------------------------\n+\n+* ``CCC-SL`` MUST implement bounded availability: the finality depth of any\n+  non-stalled bc-block MUST NOT exceed ``L``, where ``L`` is a protocol\n+  parameter. (Stalled blocks may exist at any finality depth; Bounded\n+  Availability bounds the depth of *transaction-carrying* blocks only.)\n+\n+* ``L`` MUST be set significantly larger than the bc-confirmation-depth ``σ``,\n+  with a minimum of ``L ≥ 2σ``.\n+\n+Staking Requirements\n+--------------------\n+\n+* ZEC holders MUST be able to earn protocol rewards by operating as Finalizers\n+  or delegating ZEC to Finalizers via ``RSM-SL-v1``.\n+\n+* Finalizers MUST NOT have discretionary control over delegated funds; they\n+  MUST NOT be able to steal delegated ZEC.\n+\n+* The protocol MUST allow slashing of Finalizer stake for provable misbehavior.\n+\n+\n+Non-requirements\n+================\n+\n+The following are explicitly out of scope for Shielded Labs Crosslink v1:\n+\n+* Minimizing time-to-finality as a primary goal over other considerations such\n+  as protocol simplicity or impact on existing use cases.\n+\n+* In-protocol liquid staking derivatives.\n+\n+* Maximizing the Finalizer count ceiling. The design may have a relatively low\n+  ceiling of hundreds to a few thousand Finalizers.\n+\n+* Reducing overall network energy usage (this cannot be achieved for a\n+  hybrid PoW/PoS design without loss of security).\n+\n+\n+Specification\n+=============\n+\n+Protocol Component Overview\n+-----------------------------\n+\n+Shielded Labs Crosslink v1 is composed of five major protocol components that\n+interact with the existing Zcash PoW consensus and ledger rules:\n+\n+1. **CCC-SL** — the hybrid consensus construction at the core of Crosslink v1.\n+2. **CFP-SL** — the BFT PoS finalization protocol run by Finalizers.\n+3. **RSM-SL-v1** — the Roster State Module that manages the Active Roster\n+   Mapping and staking rules.\n+4. **CNTP-SL** — the network transport layer for finality updates.\n+5. **Zcash PoW consensus** (existing, with minimal CCC-SL modifications).\n+\n+Component Diagram\n+-----------------\n+\n+The following diagram illustrates the components of Shielded Labs Crosslink v1\n+and their interactions. Arrows indicate data flow or dependency::\n+\n+                 ┌──────────────────────────────────────────────────────────┐\n+                 │              Shielded Labs Crosslink v1                  │\n+                 │                                                          │\n+   ┌──────────┐  │  ┌────────────────────────────────────────────────────┐  │\n+   │  Zcash   │  │  │    CCC-SL  (Crosslink Consensus Construction       │  │\n+   │  PoW     │◄─┼──│             from Shielded Labs)                    │  │\n+   │  (Π_bc)  │──┼─►│  • context_bft field in bc-block headers          │  │\n+   └──────────┘  │  │  • Finality gap / Stalled Mode enforcement        │  │\n+        ▲        │  │  • Locally finalized chain (localfin)             │  │\n+        │        │  │  • Locally bounded-available chain (localba)      │  │\n+        │        │  └──────────────────────┬─────────────────────────────┘  │\n+        │        │                         │                                 │\n+        │        │  ┌──────────────────────▼─────────────────────────────┐  │\n+        │        │  │    CFP-SL  (Crosslink Finalization Protocol        │  │\n+        │        │  │             from Shielded Labs)                    │  │\n+        └────────┼──│  • BFT PoS consensus over PoW chain snapshots     │  │\n+                 │  │  • headers_bc in bft-proposals and bft-blocks     │  │\n+                 │  │  • Final Agreement property                       │  │\n+                 │  │  • Adaptation modifications for CCC-SL interface  │  │\n+                 │  └────────────┬──────────────────┬────────────────────┘  │\n+                 │               │                  │                        │\n+                 │  ┌────────────▼───────────┐  ┌──▼──────────────────────┐ │\n+                 │  │  RSM-SL-v1             │  │  CNTP-SL                │ │\n+                 │  │  (Roster State Module) │  │  (Crosslink Network     │ │\n+                 │  │                        │  │   Transport Protocol)   │ │\n+                 │  │  • Active Roster       │  │                         │ │\n+                 │  │    Mapping             │  │  • Finality update      │ │\n+                 │  │  • Roster Evolution    │  │    dissemination        │ │\n+                 │  │    Rules               │  │  • Transport encryption │ │\n+                 │  │  • Staking rules:      │  │  • Faster sync          │ │\n+                 │  │    - ZEC delegation    │  │  • Firewall traversal   │ │\n+                 │  │    - Unbonding periods │  │  • DoS protection       │ │\n+                 │  │    - Slashing          │  │                         │ │\n+                 │  │    - Quantization      │  │                         │ │\n+                 │  └────────────────────────┘  └─────────────────────────┘ │\n+                 │                                                           │\n+                 └───────────────────────────────────────────────────────────┘\n+\n+Component Descriptions\n+-----------------------\n+\n+CCC-SL: Crosslink Consensus Construction from Shielded Labs\n+  CCC-SL is the core hybrid consensus construction specifying how the Zcash\n+  PoW subprotocol and ``CFP-SL`` interact. It is closely based on CCC-TFLv2\n+  and provides the following key mechanisms:\n+\n+  * **Cross-referencing fields**: Each PoW block header includes a\n+    ``context_bft`` field referencing a ``CFP-SL`` block; each ``CFP-SL``\n+    proposal includes a ``headers_bc`` field with ``σ`` PoW block headers.\n+    These fields allow each chain to objectively verify properties of the\n+    other.\n+\n+  * **Locally finalized chain**: Each node maintains a locally finalized\n+    bc-chain (``localfin``) that only ever advances forward, never rolling\n+    back.\n+\n+  * **Locally bounded-available chain**: Each node also maintains a confirmed\n+    (but not necessarily finalized) bc-chain view (``localba``) at a chosen\n+    confirmation depth.\n+\n+  * **Stalled Mode**: When the finality gap (number of unfinalized PoW blocks)\n+    exceeds ``L``, CCC-SL enforces Stalled Mode, constraining PoW block\n+    producers to produce only stalled blocks.\n+\n+  CCC-SL is formally specified in ZIP [#zip-ccc-sl]_.\n+\n+CFP-SL: Crosslink Finalization Protocol from Shielded Labs\n+  CFP-SL is the BFT Proof-of-Stake consensus protocol run by Finalizers to\n+  provide Crosslink Finality. It operates over snapshots of the Zcash PoW chain:\n+  each CFP-SL proposal commits to a sequence of PoW block headers, and\n+  finalized CFP-SL blocks determine which PoW blocks are considered final.\n+\n+  CFP-SL is a modified version of an off-the-shelf BFT protocol, with\n+  adaptation modifications to:\n+\n+  * Include ``headers_bc`` in proposals and blocks (required by CCC-SL).\n+  * Consult the Active Roster Mapping from ``RSM-SL-v1`` for voter weights.\n+  * Verify PoW header validity before voting on proposals.\n+\n+  The specific off-the-shelf BFT algorithm used by CFP-SL will be specified in\n+  a companion ZIP. CFP-SL must satisfy the **Final Agreement** property: in any\n+  execution where safety assumptions hold, all honest nodes' views of finalized\n+  CFP-SL blocks are consistent.\n+\n+RSM-SL-v1: Roster State Module (Shielded Labs v1)\n+  RSM-SL-v1 is the specific instantiation of the abstract Roster State Module\n+  interface required by CFP-SL. It maintains the Active Roster Mapping that\n+  determines which Finalizer identities are authorized to vote in CFP-SL and\n+  with what weight.\n+\n+  RSM-SL-v1 defines all staking rules that users interact with directly,\n+  including:\n+\n+  * **ZEC delegation**: Staking ZEC to a Finalizer vote-verifier identity.\n+  * **Unbonding periods**: The mandatory waiting period before staked ZEC can\n+    be withdrawn after unbonding.\n+  * **Amount quantization**: Minimum and increment sizes for staking amounts.\n+  * **Slashing**: Conditions and amounts for penalizing provably misbehaving\n+    Finalizers, enforced as Roster Evolution Rules.\n+  * **Roster Evolution Rules**: Ledger state evolution rules that govern how\n+    the Active Roster Mapping changes over time as part of Zcash's broader\n+    ledger state consensus.\n+\n+  RSM-SL-v1 is specified in ZIP [#zip-rsm-sl-v1]_.\n+\n+CNTP-SL: Crosslink Network Transport Protocol from Shielded Labs\n+  CNTP-SL is the network transport layer providing the communication substrate\n+  for Shielded Labs Crosslink v1. Although not strictly logically required for\n+  a correct Crosslink deployment (any sufficiently reliable broadcast network\n+  would suffice), CNTP-SL provides important practical properties:\n+\n+  * **Finality update dissemination**: Efficient propagation of CFP-SL blocks\n+    and finality updates to all network participants, including light clients.\n+  * **DoS protection**: Network-layer defenses against denial-of-service\n+    attacks targeting the finalization protocol.\n+  * **Transport-layer encryption**: Authenticated, encrypted communication\n+    between Crosslink v1 nodes.\n+  * **Faster sync protocols**: Improved block and ledger synchronization\n+    compared to the status quo Zcash p2p protocol.\n+  * **Firewall traversal**: Support for nodes behind NAT/firewalls.\n+\n+  CNTP-SL will be specified in a companion ZIP.\n+\n+Interaction Between Components\n+--------------------------------\n+\n+The components of Shielded Labs Crosslink v1 interact as follows:\n+\n+1. **Zcash PoW ↔ CCC-SL**: CCC-SL augments PoW block validity rules with\n+   the ``context_bft`` cross-reference requirement and the Stalled Mode\n+   policy. Honest PoW block producers consult their CFP-SL node to determine\n+   the current BFT chain tip for ``context_bft``.\n+\n+2. **CCC-SL ↔ CFP-SL**: CCC-SL defines the interface requirements CFP-SL must\n+   satisfy (Final Agreement, the ``headers_bc`` format, and the\n+   ``bftLastFinal`` function). CFP-SL produces finalized blocks that CCC-SL\n+   uses to compute ``localfin``. CFP-SL proposals include PoW block headers\n+   that CCC-SL verifies.\n+\n+3. **CFP-SL ↔ RSM-SL-v1**: CFP-SL queries RSM-SL-v1 for the current Active\n+   Roster Mapping to determine voter weights and authorized identities for each\n+   round of BFT consensus. RSM-SL-v1's Roster Evolution Rules are enforced as\n+   part of Zcash ledger state transitions, so the Active Roster Mapping changes\n+   as the PoW ledger evolves.\n+\n+4. **CFP-SL ↔ CNTP-SL**: CNTP-SL provides the network transport over which\n+   CFP-SL messages (proposals, votes, finalized blocks) are communicated\n+   between Finalizers and propagated to all network participants.\n+\n+5. **Zcash PoW ↔ RSM-SL-v1**: The Roster Evolution Rules in RSM-SL-v1 are\n+   expressed as standard Zcash ledger state evolution rules, enforced by the\n+   PoW consensus mechanism. Staking and unstaking transactions appear on the\n+   PoW chain and update the Active Roster Mapping.\n+\n+Relationship to the Existing Zcash Protocol\n+---------------------------------------------\n+\n+The Zcash PoW subprotocol within Shielded Labs Crosslink v1 is closely related\n+to the existing Zcash NU5/NU6 consensus protocol but is not identical. The\n+CCC-SL construction requires the following changes (specified in detail in ZIP\n+[#zip-ccc-sl]_):\n+\n+* Each PoW block header MUST include a ``context_bft`` field referencing a\n+  CFP-SL block, committing the PoW block producer to their view of the\n+  finalization chain.\n+\n+* PoW block validity rules are extended with contextual checks enforcing the\n+  finality gap constraint and Stalled Mode policy.\n+\n+The RSM-SL-v1 staking rules add new transaction types and ledger state to the\n+Zcash protocol, specified in ZIP [#zip-rsm-sl-v1]_.\n+\n+Network Upgrade Mechanism\n+--------------------------\n+\n+Shielded Labs Crosslink v1 would be deployed via the existing Zcash network\n+upgrade mechanism [#zip-0200]_ as a consensus rule change. The precise\n+activation height(s) will be determined in subsequent process ZIPs once all\n+component specifications are finalized.\n+\n+Reward Distribution\n+--------------------\n+\n+Activating Shielded Labs Crosslink v1 requires adjusting the Zcash block\n+reward to provide rewards to Finalizers (via RSM-SL-v1) while maintaining the\n+existing ZEC supply schedule. The specific reward allocation between PoW miners\n+and Finalizers is not specified in this ZIP; it will be addressed in a separate\n+funding ZIP.\n+\n+The ZEC supply cap of 21 million ZEC and the overall issuance schedule MUST\n+be preserved.\n+\n+\n+Security Considerations\n+=======================\n+\n+Hybrid Protocol Security\n+-------------------------\n+\n+The security of Shielded Labs Crosslink v1 depends on the security of both the\n+Zcash PoW subprotocol and ``CFP-SL``, as well as the correctness of ``CCC-SL``.\n+The CCC-SL specification (ZIP [#zip-ccc-sl]_) includes a security analysis\n+establishing the key properties: Crosslink Finality, Prefix Consistency, and\n+Bounded Availability.\n+\n+CFP-SL Failure\n+---------------\n+\n+If ``CFP-SL`` experiences a safety failure (e.g., due to more than one-third\n+of staked weight being controlled by adversarial Finalizers), the CCC-SL\n+construction's finalization safety hazard detection will record the event. The\n+bounded availability property limits the damage from CFP-SL liveness failures\n+by entering Stalled Mode when the finality gap grows too large.\n+\n+PoW Protocol Failure\n+---------------------\n+\n+If the Zcash PoW subprotocol experiences a deep reorg, but CFP-SL has not yet\n+finalized the affected blocks, CCC-SL will simply not finalize those blocks. If\n+the reorg affects already-finalized blocks, the finalization safety hazard\n+detection will record the event — but such an event can only occur if safety\n+assumptions about CFP-SL (e.g., that fewer than one-third of staked weight is\n+adversarial) are also violated.\n+\n+Staked ZEC Risk\n+---------------\n+\n+ZEC staked to Finalizers via RSM-SL-v1 may be subject to slashing penalties\n+for provable misbehavior. The protocol design MUST ensure that slashing\n+penalties cannot be imposed on honest Finalizers, and that the slashing\n+conditions and amounts are deterministic and auditable.\n+\n+Privacy Considerations\n+-----------------------\n+\n+Shielded Labs Crosslink v1 introduces new on-chain data (CFP-SL blocks,\n+Finalizer votes, cross-references between chains, and staking transactions).\n+Care must be taken to ensure that this new data does not leak information about\n+shielded transactions. The privacy implications of Finalizer identity and\n+staking is analyzed in ZIP [#zip-rsm-sl-v1]_.\n+\n+\n+Reference Implementation\n+========================\n+\n+A complete prototype implementation of Shielded Labs Crosslink v1 is being\n+developed based on Zebra in the ``zebra-crosslink`` repository [#zebra-crosslink]_\n+and integrated in the ``crosslink_monolith`` repository [#crosslink-monolith]_.\n+These implementations are experimental and should not be used in production.\n+\n+\n+Appendix: Divergences Between Reference Sources\n+================================================\n+\n+The Shielded Labs Crosslink v1 ZIPs draw on three main reference sources:\n+\n+1. **The TFL book** [#tfl-book]_ — the original ECC design document specifying\n+   ``CCC-TFLv2``.\n+2. **The zebra-crosslink design book** [#zebra-crosslink-cl2]_ — Shielded Labs'\n+   working design documentation, which is based on the TFL book but records\n+   intentional divergences.\n+3. **These ZIPs** (this document and the CCC-SL construction ZIP) — the formal\n+   protocol specifications for Shielded Labs Crosslink v1.\n+\n+The zebra-crosslink design book ``book/src`` is treated as the authoritative\n+source for terminology and design decisions when sources diverge. The following\n+known divergences exist between these sources:\n+\n+Finality Terminology\n+---------------------\n+\n+* **TFL book** uses \"Assured Finality\" as the name of the formal safety\n+  property of the hybrid construction.\n+* **zebra-crosslink** ``security-properties.md`` [#zebra-crosslink-security]_\n+  refers to the same concept as \"Irreversible Finality\".\n+* **zebra-crosslink** ``terminology.md`` [#zebra-crosslink-terminology]_\n+  defines \"Crosslink Finality\" as the user-facing name for this property, with\n+  five defining characteristics (accountable, irreversible, objectively\n+  verifiable, globally consistent, asymmetric cost-of-attack defense).\n+* **These ZIPs** adopt \"Crosslink Finality\" as the primary term, following\n+  ``terminology.md`` as the authoritative source.\n+\n+Stalled Mode\n+------------\n+\n+* **CCC-TFLv2** (TFL book) and **CCC-SL** (these ZIPs) include Stalled Mode:\n+  when the finality depth exceeds ``L``, bc-block producers MUST produce only\n+  stalled blocks (blocks that do not include new unfinalized-output-spending\n+  transactions).\n+* **zebra-crosslink** explicitly omits Stalled Mode from its current design.\n+  The ``cl2-construction.md`` file in the zebra-crosslink design book contains\n+  a prominent warning: \"The ``zebra-crosslink`` design in this book diverges\n+  from this text by *not* including 'Stalled Mode' rules.\"\n+* **These ZIPs** retain Stalled Mode from CCC-TFLv2, as it is a key component\n+  of the Bounded Availability property. This divergence from the current\n+  zebra-crosslink implementation is intentional and will need to be resolved\n+  before deployment.\n+\n+``finality_depth`` Naming\n+--------------------------\n+\n+* **TFL book** defines ``finality_depth(H)`` as the height of ``H`` minus the\n+  height of ``snapshot(LF(H))``, and uses ``finality_depth(H) > L`` as the\n+  condition for Stalled Mode.\n+* **These ZIPs** use the equivalent expression\n+  ``tip_height(H) - fin_height(H) > L``, where ``fin_height(H)`` is the\n+  height of ``candidate(H)``. The quantity ``candidate(H)`` is closely\n+  related to ``snapshot(LF(H))`` via the ``lastCommonAncestor`` function.\n+  The two formulations are equivalent in normal operation.\n+\n+Construction Name\n+-----------------\n+\n+* **TFL book** and **zebra-crosslink** refer to the hybrid construction as\n+  \"Crosslink 2\" (or \"CL2\").\n+* **These ZIPs** use \"CCC-SL\" (Crosslink Consensus Construction from Shielded\n+  Labs) to distinguish the Shielded Labs adaptation from the abstract\n+  CCC-TFLv2 construction.\n+\n+\n+References\n+==========\n+\n+.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2022.3.8 or later <protocol/protocol.pdf>`_\n+.. [#protocol-networks] `Zcash Protocol Specification, Version 2022.3.8. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#zip-0200] `ZIP 200: Network Upgrade Mechanism <zip-0200.rst>`_\n+.. [#zip-ccc-sl] `ZIP [Unassigned]: CCC-SL: Crosslink Consensus Construction from Shielded Labs <draft-shieldedlabs-crosslink-construction.rst>`_\n+.. [#zip-rsm-sl-v1] `ZIP [Unassigned]: RSM-SL-v1: Crosslink Ledger State and Ledger Mutations <draft-shieldedlabs-crosslink-ledger-state.rst>`_\n+.. [#tfl-book] `Zcash Trailing Finality Layer Design Book <https://electric-coin-company.github.io/tfl-book/>`_\n+.. [#ebb-and-flow] `Ebb-and-Flow Protocols: A Resolution of the Availability-Finality Dilemma. David Neu, Joachim Neu, Ertem Nusret Tas, David Tse. <https://eprint.iacr.org/2020/1091.pdf>`_\n+.. [#zebra-crosslink] `ShieldedLabs zebra-crosslink repository <https://github.com/ShieldedLabs/zebra-crosslink>`_\n+.. [#crosslink-monolith] `ShieldedLabs crosslink_monolith repository <https://github.com/ShieldedLabs/crosslink_monolith>`_\n+.. [#zebra-crosslink-terminology] `Shielded Labs zebra-crosslink design book: Terminology <https://github.com/ShieldedLabs/zebra-crosslink/blob/main/book/src/design/terminology.md>`_\n+.. [#zebra-crosslink-cl2] `Shielded Labs zebra-crosslink design book: Crosslink 2 Construction <https://github.com/ShieldedLabs/zebra-crosslink/blob/main/book/src/design/cl2-construction.md>`_\n+.. [#zebra-crosslink-security] `Shielded Labs zebra-crosslink design book: Security Properties <https://github.com/ShieldedLabs/zebra-crosslink/blob/main/book/src/design/security-properties.md>`_"},{"filename":"zips/zip-CROSSLINK.md","status":"added","additions":86,"deletions":0,"patch":"@@ -0,0 +1,86 @@\n+    ZIP: Unassigned-CROSSLINK-CAPSTONE\n+    Title: The Crosslink Consensus Protocol\n+    Owners: Nate Wilcox <nate+zips@shieldedlabs.net>\n+            Andrew Reece <andrew@shieldedlabs.net>\n+    Credits: Nate Wilcox\n+             Daira Emma Hopwood\n+             Jack Str4d Grigg\n+             Andrew Reece\n+    Status: Draft\n+    Category: Consensus & Network & RPC & Wallet\n+    Created: 2026-03-17\n+    License: MIT\n+    Pull-Request: <https://github.com/zcash/zips/pull/???>\n+\n+\n+# Terminology\n+\n+{Edit this to reflect the key words that are actually used.}\n+The key words \"MUST\", \"REQUIRED\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and\n+only when, they appear in all capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol Specification. [^protocol]\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in § 3.12 ‘Mainnet and Testnet’. [^protocol-networks]\n+\n+The term \"full validator\" in this document is to be interpreted as defined in § 3.3 ‘The Block Chain’. [^protocol-blockchain].\n+\n+The terms below are to be interpreted as follows:\n+\n+{Term to be defined}\n+\n+: {Definition.}\n+\n+{Another term}\n+\n+: {Definition.}\n+\n+\n+# Abstract\n+\n+\n+# Motivation\n+\n+\n+# Privacy Implications\n+\n+\n+# Requirements\n+\n+\n+# Non-requirements\n+\n+\n+# Specification\n+\n+<details>\n+<summary>\n+{details heading}\n+</summary>\n+\n+{ details body }\n+</details>\n+<br/>\n+\n+# Rationale\n+\n+\n+# Deployment\n+\n+\n+# Reference implementation\n+\n+\n+# Open issues\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later](protocol/protocol.pdf)\n+\n+[^protocol-blockchain]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.3: The Block Chain](protocol/protocol.pdf#blockchain)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-shieldedlabs-crosslink-construction.rst","title":"CCC-SL: Crosslink Consensus Construction from Shielded Labs","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"parameters","text":"Parameters"},{"level":2,"id":"subprotocol-interface-requirements","text":"Subprotocol Interface Requirements"},{"level":2,"id":"structural-additions","text":"Structural Additions"},{"level":2,"id":"derived-functions","text":"Derived Functions"},{"level":2,"id":"locally-finalized-chain","text":"Locally Finalized Chain"},{"level":2,"id":"locally-bounded-available-chain","text":"Locally Bounded-Available Chain"},{"level":2,"id":"syncing-and-checkpoints","text":"Syncing and Checkpoints"},{"level":2,"id":"bft-cfp-sl-changes-from-origbft","text":"Πbft (CFP-SL) Changes from Πorigbft"},{"level":2,"id":"bc-zcash-pow-changes-from-origbc","text":"Πbc (Zcash PoW) Changes from Πorigbc"},{"level":2,"id":"stalled-mode-policy","text":"Stalled Mode Policy"},{"level":3,"id":"open-questions","text":"Open questions"},{"level":2,"id":"security-analysis","text":"Security Analysis"},{"level":3,"id":"crosslink-finality","text":"Crosslink Finality"},{"level":3,"id":"bounded-availability","text":"Bounded Availability"},{"level":3,"id":"resilience-of-finality-to-pow-reorgs","text":"Resilience of Finality to PoW Reorgs"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1291,"title":"ZIP 244, ZIP 246: Disambiguate scope of BLAKE2b-256 over per-input data.","author":"nuttycom","createdAt":"2026-05-26T17:56:24Z","updatedAt":"2026-05-26T17:56:24Z","url":"https://github.com/zcash/zips/pull/1291","diffUrl":"https://github.com/zcash/zips/pull/1291.diff","headRef":"zip-244-246-blake2b","headRepo":"nuttycom/zips","headSha":"591bd18ab4aab6b6c54f0adc0777c90bd786bc86","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #1082. In \\`transparent_auth_digest\\` (ZIP 246), \\`sapling_auth_digest\\` and \\`transparent_scripts_digest\\` (ZIP 244), the existing wording \"BLAKE2b-256 hash of for each \" was ambiguous: it could be read as either a single hash over the concatenation of all values, or as the concatenation of ","zipRefs":[244,246],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0244.rst","status":"modified","additions":9,"deletions":7,"patch":"@@ -688,8 +688,8 @@ The personalization field of this hash is set to::\n A.1: transparent_scripts_digest\n ```````````````````````````````\n In the case that the transaction contains transparent inputs, this is a BLAKE2b-256 hash\n-of the field encoding of the concatenated values of the Bitcoin script values associated\n-with each transparent input belonging to the transaction.\n+of the concatenation of the field encoding of the Bitcoin script value associated with\n+each transparent input belonging to the transaction, in transaction order.\n \n The personalization field of this hash is set to::\n \n@@ -702,11 +702,13 @@ In the case that the transaction has no transparent inputs, ``transparent_script\n A.2: sapling_auth_digest\n ````````````````````````\n In the case that Sapling Spends or Sapling Outputs are present, this is a BLAKE2b-256 hash\n-of the field encoding of the Sapling ``zkproof`` value of each Sapling Spend Description,\n-followed by the field encoding of the ``spend_auth_sig`` value of each Sapling Spend\n-Description belonging to the transaction, followed by the field encoding of the\n-``zkproof`` field of each Sapling Output Description belonging to the transaction,\n-followed by the field encoding of the binding signature::\n+of the concatenation of the field encoding of the Sapling ``zkproof`` value for each\n+Sapling Spend Description belonging to the transaction in transaction order, followed by\n+the concatenation of the field encoding of the ``spend_auth_sig`` value for each Sapling\n+Spend Description belonging to the transaction in transaction order, followed by the\n+concatenation of the field encoding of the ``zkproof`` field for each Sapling Output\n+Description belonging to the transaction in transaction order, followed by the field\n+encoding of the binding signature::\n \n    A.2a: spend_zkproofs           (field encoding bytes)\n    A.2b: spend_auth_sigs          (field encoding bytes)"},{"filename":"zips/zip-0246.rst","status":"modified","additions":1,"deletions":1,"patch":"@@ -529,7 +529,7 @@ Note that while the structure of ``sapling_auth_digest`` remains the same as in\n A.1: transparent_auth_digest\n ````````````````````````````\n \n-In the case that the transaction contains transparent inputs, this is a BLAKE2b-256 hash of the following values for each transparent input::\n+In the case that the transaction contains transparent inputs, this is a BLAKE2b-256 hash of the concatenation of the field encoding of the ``TransparentSighashInfo`` value for each transparent input in transaction order, followed by the concatenation of the field encoding of the Bitcoin ``scriptSig`` value for each transparent input in transaction order::\n \n    A.1a: TransparentSighashInfo    (field encoding bytes)\n    A.1b: scriptSig                 (field encoding bytes)"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0244.rst","title":"Transaction Identifier Non-Malleability","status":"Final","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"digests","text":"Digests"},{"level":3,"id":"txid-digest","text":"TxId Digest"},{"level":4,"id":"txid-digest-1","text":"txid_digest"},{"level":3,"id":"signature-digest","text":"Signature Digest"},{"level":4,"id":"signature-digest-1","text":"signature_digest"},{"level":3,"id":"authorizing-data-commitment","text":"Authorizing Data Commitment"},{"level":4,"id":"auth-digest","text":"auth_digest"},{"level":2,"id":"block-header-changes","text":"Block Header Changes"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1290,"title":"ZIP 308: Update migration parameters to match zcash/zcash#7132.","author":"nuttycom","createdAt":"2026-05-26T17:56:18Z","updatedAt":"2026-05-26T17:56:18Z","url":"https://github.com/zcash/zips/pull/1290","diffUrl":"https://github.com/zcash/zips/pull/1290.diff","headRef":"zip-308-update-7132","headRepo":"nuttycom/zips","headSha":"77b7680a16c95bcd09ffbf8a299d5566a908cd40","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #1246. Updates the Sprout-to-Sapling migration parameters to reflect the faster schedule implemented in zcash/zcash#7132: - Batch interval reduced from 500 blocks to 100 blocks (triggering height \\`99 mod 100\\`). - Expiry delta reduced from 450 to 50 blocks. - Two-range exponent distribution:","zipRefs":[308],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0308.rst","status":"modified","additions":39,"deletions":21,"patch":"@@ -132,17 +132,17 @@ Transaction schedule\n --------------------\n \n When migration is enabled, a node will send up to 5 transactions for inclusion\n-in each block with height a multiple of 500 (that is, they are sent immediately\n-after seeing a block with height 499, modulo 500). Up to the limit of 5, as\n+in each block with height a multiple of 100 (that is, they are sent immediately\n+after seeing a block with height 99, modulo 100). Up to the limit of 5, as\n many transactions are sent as are needed to migrate the remaining funds\n (possibly with a remainder less than 0.01 ZEC left unmigrated).\n \n Nodes SHOULD NOT send migration transactions during initial block download, or\n-if the timestamp of the triggering block (with height 499, modulo 500) is more\n+if the timestamp of the triggering block (with height 99, modulo 100) is more\n than three hours in the past according to the node's adjusted local clock.\n \n-Note: the 500-block interval has *not* been altered as a result of the halving\n-of target block spacing to 75 seconds with the Blossom upgrade. [#zip-0208]_\n+Note: the original specification of this ZIP used a 500-block interval; this\n+was reduced to 100 blocks (see \"Support in zcashd\" below).\n \n The migration transactions to be sent in a particular batch can take\n significant time to generate, and this time depends on the speed of the user's\n@@ -175,13 +175,21 @@ coordinated time. Sending multiple transactions in each batch ensures that:\n * less information about balances is leaked;\n * it is easier to finish in a reasonable length of time.\n \n-The choice of 500 blocks as the batch interval ensures that each batch occurs\n-at a different time of day (both before and after the Blossom upgrade), which\n-may help to mitigate problems with the availability of nodes being correlated\n-with the local time-of-day.\n+The original choice of 500 blocks as the batch interval ensured that each\n+batch occurred at a different time of day (both before and after the Blossom\n+upgrade), which helped to mitigate problems with the availability of nodes\n+being correlated with the local time-of-day. The current 100-block interval\n+no longer has this property.\n \n-Simulation shows that the migration process will typically complete reasonably\n-quickly even if the amount to be migrated is large:\n+The following simulation results reflect the original parameters\n+(500-block interval, exponents in 6..8). With the current parameters\n+(100-block interval, exponents in 8..10 when more than 1 ZEC remains to\n+migrate, falling back to the slow exponent range for the final amount\n+below 1 ZEC), migrations complete significantly faster.\n+\n+Simulation shows that, under the original parameters, the migration process\n+would typically complete reasonably quickly even if the amount to be\n+migrated were large:\n \n +-----------+--------------------------------------------+\n |           |     Time in days to complete migration     |\n@@ -220,7 +228,14 @@ If the remaining amount to be migrated is less than 0.01 ZEC, end the migration.\n Otherwise, the amount to send in each transaction is chosen according to the\n following distribution:\n \n-1. Choose an integer exponent uniformly in the range 6 to 8 inclusive.\n+1. Choose an integer exponent uniformly in a range that depends on the\n+   remaining amount to be migrated:\n+\n+   * If the remaining amount is greater than 1 ZEC, choose the exponent\n+     uniformly in the range 8 to 10 inclusive (\"fast range\").\n+   * Otherwise, choose the exponent uniformly in the range 6 to 8\n+     inclusive (\"slow range\").\n+\n 2. Choose an integer mantissa uniformly in the range 1 to 99 inclusive.\n 3. Calculate amount := (mantissa \\* 10\\ :sup:`exponent`) zatoshi.\n 4. If amount is greater than the amount remaining to send, repeat from step 1.\n@@ -271,9 +286,9 @@ the same as for a normal transaction sending the same amount with the same\n wallet state. Only the ``vpub_new`` of the last JoinSplit will be nonzero.\n There will always be exactly one Sapling Output.\n \n-The expiry delta for migration transactions MUST be 450 blocks. Since these\n-transactions are sent when the block height is 499 modulo 500, their expiry\n-height will be 451 blocks later, i.e. ``nExpiryHeight`` will be 450 modulo 500.\n+The expiry delta for migration transactions MUST be 50 blocks. Since these\n+transactions are sent when the block height is 99 modulo 100, their expiry\n+height will be 51 blocks later, i.e. ``nExpiryHeight`` will be 50 modulo 100.\n \n The fee for each migration transaction MUST be 0.0001 ZEC. This fee is taken\n from the funds to be migrated.\n@@ -303,17 +318,19 @@ Open questions\n \n The above strategy has several \"magic number\" parameters:\n \n-* the interval between batches (500 blocks)\n+* the interval between batches (100 blocks)\n * the maximum number of transactions in a batch (5)\n-* the distribution of exponents (uniform integer in 6..8)\n+* the distribution of exponents (uniform integer in 8..10 when the remaining\n+  amount is greater than 1 ZEC, otherwise uniform integer in 6..8)\n+* the threshold between the fast and slow exponent ranges (1 ZEC)\n * the distribution of mantissae (uniform integer in 1..99).\n \n These have been chosen by guesswork. Should we change any of them?\n \n-In particular, if the amount to migrate is large, then this strategy can\n-result in fairly large amounts (up to 99 ZEC, worth USD ~6700 at time of\n-writing) transferred in each transaction. This leaks the fact that the\n-transaction was sent by a user who has at least that amount.\n+In particular, when the remaining amount to migrate is large, this strategy\n+can result in fairly large amounts (up to 9900 ZEC) transferred in each\n+transaction. This leaks the fact that the transaction was sent by a user who\n+has at least that amount.\n \n The strategy does not migrate any remaining fractional amount less than\n 0.01 ZEC (worth USD ~0.68 at time of writing). Is this reasonable?\n@@ -421,6 +438,7 @@ The following PRs implement this specification:\n * https://github.com/zcash/zcash/pull/3997 (bugfix)\n * https://github.com/zcash/zcash/pull/4002 (minor RPC improvements)\n * https://github.com/zcash/zcash/pull/4005 (change expiry for migration transactions)\n+* https://github.com/zcash/zcash/pull/7132 (speed up Sprout-to-Sapling migration)\n \n \n References"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0308.rst","title":"Sprout to Sapling Migration","status":"Active","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"transaction-schedule","text":"Transaction schedule"},{"level":3,"id":"rationale-for-transaction-schedule","text":"Rationale for transaction schedule"},{"level":2,"id":"how-much-to-send-in-each-transaction","text":"How much to send in each transaction"},{"level":3,"id":"rationale-for-how-much-to-send","text":"Rationale for how much to send"},{"level":2,"id":"other-design-decisions","text":"Other design decisions"},{"level":2,"id":"open-questions","text":"Open questions"},{"level":2,"id":"rpc-calls","text":"RPC calls"},{"level":1,"id":"support-in-zcashd","text":"Support in zcashd"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1289,"title":"ZIP 321: Add Deployment section.","author":"nuttycom","createdAt":"2026-05-26T17:56:13Z","updatedAt":"2026-05-26T17:56:13Z","url":"https://github.com/zcash/zips/pull/1289","diffUrl":"https://github.com/zcash/zips/pull/1289.diff","headRef":"zip-321-active","headRepo":"nuttycom/zips","headSha":"130a2954c2d863620a0af97a8aa51a9aae8e4294","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #937. ZIP 321 has been Active since its initial publication; this PR adds an explicit Deployment section describing its existing deployment in production wallets and noting that the \\`req-asset\\` extension is not yet deployed pending ZIP 227 activation. ### Reviewer notes (please verify befor","zipRefs":[321],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":0,"deletions":2,"patch":"@@ -198,7 +198,6 @@ written.\n     <tr> <td>312</td> <td class=\"left\"><a href=\"zips/zip-0312.rst\">FROST for Spend Authorization Multisignatures</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/382\">zips#382</a></td>\n     <tr> <td><span class=\"reserved\">314</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0314.rst\">Privacy upgrades to the Zcash light client protocol</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/434\">zips#434</a></td>\n     <tr> <td>315</td> <td class=\"left\"><a href=\"zips/zip-0315.rst\">Best Practices for Wallet Implementations</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/447\">zips#447</a></td>\n-    <tr> <td><span class=\"reserved\">318</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0318.rst\">Associated Payload Encryption</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/633\">zips#633</a></td>\n     <tr> <td><span class=\"reserved\">319</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0319.rst\">Options for Shielded Pool Retirement</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/635\">zips#635</a></td>\n     <tr> <td><span class=\"reserved\">322</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0322.rst\">Generic Signed Message Format</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/429\">zips#429</a></td>\n     <tr> <td><span class=\"reserved\">323</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0323.rst\">Specification of getblocktemplate for Zcash</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/405\">zips#405</a></td>\n@@ -355,7 +354,6 @@ Index of ZIPs\n     <tr> <td>315</td> <td class=\"left\"><a href=\"zips/zip-0315.rst\">Best Practices for Wallet Implementations</a></td> <td>Draft</td>\n     <tr> <td>316</td> <td class=\"left\"><a href=\"zips/zip-0316.rst\">Unified Addresses and Unified Viewing Keys</a></td> <td>[Revision 0] Active, [Revision 1] Withdrawn, [Revision 2] Draft</td>\n     <tr> <td>317</td> <td class=\"left\"><a href=\"zips/zip-0317.rst\">Proportional Transfer Fee Mechanism</a></td> <td>Active</td>\n-    <tr> <td><span class=\"reserved\">318</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0318.rst\">Associated Payload Encryption</a></td> <td>Reserved</td>\n     <tr> <td><span class=\"reserved\">319</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0319.rst\">Options for Shielded Pool Retirement</a></td> <td>Reserved</td>\n     <tr> <td>320</td> <td class=\"left\"><a href=\"zips/zip-0320.rst\">Defining an Address Type to which funds can only be sent from Transparent Addresses</a></td> <td>Active</td>\n     <tr> <td>321</td> <td class=\"left\"><a href=\"zips/zip-0321.rst\">Payment Request URIs</a></td> <td>Active</td>"},{"filename":"zips/zip-0321.rst","status":"modified","additions":18,"deletions":0,"patch":"@@ -449,6 +449,23 @@ query parameter rather than in the ``hier-part`` of the URI. They may also\n not support the ``memo`` parameter, or may not treat it as base64url-encoded.\n \n \n+Deployment\n+==========\n+\n+ZIP 321 payment request URIs are widely deployed in the Zcash ecosystem.\n+A reference implementation of parsing and construction of ZIP 321 URIs (for\n+the ZEC-only subset of the format, excluding the ``req-asset`` extension) is\n+provided by the ``zip321`` crate in the ``librustzcash`` repository\n+[#librustzcash-zip321]_, and is used by wallets built on the\n+``zcash_client_backend`` library. Support for ZIP 321 URIs has shipped in\n+production wallets including Zashi, Nighthawk Wallet, Edge, Unstoppable\n+Wallet, YWallet, and (historically) ZecWallet Lite, among others, and is\n+commonly relied upon for generating QR codes and clickable payment links.\n+\n+The ``req-asset`` extension for Custom Asset payments described in this ZIP\n+is not yet deployed, pending the activation of ZIP 227 [#zip-0227]_.\n+\n+\n References\n ==========\n \n@@ -463,6 +480,7 @@ References\n .. [#zip-0173] `ZIP 173: Bech32 Format <zip-0173.rst>`_\n .. [#zip-0211] `ZIP 211: Disabling Addition of New Value to the Sprout Value Pool <zip-0211.rst>`_\n .. [#zip-0227] `ZIP 227: Issuance of Zcash Shielded Assets <zip-0227.rst>`_\n+.. [#librustzcash-zip321] `librustzcash: zip321 crate <https://github.com/zcash/librustzcash/tree/main/components/zip321>`_\n .. [#zip-0227-asset-identifier] `ZIP 227: Issuance of Zcash Shielded Assets — Asset Identifier <zip-0227.rst#asset-identifier>`_\n .. [#zip-0316] `ZIP 316: Unified Addresses and Unified Viewing Keys <zip-0316.rst>`_\n .. [#protocol-networks] `Zcash Protocol Specification, Version 2023.4.0. Section 3.11: Mainnet and Testnet <protocol/protocol.pdf#networks>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0321.rst","title":"Payment Request URIs","status":"Active","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"uri-syntax","text":"URI Syntax"},{"level":2,"id":"uri-semantics","text":"URI Semantics"},{"level":2,"id":"custom-assets","text":"Custom Assets"},{"level":3,"id":"example","text":"Example"},{"level":2,"id":"zec-transfer-amount","text":"ZEC Transfer Amount"},{"level":2,"id":"query-keys","text":"Query Keys"},{"level":2,"id":"examples","text":"Examples"},{"level":3,"id":"valid-examples","text":"Valid examples"},{"level":3,"id":"invalid-examples","text":"Invalid Examples"},{"level":2,"id":"forward-compatibility","text":"Forward compatibility"},{"level":2,"id":"backward-compatibility","text":"Backward compatibility"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1288,"title":"ZIP 317: Clarify that tx_in/out_total_size exclude tx_in/out_count.","author":"nuttycom","createdAt":"2026-05-26T17:56:07Z","updatedAt":"2026-05-26T17:56:07Z","url":"https://github.com/zcash/zips/pull/1288","diffUrl":"https://github.com/zcash/zips/pull/1288.diff","headRef":"zip-317-total-size","headRepo":"nuttycom/zips","headSha":"828ed85b2ac306fb2d5358b1650a23c079054dd5","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #683. The definitions of \\`tx_in_total_size\\` and \\`tx_out_total_size\\` were ambiguous about whether the CompactSize \\`tx_in_count\\` and \\`tx_out_count\\` prefixes that precede the \\`tx_in\\` and \\`tx_out\\` arrays in the encoded transaction are included. A careful reading of the protocol specif","zipRefs":[317],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0317.rst","status":"modified","additions":7,"deletions":0,"patch":"@@ -163,6 +163,13 @@ Input                                         Units  Description\n :math:`\\mathit{nMemoChunks}`                  number the number of memo chunks\n ============================================= ====== ====================================================================\n \n+Here, :math:`\\mathit{tx\\_in\\_total\\_size}` is the sum of the sizes in bytes of the\n+serialized ``tx_in`` entries, and :math:`\\mathit{tx\\_out\\_total\\_size}` is the sum of\n+the sizes in bytes of the serialized ``tx_out`` entries, as defined by the Bitcoin-style\n+transaction format referenced in [#protocol-txnencoding]_. These totals do not include\n+the size of the ``tx_in_count`` or ``tx_out_count`` CompactSize prefixes that precede\n+the ``tx_in`` and ``tx_out`` arrays in the encoded transaction.\n+\n It is not a consensus requirement that fees follow this formula; however,\n wallets SHOULD create transactions that pay this fee, in order to reduce\n information leakage, unless overridden by the user."}],"filesTruncated":false,"preview":{"filename":"zips/zip-0317.rst","title":"Proportional Transfer Fee Mechanism","status":"Active","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"notation","text":"Notation"},{"level":2,"id":"fee-calculation","text":"Fee calculation"},{"level":3,"id":"rationale-for-logical-actions","text":"Rationale for logical actions"},{"level":3,"id":"rationale-for-the-chosen-parameters","text":"Rationale for the chosen parameters"},{"level":2,"id":"transaction-relaying","text":"Transaction relaying"},{"level":2,"id":"mempool-size-limiting","text":"Mempool size limiting"},{"level":2,"id":"block-production","text":"Block production"},{"level":3,"id":"recommended-algorithm-for-block-template-construction","text":"Recommended algorithm for block template construction"},{"level":3,"id":"rationale-for-block-template-construction-algorithm","text":"Rationale for block template construction algorithm"},{"level":3,"id":"incentive-compatibility-for-miners","text":"Incentive compatibility for miners"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy considerations"},{"level":2,"id":"denial-of-service","text":"Denial of Service"},{"level":1,"id":"deployment","text":"Deployment"},{"level":2,"id":"deployment-in-zcashd","text":"Deployment in zcashd"},{"level":2,"id":"deployment-in-zebra","text":"Deployment in zebra"},{"level":1,"id":"considered-alternatives","text":"Considered Alternatives"},{"level":1,"id":"endorsements","text":"Endorsements"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1287,"title":"ZIP 143, ZIP 243: Note that test vectors are randomly generated.","author":"nuttycom","createdAt":"2026-05-26T17:56:02Z","updatedAt":"2026-05-26T17:56:02Z","url":"https://github.com/zcash/zips/pull/1287","diffUrl":"https://github.com/zcash/zips/pull/1287.diff","headRef":"zip-143-243-test-vectors","headRepo":"nuttycom/zips","headSha":"30a9ac30df69cbdac8b648de9c3488a050548bd3","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #356. Clarifies (in the Example sections of both ZIPs) that the sample transactions used to exercise the sighash algorithm are randomly generated and are not necessarily valid Zcash transactions; their purpose is to verify the transaction digest algorithm rather than to pass full validation. ","zipRefs":[143,243],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0143.rst","status":"modified","additions":4,"deletions":0,"patch":"@@ -303,6 +303,10 @@ the ZIP 143 test vectors [#test-vectors]_. The first two test vectors are broken\n that 32-byte values below are exactly as the hash function returns, and are not reversed. Further examples can\n be found in the SignatureHash test data [#sighash-tests]_.\n \n+The sample transactions below and in [#sighash-tests]_ are randomly generated and are intended only for\n+testing implementations of the transaction digest algorithm; they are not necessarily valid Zcash\n+transactions and do not necessarily pass full validation.\n+\n The test vectors below should be verified with the Overwinter consensus branch ID (0x5ba81b19) [#zip-0201]_.\n \n Test vector 1"},{"filename":"zips/zip-0243.rst","status":"modified","additions":3,"deletions":2,"patch":"@@ -273,8 +273,9 @@ the ZIP 243 test vectors [#test-vectors]_. The first two test vectors are broken\n that 32-byte values below are exactly as the hash function returns, and are not reversed. Further examples can\n be found in the SignatureHash test data [#sighash-tests]_.\n \n-The sample transactions below and in [#sighash-tests]_ are intended only for testing implementations of the\n-transaction digest algorithm; they do not necessarily pass full validation.\n+The sample transactions below and in [#sighash-tests]_ are randomly generated and are intended only for\n+testing implementations of the transaction digest algorithm; they are not necessarily valid Zcash\n+transactions and do not necessarily pass full validation.\n \n \n Test vector 1"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0143.rst","title":"Transaction Signature Validation for Overwinter","status":"Final","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"field-definitions","text":"Field definitions"},{"level":3,"id":"header","text":"1: header"},{"level":3,"id":"nversiongroupid","text":"2: nVersionGroupId"},{"level":3,"id":"hashprevouts","text":"3: hashPrevouts"},{"level":3,"id":"hashsequence","text":"4: hashSequence"},{"level":3,"id":"hashoutputs","text":"5: hashOutputs"},{"level":3,"id":"hashjoinsplits","text":"6: hashJoinSplits"},{"level":3,"id":"nexpiryheight","text":"8: nExpiryHeight"},{"level":3,"id":"b-scriptcode","text":"10b: scriptCode"},{"level":3,"id":"c-value","text":"10c: value"},{"level":2,"id":"notes","text":"Notes"},{"level":1,"id":"example","text":"Example"},{"level":2,"id":"test-vector-1","text":"Test vector 1"},{"level":2,"id":"test-vector-2","text":"Test vector 2"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"backward-compatibility","text":"Backward compatibility"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1285,"title":"ZIP 317: Allow either inclusion or omission of dependent transactions in block template construction.","author":"nuttycom","createdAt":"2026-05-20T22:10:43Z","updatedAt":"2026-05-20T22:10:43Z","url":"https://github.com/zcash/zips/pull/1285","diffUrl":"https://github.com/zcash/zips/pull/1285.diff","headRef":"zip-317-dependent-txs","headRepo":"nuttycom/zips","headSha":"24941a85db27ea386ff0980ec8cd22d199ce55e3","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Resolves zcash/zips#696. Clarifies that the block template construction algorithm permits either including chains of in-mempool dependent transactions (as zcashd does) or restricting the candidate set to transactions whose inputs are all already confirmed (as zebrad does). Both variants are conforma","zipRefs":[317],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0317.rst","status":"modified","additions":43,"deletions":9,"patch":"@@ -358,26 +358,48 @@ from a set of transactions in a node's mempool:\n 1. Set the block template $T$ to include a placeholder for the\n    coinbase transaction (see Note below).\n \n-2. For each transaction $\\mathit{tx}$ in the mempool, calculate\n-   $\\mathit{tx.weight\\_ratio} = \\mathsf{min}\\!\\left(\\frac{\\mathsf{max}(1,\\, \\mathit{tx.fee})}{\\mathit{conventional\\_fee}(\\mathit{tx})},\\, \\mathit{weight\\_ratio\\_cap}\\right)$\n-   and add the transaction to the set of candidate transactions.\n-\n-3. Repeat while there is any candidate transaction that pays at least the\n-   conventional fee:\n+2. Construct the set of candidate transactions. An implementation MAY\n+   support chains of dependent transactions in the mempool, or MAY\n+   restrict the candidate set to transactions whose inputs are all\n+   already confirmed in the chain. Specifically, either:\n+\n+   a. (Dependent transactions allowed.) For each transaction\n+      $\\mathit{tx}$ in the mempool, add $\\mathit{tx}$ to the set of\n+      candidate transactions. A transaction $\\mathit{tx}$ in the\n+      candidate set is said to be *eligible* if every in-mempool\n+      ancestor of $\\mathit{tx}$ has already been included in $T$;\n+      otherwise it is *ineligible*. An \"in-mempool ancestor\" of\n+      $\\mathit{tx}$ is any transaction in the mempool that\n+      $\\mathit{tx}$ directly or transitively spends a transparent\n+      output of. Or:\n+   b. (Dependent transactions omitted.) For each transaction\n+      $\\mathit{tx}$ in the mempool whose transparent inputs (if any)\n+      all spend outputs of transactions already confirmed in the chain\n+      (i.e. not other mempool transactions), add $\\mathit{tx}$ to the\n+      set of candidate transactions. Every candidate transaction is\n+      *eligible*.\n+\n+   In either case, for each candidate transaction $\\mathit{tx}$ calculate\n+   $\\mathit{tx.weight\\_ratio} = \\mathsf{min}\\!\\left(\\frac{\\mathsf{max}(1,\\, \\mathit{tx.fee})}{\\mathit{conventional\\_fee}(\\mathit{tx})},\\, \\mathit{weight\\_ratio\\_cap}\\right)$.\n+\n+3. Repeat while there is any eligible candidate transaction that pays\n+   at least the conventional fee:\n \n    a. Pick one of those transactions at random with probability in direct\n       proportion to its $\\mathit{weight\\_ratio}$, and remove it from the set of\n       candidate transactions. Let $B$ be the block template $T$\n-      with this transaction included.\n+      with this transaction included (after its in-mempool ancestors,\n+      which by eligibility are already in $T$).\n    b. If $B$ would be within the block size limit and block sigop\n       limit [#sigop-limit]_, set $T := B$.\n \n-4. Repeat while there is any candidate transaction:\n+4. Repeat while there is any eligible candidate transaction:\n \n    a. Pick one of those transactions at random with probability in direct\n       proportion to its $\\mathit{weight\\_ratio}$, and remove it from the set of\n       candidate transactions. Let $B$ be the block template $T$\n-      with this transaction included.\n+      with this transaction included (after its in-mempool ancestors,\n+      which by eligibility are already in $T$).\n    b. If $B$ would be within the block size limit and block sigop\n       limit [#sigop-limit]_ and $\\mathit{block\\_unpaid\\_actions}(B) \\leq \\mathit{block\\_unpaid\\_action\\_limit}$,\n       set $T := B$.\n@@ -435,6 +457,18 @@ rather than just $\\mathit{tx.fee}$ avoids needing to define \"with probability in\n proportion to its $\\mathit{weight\\_ratio}$\" for the case where all remaining candidate\n transactions would have $\\mathit{weight\\_ratio} = 0$.\n \n+The candidate set in step 2 is specified in two variants so that the\n+algorithm accommodates both implementations that track chains of\n+dependent in-mempool transactions and those that do not. Including\n+dependent transactions tends to increase the total fees collected and\n+allows fee-paying children to \"pull in\" lower-fee parents; omitting\n+them is simpler and avoids the need to reason about ancestor sets when\n+evaluating the block size, sigop, and unpaid-action limits. Both\n+variants are conformant. In either case, each candidate transaction's\n+$\\mathit{weight\\_ratio}$ is computed from its own fee and conventional\n+fee; the algorithm does not redistribute fees from a child to its\n+ancestors when choosing candidates.\n+\n Incentive compatibility for miners\n ''''''''''''''''''''''''''''''''''\n "}],"filesTruncated":false,"preview":{"filename":"zips/zip-0317.rst","title":"Proportional Transfer Fee Mechanism","status":"Active","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"notation","text":"Notation"},{"level":2,"id":"fee-calculation","text":"Fee calculation"},{"level":3,"id":"rationale-for-logical-actions","text":"Rationale for logical actions"},{"level":3,"id":"rationale-for-the-chosen-parameters","text":"Rationale for the chosen parameters"},{"level":2,"id":"transaction-relaying","text":"Transaction relaying"},{"level":2,"id":"mempool-size-limiting","text":"Mempool size limiting"},{"level":2,"id":"block-production","text":"Block production"},{"level":3,"id":"recommended-algorithm-for-block-template-construction","text":"Recommended algorithm for block template construction"},{"level":3,"id":"rationale-for-block-template-construction-algorithm","text":"Rationale for block template construction algorithm"},{"level":3,"id":"incentive-compatibility-for-miners","text":"Incentive compatibility for miners"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy considerations"},{"level":2,"id":"denial-of-service","text":"Denial of Service"},{"level":1,"id":"deployment","text":"Deployment"},{"level":2,"id":"deployment-in-zcashd","text":"Deployment in zcashd"},{"level":2,"id":"deployment-in-zebra","text":"Deployment in zebra"},{"level":1,"id":"considered-alternatives","text":"Considered Alternatives"},{"level":1,"id":"endorsements","text":"Endorsements"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":713,"title":"prefix table with protocol labels to increase clarity","author":"zancas","createdAt":"2023-09-07T20:29:19Z","updatedAt":"2026-05-19T20:54:33Z","url":"https://github.com/zcash/zips/pull/713","diffUrl":"https://github.com/zcash/zips/pull/713.diff","headRef":"add_protocol_lables_to_table","headRepo":"zancas/zips","headSha":"94cf2c7d5e6e5088b00dd8258eabae2679aca980","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[317],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zip-0317.html","status":"modified","additions":17,"deletions":53,"patch":"@@ -126,59 +126,23 @@\n   conventional\\_fee &amp;=&amp; marginal\\_fee \\cdot \\mathsf{max}(grace\\_actions, logical\\_actions)\n \\end{array}\\)</div>\n                 <p>The inputs to this formula are taken from transaction fields defined in the Zcash protocol specification <a id=\"footnote-reference-4\" class=\"footnote_reference\" href=\"#protocol-txnencoding\">3</a>:</p>\n-                <table>\n-                    <thead>\n-                        <tr>\n-                            <th>Input</th>\n-                            <th>Units</th>\n-                            <th>Description</th>\n-                        </tr>\n-                    </thead>\n-                    <tbody>\n-                        <tr>\n-                            <td>\n-                                <span class=\"math\">\\(tx\\_in\\_total\\_size\\)</span>\n-                            </td>\n-                            <td>bytes</td>\n-                            <td>total size in bytes of the <code>tx_in</code> field</td>\n-                        </tr>\n-                        <tr>\n-                            <td>\n-                                <span class=\"math\">\\(tx\\_out\\_total\\_size\\)</span>\n-                            </td>\n-                            <td>bytes</td>\n-                            <td>total size in bytes of the <code>tx_out</code> field</td>\n-                        </tr>\n-                        <tr>\n-                            <td>\n-                                <span class=\"math\">\\(nJoinSplit\\)</span>\n-                            </td>\n-                            <td>number</td>\n-                            <td>the number of Sprout JoinSplits</td>\n-                        </tr>\n-                        <tr>\n-                            <td>\n-                                <span class=\"math\">\\(nSpendsSapling\\)</span>\n-                            </td>\n-                            <td>number</td>\n-                            <td>the number of Sapling spends</td>\n-                        </tr>\n-                        <tr>\n-                            <td>\n-                                <span class=\"math\">\\(nOutputsSapling\\)</span>\n-                            </td>\n-                            <td>number</td>\n-                            <td>the number of Sapling outputs</td>\n-                        </tr>\n-                        <tr>\n-                            <td>\n-                                <span class=\"math\">\\(nActionsOrchard\\)</span>\n-                            </td>\n-                            <td>number</td>\n-                            <td>the number of Orchard actions</td>\n-                        </tr>\n-                    </tbody>\n-                </table>\n+                <div class=\"math\">\\(\\begin{array}{|l|l|l|l|}\n+\\hline\n+\\text{Protocol} &amp; \\text{Input} &amp; \\text{Units} &amp; \\text{Description} \\\\\n+\\hline\n+\\text{Transparent} &amp; tx\\_in\\_total\\_size &amp; \\text{bytes} &amp; \\text{total size in bytes of the } tx\\_in \\\\\n+\\hline\n+&amp; tx\\_out\\_total\\_size &amp; \\text{bytes} &amp; \\text{total size in bytes of the } tx\\_out \\\\\n+\\hline\n+\\text{Sprout} &amp; nJoinSplit &amp; \\text{number} &amp; \\text{the number of Sprout JoinSplits} \\\\\n+\\hline\n+\\text{Sapling} &amp; nSpendsSapling &amp; \\text{number} &amp; \\text{the number of Sapling spends} \\\\\n+\\hline\n+&amp; nOutputsSapling &amp; \\text{number} &amp; \\text{the number of Sapling outputs} \\\\\n+\\hline\n+\\text{Orchard} &amp; nActionsOrchard &amp; \\text{number} &amp; \\text{the number of Orchard actions} \\\\\n+\\hline\n+\\end{array}\\)</div>\n                 <p>It is not a consensus requirement that fees follow this formula; however, wallets SHOULD create transactions that pay this fee, in order to reduce information leakage, unless overridden by the user.</p>\n                 <section id=\"rationale-for-logical-actions\"><h4><span class=\"section-heading\">Rationale for logical actions</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#rationale-for-logical-actions\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h4>\n                 <details>"},{"filename":"zip-0317.rst","status":"modified","additions":19,"deletions":10,"patch":"@@ -123,16 +123,25 @@ calculated in zatoshis per the following formula:\n The inputs to this formula are taken from transaction fields defined in the Zcash protocol\n specification [#protocol-txnencoding]_:\n \n-============================ ====== ===========================================\n-Input                        Units  Description\n-============================ ====== ===========================================\n-:math:`tx\\_in\\_total\\_size`  bytes  total size in bytes of the ``tx_in`` field\n-:math:`tx\\_out\\_total\\_size` bytes  total size in bytes of the ``tx_out`` field\n-:math:`nJoinSplit`           number the number of Sprout JoinSplits\n-:math:`nSpendsSapling`       number the number of Sapling spends\n-:math:`nOutputsSapling`      number the number of Sapling outputs\n-:math:`nActionsOrchard`      number the number of Orchard actions\n-============================ ====== ===========================================\n+.. math::\n+    \n+    \\begin{array}{|l|l|l|l|}\n+    \\hline\n+    \\text{Protocol} & \\text{Input} & \\text{Units} & \\text{Description} \\\\\n+    \\hline\n+    \\text{Transparent} & tx\\_in\\_total\\_size & \\text{bytes} & \\text{total size in bytes of the } tx\\_in \\\\\n+    \\hline\n+    & tx\\_out\\_total\\_size & \\text{bytes} & \\text{total size in bytes of the } tx\\_out \\\\\n+    \\hline\n+    \\text{Sprout} & nJoinSplit & \\text{number} & \\text{the number of Sprout JoinSplits} \\\\\n+    \\hline\n+    \\text{Sapling} & nSpendsSapling & \\text{number} & \\text{the number of Sapling spends} \\\\\n+    \\hline\n+    & nOutputsSapling & \\text{number} & \\text{the number of Sapling outputs} \\\\\n+    \\hline\n+    \\text{Orchard} & nActionsOrchard & \\text{number} & \\text{the number of Orchard actions} \\\\\n+    \\hline\n+    \\end{array}\n \n It is not a consensus requirement that fees follow this formula; however,\n wallets SHOULD create transactions that pay this fee, in order to reduce"}],"filesTruncated":false,"preview":{"filename":"zip-0317.rst","title":"Proportional Transfer Fee Mechanism","status":"Active","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"notation","text":"Notation"},{"level":2,"id":"fee-calculation","text":"Fee calculation"},{"level":3,"id":"rationale-for-logical-actions","text":"Rationale for logical actions"},{"level":3,"id":"rationale-for-the-chosen-parameters","text":"Rationale for the chosen parameters"},{"level":4,"id":"grace-actions","text":"Grace Actions"},{"level":4,"id":"transparent-contribution","text":"Transparent Contribution"},{"level":4,"id":"marginal-fee","text":"Marginal Fee"},{"level":2,"id":"transaction-relaying","text":"Transaction relaying"},{"level":2,"id":"mempool-size-limiting","text":"Mempool size limiting"},{"level":2,"id":"block-production","text":"Block production"},{"level":3,"id":"recommended-algorithm-for-block-template-construction","text":"Recommended algorithm for block template construction"},{"level":3,"id":"rationale-for-block-template-construction-algorithm","text":"Rationale for block template construction algorithm"},{"level":3,"id":"incentive-compatibility-for-miners","text":"Incentive compatibility for miners"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy considerations"},{"level":2,"id":"denial-of-service","text":"Denial of Service"},{"level":1,"id":"deployment","text":"Deployment"},{"level":2,"id":"deployment-in-zcashd","text":"Deployment in zcashd"},{"level":2,"id":"deployment-in-zebra","text":"Deployment in zebra"},{"level":1,"id":"considered-alternatives","text":"Considered Alternatives"},{"level":1,"id":"endorsements","text":"Endorsements"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":660,"title":"ZIP 316 updates for Unified Raw Encodings","author":"daira","createdAt":"2023-01-11T20:55:26Z","updatedAt":"2026-05-19T20:53:56Z","url":"https://github.com/zcash/zips/pull/660","diffUrl":"https://github.com/zcash/zips/pull/660.diff","headRef":"zip-316-raw-encoding","headRepo":"daira/zips","headSha":"05ecd086cf7c17963f5b92a02c5c51bf163d1457","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Signed-off-by: Daira Hopwood","zipRefs":[316],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zip-0316.html","status":"modified","additions":191,"deletions":59,"patch":"@@ -37,16 +37,16 @@\n                 <dd>A wallet or other software that can send transfers of assets, or other consensus state side-effects defined in future. Senders are a subset of Consumers.</dd>\n                 <dt>Receiver</dt>\n                 <dd>The necessary information to transfer an asset to a Recipient that generated that Receiver using a specific Transfer Protocol. Each Receiver is associated unambiguously with a specific Receiver Type, identified by an integer Typecode.</dd>\n-                <dt>Receiver Encoding</dt>\n+                <dt>Receiver Item</dt>\n                 <dd>An encoding of a Receiver as a byte sequence.</dd>\n                 <dt>Viewing Key</dt>\n                 <dd>The necessary information to view information about payments to an Address, or (in the case of a Full Viewing Key) from an Address. An Incoming Viewing Key can be derived from a Full Viewing Key, and an Address can be derived from an Incoming Viewing Key.</dd>\n-                <dt>Viewing Key Encoding</dt>\n+                <dt>Viewing Key Item</dt>\n                 <dd>An encoding of a Viewing Key as a byte sequence.</dd>\n-                <dt>Metadata Encoding</dt>\n+                <dt>Metadata Item</dt>\n                 <dd>An encoding of metadata that is not a Receiver or Viewing Key, but may affect the interpretation of the overall Unified Address/Viewing Key.</dd>\n                 <dt>Item</dt>\n-                <dd>An Receiver Encoding, Viewing Key Encoding, or Metadata Encoding.</dd>\n+                <dd>An Receiver Item, Viewing Key Item, or Metadata Item.</dd>\n                 <dt>Legacy Address</dt>\n                 <dd>A Transparent, Sprout, or Sapling Address.</dd>\n                 <dt>Unified Address (or UA)</dt>\n@@ -65,10 +65,23 @@\n                 <dd>An encoding of a UA/UVK as a US-ASCII string, intended either for display and transfer by Zcash end-users, or internal use by Zcash-related software.</dd>\n                 <dt>Unified QR Encoding</dt>\n                 <dd>An encoding of a UA/UVK as a QR code, intended for display and transfer by Zcash end-users in situations where usability advantages of a 2D bar code may be relevant.</dd>\n+                <dt>Unified Raw Encoding</dt>\n+                <dd>An encoding of a UA/UVK as a byte sequence, intended <em>only</em> for internal use by Zcash-related software. Unified Raw Encodings MUST NOT be exposed to end-users, since they lack resilience and nonmalleability properties necessary for that purpose.</dd>\n                 <dt>Address Encoding</dt>\n-                <dd>The externally visible encoding of an Address (e.g. as a string of characters or a QR code).</dd>\n+                <dd>An externally visible encoding of an Address.</dd>\n             </dl>\n             <p>Notation for sequences, conversions, and arithmetic operations follows the Zcash protocol specification <a id=\"footnote-reference-2\" class=\"footnote_reference\" href=\"#protocol-notation\">3</a>.</p>\n+            <p>The notation\n+                <span class=\"math\">\\(\\mathtt{X}[i\\,..\\!=j]\\)</span>\n+            , where\n+                <span class=\"math\">\\(\\mathtt{X}\\)</span>\n+             is a byte sequence, means the subsequence of bytes of\n+                <span class=\"math\">\\(\\mathtt{X}\\)</span>\n+             at zero-based indices\n+                <span class=\"math\">\\(i\\)</span>\n+             to\n+                <span class=\"math\">\\(j\\)</span>\n+             inclusive.</p>\n         </section>\n         <section id=\"abstract\"><h2><span class=\"section-heading\">Abstract</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#abstract\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>This proposal defines Unified Addresses, which bundle together Zcash Addresses of different types in a way that can be presented as a single Address Encoding. It also defines Unified Viewing Keys, which perform a similar function for Zcash viewing keys.</p>\n@@ -114,7 +127,7 @@\n                     <li>(Perhaps later in time) if the Consumer wallet is a Sender, it can execute a transfer of ZEC (or other assets or protocol state changes) to the Address.</li>\n                 </ol>\n                 <p>Encodings of the same Address may be distributed zero or more times through different means. Zero or more Consumers may import Addresses. Zero or more of those (that are Senders) may execute a Transfer. A single Sender may execute multiple Transfers over time from a single import.</p>\n-                <p>Steps 1 to 5 inclusive also apply to Interaction Flows for Unified Full Viewing Keys and Unified Incoming Viewing Keys.</p>\n+                <p>Steps 1 to 5 inclusive also apply to Interaction Flows for Full Viewing Keys and Incoming Viewing Keys.</p>\n             </section>\n             <section id=\"addresses\"><h3><span class=\"section-heading\">Addresses</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#addresses\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>A Unified Address (or UA for short) combines one or more Receivers.</p>\n@@ -123,15 +136,16 @@\n             <section id=\"receivers\"><h3><span class=\"section-heading\">Receivers</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#receivers\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>Every wallet must properly <em>parse</em> encodings of a Unified Address or Unified Viewing Key containing unrecognized Items.</p>\n                 <p>A wallet may process unrecognized Items by indicating to the user their presence or similar information for usability or diagnostic purposes.</p>\n+                <p>Unified Addresses and Unified Viewing Keys must support sufficiently many Receiver Types to allow for reasonable future expansion.</p>\n             </section>\n-            <section id=\"transport-encoding\"><h3><span class=\"section-heading\">Transport Encoding</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#transport-encoding\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"transport-encodings\"><h3><span class=\"section-heading\">Transport Encodings</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#transport-encodings\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The Unified String Encoding is “opaque” to human readers: it does <em>not</em> allow visual identification of which Receivers or Receiver Types are present.</p>\n                 <p>The Unified String Encoding is resilient against typos, transcription errors, cut-and-paste errors, truncation, or other likely UX hazards.</p>\n+                <p>The Unified String Encoding is resistant, as far as reasonably possible, to “malleability attacks” that attempt to substitute a visually similar string representing a different valid Address in order to lure a user into sending funds to the wrong Address. In particular, as long as a user checks sufficiently many characters of a given Unified String Encoding (say, at least 20 characters) against the corresponding characters of a known-good reference Unified String Encoding, that check should be sufficient to give them confidence that a malleability attack would be infeasible: either they have an invalid Address Encoding, or one identical to the reference.</p>\n                 <p>There is a well-defined Unified QR Encoding of a Unified Address (or UFVK or UIVK) as a QR code, which produces QR codes that are reasonably compact and robust.</p>\n-                <p>There is a well-defined transformation between the Unified QR Encoding and Unified String Encoding of a given UA/UVK in either direction.</p>\n+                <p>There is a well-defined Unified Raw Encoding of a UA/UVK which is a compact byte sequence, without excessive encoding overhead. Since the Unified Raw Encoding is intended to only be used internally to Zcash-related software or transmitted over reliable channels, it need not have the resilience and nonmalleability properties described above.</p>\n+                <p>There are well-defined transformations between the Unified QR Encoding, Unified String Encoding, and Unified Raw Encoding of a given UA/UVK in any direction.</p>\n                 <p>The Unified String Encoding fits into ZIP-321 Payment URIs <a id=\"footnote-reference-6\" class=\"footnote_reference\" href=\"#zip-0321\">26</a> and general URIs without introducing parse ambiguities.</p>\n-                <p>The encoding must support sufficiently many Recipient Types to allow for reasonable future expansion.</p>\n-                <p>The encoding must allow all wallets to safely and correctly parse out unrecognized Receiver Types well enough to ignore them.</p>\n             </section>\n             <section id=\"transfers\"><h3><span class=\"section-heading\">Transfers</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#transfers\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>When executing a Transfer the Sender selects a Receiver via a Selection process.</p>\n@@ -155,16 +169,65 @@\n             </section>\n         </section>\n         <section id=\"specification\"><h2><span class=\"section-heading\">Specification</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#specification\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n-            <section id=\"encoding-of-unified-addresses\"><h3><span class=\"section-heading\">Encoding of Unified Addresses</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#encoding-of-unified-addresses\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n-                <p>Rather than defining a Bech32 string encoding of Orchard Shielded Payment Addresses, we instead define a Unified Address format that is able to encode a set of Receivers of different types. This enables the Consumer of a Unified Address to choose the Receiver of the best type it supports, providing a better user experience as new Receiver Types are added in the future.</p>\n-                <p>Assume that we are given a set of one or more Receiver Encodings for distinct types. That is, the set may optionally contain one Receiver of each of the Receiver Types in the following fixed Priority List:</p>\n+            <p>Rather than defining a Bech32 string encoding of Orchard Shielded Payment Addresses, we instead define a Unified Address format that is able to encode a set of Receivers of different types. This enables the Consumer of a Unified Address to choose the Receiver of the best type it supports, providing a better user experience as new Receiver Types are added in the future.</p>\n+            <p>Similarly, a Unified Full Viewing Key or Unified Incoming Viewing Key provides the corresponding visibility into transactions that may use addresses of different types. Typically these will be the addresses for each pool derived from an Account as defined in <a id=\"footnote-reference-10\" class=\"footnote_reference\" href=\"#zip-0032-specification-wallet-usage\">20</a>. (It is not possible for a UVK to include viewing keys for multiple addresses of the same type.)</p>\n+            <p>When a string encoding of a Unified Address or Unified Viewing Key is shown to a user, it MUST be encoded using the Unified String Encoding defined in <a href=\"#string-encodings-of-unified-addresses-and-unified-viewing-keys\">String Encodings of Unified Addresses and Unified Viewing Keys</a>. The main reason for this is to satisfy the requirements concerning resilience to user error and resistance to malleability attacks, as described in <a href=\"#transport-encodings\">Transport Encodings</a>.</p>\n+            <p>In cases where Unified Addresses or Unified Viewing Keys are not shown directly to users but need to be encoded as machine-readable data, the Unified Raw Encoding MAY be used instead of the Unified String Encoding. This has the advantage of reducing the space requirement, and may also reduce computational costs and implementation complexity.</p>\n+            <p>A Unified Raw Encoding has no resilience to data corruption and transcription errors, or resistance to malleability attacks, and therefore MUST NOT be used in situations where these properties are required. For clarity, this includes <em>all</em> situations where Address Encodings are exposed directly to end-users.</p>\n+            <section id=\"encoding-prefixes\"><h3><span class=\"section-heading\">Encoding Prefixes</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#encoding-prefixes\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>The following HRPs (Human-Readable Parts as defined in <a id=\"footnote-reference-11\" class=\"footnote_reference\" href=\"#bip-0350\">33</a>) and Lead Bytes are defined to tag particular Unified Address or Viewing Key types on a specific network.</p>\n+                <table>\n+                    <thead>\n+                        <tr>\n+                            <th>Meaning</th>\n+                            <th>HRP</th>\n+                            <th>Lead Byte</th>\n+                        </tr>\n+                    </thead>\n+                    <tbody>\n+                        <tr>\n+                            <td>Unified Addresses on Mainnet</td>\n+                            <td><code>u</code></td>\n+                            <td>0</td>\n+                        </tr>\n+                        <tr>\n+                            <td>Unified Addresses on Testnet</td>\n+                            <td><code>utest</code></td>\n+                            <td>1</td>\n+                        </tr>\n+                        <tr>\n+                            <td>Unified Incoming Viewing Keys on Mainnet</td>\n+                            <td><code>uivk</code></td>\n+                            <td>2</td>\n+                        </tr>\n+                        <tr>\n+                            <td>Unified Incoming Viewing Keys on Testnet</td>\n+                            <td><code>uivktest</code></td>\n+                            <td>3</td>\n+                        </tr>\n+                        <tr>\n+                            <td>Unified Full Viewing Keys on Mainnet</td>\n+                            <td><code>uview</code></td>\n+                            <td>4</td>\n+                        </tr>\n+                        <tr>\n+                            <td>Unified Full Viewing Keys on Testnet</td>\n+                            <td><code>uviewtest</code></td>\n+                            <td>5</td>\n+                        </tr>\n+                    </tbody>\n+                </table>\n+                <p>Their usage is defined in the following sections.</p>\n+            </section>\n+            <section id=\"raw-encoding-of-unified-addresses\"><h3><span class=\"section-heading\">Raw Encoding of Unified Addresses</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#raw-encoding-of-unified-addresses\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>Assume that we are given a set of one or more Receiver Items for distinct types. That is, the set may optionally contain one Receiver of each of the Receiver Types in the following fixed Priority List:</p>\n                 <ul>\n                     <li>Typecode\n                         <span class=\"math\">\\(\\mathtt{0x03}\\)</span>\n-                     — an Orchard raw address as defined in <a id=\"footnote-reference-10\" class=\"footnote_reference\" href=\"#protocol-orchardpaymentaddrencoding\">10</a>;</li>\n+                     — an Orchard raw address as defined in <a id=\"footnote-reference-12\" class=\"footnote_reference\" href=\"#protocol-orchardpaymentaddrencoding\">10</a>;</li>\n                     <li>Typecode\n                         <span class=\"math\">\\(\\mathtt{0x02}\\)</span>\n-                     — a Sapling raw address as defined in <a id=\"footnote-reference-11\" class=\"footnote_reference\" href=\"#protocol-saplingpaymentaddrencoding\">9</a>;</li>\n+                     — a Sapling raw address as defined in <a id=\"footnote-reference-13\" class=\"footnote_reference\" href=\"#protocol-saplingpaymentaddrencoding\">9</a>;</li>\n                     <li>Typecode\n                         <span class=\"math\">\\(\\mathtt{0x01}\\)</span>\n                      — a Transparent P2SH address, <em>or</em> Typecode\n@@ -175,9 +238,14 @@\n                 <p>We say that a Receiver Type is “preferred” over another when it appears earlier in this Priority List (as potentially modified by experiments).</p>\n                 <p>The Sender of a payment to a Unified Address MUST use the Receiver of the most preferred Receiver Type that it supports from the set.</p>\n                 <p>For example, consider a wallet that supports sending funds to Orchard Receivers, and does not support sending to any Receiver Type that is preferred over Orchard. If that wallet is given a UA that includes an Orchard Receiver and possibly other Receivers, it MUST send to the Orchard Receiver.</p>\n-                <p>The raw encoding of a Unified Address is a concatenation of\n+                <p>A Binary HRP is a single length byte\n+                    <span class=\"math\">\\(\\mathtt{hrp\\_len}\\)</span>\n+                 from 0 to 16 inclusive, followed by\n+                    <span class=\"math\">\\(\\mathtt{hrp\\_len}\\)</span>\n+                 bytes of a US-ASCII-encoded HRP.</p>\n+                <p>The Unified Raw Encoding of a Unified Address is a Binary HRP followed by a concatenation of\n                     <span class=\"math\">\\((\\mathtt{typecode}, \\mathtt{length}, \\mathtt{addr})\\)</span>\n-                 encodings of the consituent Receivers, in ascending order of Typecode:</p>\n+                 encodings of the constituent Receivers, in ascending order of Typecode:</p>\n                 <ul>\n                     <li>\n                         <span class=\"math\">\\(\\mathtt{typecode} : \\mathtt{compactSize}\\)</span>\n@@ -189,7 +257,7 @@\n                     </li>\n                     <li>\n                         <span class=\"math\">\\(\\mathtt{addr} : \\mathtt{byte[length]}\\)</span>\n-                     — the Receiver Encoding.</li>\n+                     — the Receiver Item.</li>\n                 </ul>\n                 <p>The values of the\n                     <span class=\"math\">\\(\\mathtt{typecode}\\)</span>\n@@ -200,43 +268,24 @@\n                  (The limitation on the total length of encodings described below imposes a smaller limit for\n                     <span class=\"math\">\\(\\mathtt{length}\\)</span>\n                  in practice.)</p>\n-                <p>A Receiver Encoding is the raw encoding of a Shielded Payment Address, or the\n+                <p>A Receiver Item is the raw encoding of a Shielded Payment Address, or the\n                     <span class=\"math\">\\(160\\!\\)</span>\n-                -bit script hash of a P2SH address <a id=\"footnote-reference-12\" class=\"footnote_reference\" href=\"#p2sh\">35</a>, or the\n+                -bit script hash of a P2SH address <a id=\"footnote-reference-14\" class=\"footnote_reference\" href=\"#p2sh\">35</a>, or the\n                     <span class=\"math\">\\(160\\!\\)</span>\n-                -bit validating key hash of a P2PKH address <a id=\"footnote-reference-13\" class=\"footnote_reference\" href=\"#p2pkh\">34</a>.</p>\n-                <p>Let <code>padding</code> be the Human-Readable Part of the Unified Address in US-ASCII, padded to 16 bytes with zero bytes. We append <code>padding</code> to the concatenated encodings, and then apply the\n-                    <span class=\"math\">\\(\\mathsf{F4Jumble}\\)</span>\n-                 algorithm as described in <a href=\"#jumbling\">Jumbling</a>. (In order for the limitation on the\n-                    <span class=\"math\">\\(\\mathsf{F4Jumble}\\)</span>\n-                 input size to be met, the total length of encodings MUST be at most\n-                    <span class=\"math\">\\(\\ell^\\mathsf{MAX}_M - 16\\)</span>\n-                 bytes, where\n-                    <span class=\"math\">\\(\\ell^\\mathsf{MAX}_M\\)</span>\n-                 is defined in <a href=\"#jumbling\">Jumbling</a>.) The output is then encoded with Bech32m <a id=\"footnote-reference-14\" class=\"footnote_reference\" href=\"#bip-0350\">33</a>, ignoring any length restrictions. This is chosen over Bech32 in order to better handle variable-length inputs.</p>\n-                <p>To decode a Unified Address Encoding, a Consumer MUST use the following procedure:</p>\n-                <ul>\n-                    <li>Decode using Bech32m, rejecting any address with an incorrect checksum.</li>\n-                    <li>Apply\n-                        <span class=\"math\">\\(\\mathsf{F4Jumble}^{-1}\\)</span>\n-                     (this can also reject if the input is not in the correct range of lengths).</li>\n-                    <li>Let <code>padding</code> be the Human-Readable Part, padded to 16 bytes as for encoding. If the result ends in <code>padding</code>, remove these 16 bytes; otherwise reject.</li>\n-                    <li>Parse the result as a raw encoding as described above, rejecting the entire Unified Address if it does not parse correctly.</li>\n-                </ul>\n-                <p>For Unified Addresses on Mainnet, the Human-Readable Part (as defined in <a id=\"footnote-reference-15\" class=\"footnote_reference\" href=\"#bip-0350\">33</a>) is “<code>u</code>”. For Unified Addresses on Testnet, the Human-Readable Part is “<code>utest</code>”.</p>\n+                -bit validating key hash of a P2PKH address <a id=\"footnote-reference-15\" class=\"footnote_reference\" href=\"#p2pkh\">34</a>.</p>\n                 <p>A wallet MAY allow its user(s) to configure which Receiver Types it can send to. It MUST NOT allow the user(s) to change the order of the Priority List used to choose the Receiver Type, except by opting into experiments.</p>\n             </section>\n-            <section id=\"encoding-of-unified-full-incoming-viewing-keys\"><h3><span class=\"section-heading\">Encoding of Unified Full/Incoming Viewing Keys</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#encoding-of-unified-full-incoming-viewing-keys\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n-                <p>Unified Full or Incoming Viewing Keys are encoded and decoded analogously to Unified Addresses. A Consumer MUST use the decoding procedure from the previous section. For Viewing Keys, a Consumer will normally take the union of information provided by all contained Receivers, and therefore the Priority List defined in the previous section is not used.</p>\n+            <section id=\"raw-encoding-of-unified-full-incoming-viewing-keys\"><h3><span class=\"section-heading\">Raw Encoding of Unified Full/Incoming Viewing Keys</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#raw-encoding-of-unified-full-incoming-viewing-keys\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>Unified Full or Incoming Viewing Keys are encoded and decoded analogously to Unified Addresses. For Viewing Keys, a Consumer will normally take the union of information provided by all contained Receivers, and therefore the Priority List defined in the previous section is not used.</p>\n                 <p>For each FVK Type or IVK Type currently defined in this specification, the same Typecode is used as for the corresponding Receiver Type in a Unified Address. Additional FVK Types and IVK Types MAY be defined in future, and these will not necessarily use the same Typecode as the corresponding Unified Address.</p>\n-                <p>The following FVK or IVK Encodings are used in place of the\n+                <p>The following FVK or IVK Items are used in place of the\n                     <span class=\"math\">\\(\\mathtt{addr}\\)</span>\n                  field:</p>\n                 <ul>\n-                    <li>An Orchard FVK or IVK Encoding, with Typecode\n+                    <li>An Orchard FVK or IVK Item, with Typecode\n                         <span class=\"math\">\\(\\mathtt{0x03},\\)</span>\n                      is is the raw encoding of the Orchard Full Viewing Key or Orchard Incoming Viewing Key respectively.</li>\n-                    <li>A Sapling FVK Encoding, with Typecode\n+                    <li>A Sapling FVK Item, with Typecode\n                         <span class=\"math\">\\(\\mathtt{0x02},\\)</span>\n                      is the encoding of\n                         <span class=\"math\">\\((\\mathsf{ak}, \\mathsf{nk}, \\mathsf{ovk}, \\mathsf{dk})\\)</span>\n@@ -245,7 +294,7 @@\n                     , where\n                         <span class=\"math\">\\(\\mathsf{EncodeExtFVKParts}\\)</span>\n                      is defined in <a id=\"footnote-reference-16\" class=\"footnote_reference\" href=\"#zip-0032-sapling-helper-functions\">14</a>. This SHOULD be derived from the Extended Full Viewing Key at the Account level of the ZIP 32 hierarchy.</li>\n-                    <li>A Sapling IVK Encoding, also with Typecode\n+                    <li>A Sapling IVK Item, also with Typecode\n                         <span class=\"math\">\\(\\mathtt{0x02},\\)</span>\n                      is an encoding of\n                         <span class=\"math\">\\((\\mathsf{dk}, \\mathsf{ivk})\\)</span>\n@@ -255,7 +304,7 @@\n                     <li>There is no defined way to represent a Viewing Key for a Transparent P2SH Address in a UFVK or UIVK (because P2SH Addresses cannot be diversified in an unlinkable way). The Typecode\n                         <span class=\"math\">\\(\\mathtt{0x01}\\)</span>\n                      MUST NOT be included in a UFVK or UIVK by Producers, and MUST be treated as unrecognized by Consumers.</li>\n-                    <li>For Transparent P2PKH Addresses that are derived according to BIP 32 <a id=\"footnote-reference-17\" class=\"footnote_reference\" href=\"#bip-0032\">27</a> and BIP 44 <a id=\"footnote-reference-18\" class=\"footnote_reference\" href=\"#bip-0044\">30</a>, the FVK and IVK Encodings have Typecode\n+                    <li>For Transparent P2PKH Addresses that are derived according to BIP 32 <a id=\"footnote-reference-17\" class=\"footnote_reference\" href=\"#bip-0032\">27</a> and BIP 44 <a id=\"footnote-reference-18\" class=\"footnote_reference\" href=\"#bip-0044\">30</a>, the FVK and IVK Items have Typecode\n                         <span class=\"math\">\\(\\mathtt{0x00}.\\)</span>\n                      Both of these are encodings of the chain code and public key\n                         <span class=\"math\">\\((\\mathsf{c}, \\mathsf{pk})\\)</span>\n@@ -267,18 +316,84 @@\n                         <span class=\"math\">\\(m / 44' / coin\\_type' / account' / 0\\)</span>\n                     .</li>\n                 </ul>\n-                <p>The Human-Readable Parts (as defined in <a id=\"footnote-reference-20\" class=\"footnote_reference\" href=\"#bip-0350\">33</a>) of Unified Viewing Keys are defined as follows:</p>\n-                <ul>\n-                    <li>“<code>uivk</code>” for Unified Incoming Viewing Keys on Mainnet;</li>\n-                    <li>“<code>uivktest</code>” for Unified Incoming Viewing Keys on Testnet;</li>\n-                    <li>“<code>uview</code>” for Unified Full Viewing Keys on Mainnet;</li>\n-                    <li>“<code>uviewtest</code>” for Unified Full Viewing Keys on Testnet.</li>\n-                </ul>\n                 <section id=\"rationale-for-address-derivation\"><h4><span class=\"section-heading\">Rationale for address derivation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#rationale-for-address-derivation\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h4>\n                     <p>The design of address derivation is designed to maintain unlinkability between addresses derived from the same UIVK, to the extent possible. (This is only partially achieved if the UA contains a Transparent P2PKH Address, since the on-chain transaction graph can potentially be used to link transparent addresses.)</p>\n                     <p>Note that it may be difficult to retain this property for Metadata Items, and this should be taken into account in the design of such Items.</p>\n                 </section>\n             </section>\n+            <section id=\"string-encodings-of-unified-addresses-and-unified-viewing-keys\"><h3><span class=\"section-heading\">String Encodings of Unified Addresses and Unified Viewing Keys</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#string-encodings-of-unified-addresses-and-unified-viewing-keys\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>A Unified String Encoding for a UA/UIVK is obtained by constructing the corresponding Unified Raw Encoding as defined in previous sections, and then converting it to a Unified String Encoding as follows.</p>\n+                <p>Let\n+                    <span class=\"math\">\\(\\ell^\\mathsf{MAX}_M\\)</span>\n+                 be as defined in <a href=\"#jumbling\">Jumbling</a>.</p>\n+                <ol type=\"1\">\n+                    <li>Parse and strip the Binary HRP from the start of the Unified Raw Encoding\n+                        <span class=\"math\">\\(\\mathtt{R}\\)</span>\n+                    , as follows:\n+                        <ul>\n+                            <li>If\n+                                <span class=\"math\">\\(\\mathsf{length}(\\mathtt{R}) = 0\\!\\)</span>\n+                            , reject.</li>\n+                            <li>Let\n+                                <span class=\"math\">\\(\\mathtt{hrp\\_len} = \\mathtt{R}[0]\\!\\)</span>\n+                            . If\n+                                <span class=\"math\">\\(\\mathtt{hrp\\_len} &gt; 16\\)</span>\n+                             or\n+                                <span class=\"math\">\\(\\mathtt{hrp\\_len} &gt; \\mathsf{length}(\\mathtt{R}) - 1\\)</span>\n+                             or\n+                                <span class=\"math\">\\(\\mathsf{length}(\\mathtt{R}) - 1 - \\mathtt{hrp\\_len} &gt; \\ell^\\mathsf{MAX}_M - 16\\!\\)</span>\n+                            , reject.</li>\n+                            <li>Let\n+                                <span class=\"math\">\\(\\mathtt{hrp} = \\mathtt{R}[1\\;..\\!=\\mathtt{hrp\\_len}]\\!\\)</span>\n+                            .</li>\n+                            <li>Let\n+                                <span class=\"math\">\\(\\mathtt{raw\\_items} = \\mathtt{R}[1 + \\mathtt{hrp\\_len}\\;..\\!=\\mathsf{length}(\\mathtt{R}) - 1]\\)</span>\n+                             (i.e. the remainder of the Unified Raw Encoding after the Binary HRP).</li>\n+                        </ul>\n+                    </li>\n+                    <li>Let\n+                        <span class=\"math\">\\(\\mathtt{padded\\_hrp}\\)</span>\n+                     be\n+                        <span class=\"math\">\\(\\mathtt{hrp}\\)</span>\n+                     padded to 16 bytes with zero bytes.</li>\n+                    <li>Apply the\n+                        <span class=\"math\">\\(\\mathsf{F4Jumble}\\)</span>\n+                     algorithm described in <a href=\"#jumbling\">Jumbling</a> to\n+                        <span class=\"math\">\\(\\mathtt{raw\\_items} \\,||\\, \\mathtt{padded\\_hrp}\\!\\)</span>\n+                    .</li>\n+                    <li>Encode the output with Bech32m <a id=\"footnote-reference-20\" class=\"footnote_reference\" href=\"#bip-0350\">33</a>, ignoring any length restrictions.</li>\n+                </ol>\n+                <p>Any equivalent procedure MAY be used, for example, a Producer MAY construct\n+                    <span class=\"math\">\\(\\mathtt{padded\\_hrp}\\)</span>\n+                 and\n+                    <span class=\"math\">\\(\\mathtt{raw\\_items}\\)</span>\n+                 directly rather than explicitly obtaining them from a Unified Raw Encoding.</p>\n+                <p>The check that\n+                    <span class=\"math\">\\(\\mathsf{length}(\\mathtt{R}) - 1 - \\mathtt{hrp\\_len} &gt; \\ell^\\mathsf{MAX}_M - 16\\)</span>\n+                 in step 1 ensures that\n+                    <span class=\"math\">\\(\\mathtt{raw\\_items} \\,||\\, \\mathtt{padded\\_hrp}\\)</span>\n+                 never exceeds the\n+                    <span class=\"math\">\\(\\mathsf{F4Jumble}\\)</span>\n+                 input size limitation in step 3.</p>\n+                <p>Bech32m is chosen over Bech32 in order to better handle variable-length inputs.</p>\n+                <p>To decode a Unified String Encoding, a Consumer MUST use the following procedure:</p>\n+                <ol type=\"1\">\n+                    <li>Decode using Bech32m, rejecting any address with an incorrect checksum. This yields HRP and data fields. If the HRP is not recognised or not supported (for example, a Testnet HRP for a Consumer that only supports Mainnet), reject.</li>\n+                    <li>Let\n+                        <span class=\"math\">\\(\\mathtt{expected\\_padded\\_hrp}\\)</span>\n+                     be the US-ASCII-encoded HRP, padded to 16 bytes with zero bytes.</li>\n+                    <li>Apply\n+                        <span class=\"math\">\\(\\mathsf{F4Jumble}^{-1}\\)</span>\n+                     to the payload (this can also reject if the input is not in the correct range of lengths).</li>\n+                    <li>If the output ends in\n+                        <span class=\"math\">\\(\\mathtt{expected\\_padded\\_hrp}\\)</span>\n+                    , remove these 16 bytes; otherwise reject.</li>\n+                    <li>Parse the result as\n+                        <span class=\"math\">\\(\\mathtt{raw\\_items}\\)</span>\n+                     described above, rejecting the entire Unified Address if it does not parse correctly.</li>\n+                </ol>\n+                <p>The Human-Readable Parts of Unified Addresses and Unified Viewing Keys are defined as given in <a href=\"#encoding-prefixes\">Encoding Prefixes</a>.</p>\n+            </section>\n             <section id=\"requirements-for-both-unified-addresses-and-unified-viewing-keys\"><h3><span class=\"section-heading\">Requirements for both Unified Addresses and Unified Viewing Keys</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#requirements-for-both-unified-addresses-and-unified-viewing-keys\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <ul>\n                     <li>A Unified Address or Unified Viewing Key MUST contain at least one shielded Item (Typecodes\n@@ -292,13 +407,13 @@\n                         <span class=\"math\">\\(\\mathtt{length}\\)</span>\n                      fields are encoded as\n                         <span class=\"math\">\\(\\mathtt{compactSize}.\\)</span>\n-                     <a id=\"footnote-reference-21\" class=\"footnote_reference\" href=\"#bitcoin-compactsize\">36</a> (Although existing Receiver Encodings and Viewing Key Encodings are all less than 256 bytes and so could use a one-byte length field, encodings for experimental types may be longer.)</li>\n+                     <a id=\"footnote-reference-21\" class=\"footnote_reference\" href=\"#bitcoin-compactsize\">36</a> (Although existing Receiver Items and Viewing Key Items are all less than 256 bytes and so could use a one-byte length field, encodings for experimental types may be longer.)</li>\n                     <li>Within a single UA or UVK, all HD-derived Receivers, FVKs, and IVKs SHOULD represent an Address or Viewing Key for the same account (as used in the ZIP 32 or BIP 44 Account level).</li>\n-                    <li>For Transparent Addresses, the Receiver Encoding does not include the first two bytes of a raw encoding.</li>\n+                    <li>For Transparent Addresses, the Receiver Item does not include the first two bytes of a raw encoding.</li>\n                     <li>There is intentionally no Typecode defined for a Sprout Shielded Payment Address or Sprout Incoming Viewing Key. Since it is no longer possible (since activation of ZIP 211 in the Canopy network upgrade <a id=\"footnote-reference-22\" class=\"footnote_reference\" href=\"#zip-0211\">23</a>) to send funds into the Sprout chain value pool, this would not be generally useful.</li>\n                     <li>Consumers MUST ignore constituent Items with Typecodes they do not recognize.</li>\n                     <li>Consumers MUST reject Unified Addresses/Viewing Keys in which the same Typecode appears more than once, or that include both P2SH and P2PKH Transparent Addresses, or that contain only a Transparent Address.</li>\n-                    <li>Consumers MUST reject Unified Addresses/Viewing Keys in which <em>any</em> constituent Item does not meet the validation requirements of its encoding, as specified in this ZIP and the Zcash Protocol Specification <a id=\"footnote-reference-23\" class=\"footnote_reference\" href=\"#protocol-nu5\">2</a>.</li>\n+                    <li>Consumers MUST reject Unified Addresses/Viewing Keys in which <em>any</em> constituent Item that the Consumer recognizes does not meet the validation requirements of its encoding, as specified in this ZIP and the Zcash Protocol Specification <a id=\"footnote-reference-23\" class=\"footnote_reference\" href=\"#protocol-nu5\">2</a>.</li>\n                     <li>Consumers MUST reject Unified Addresses/Viewing Keys in which the constituent Items are not ordered in ascending Typecode order. Note that this is different to priority order, and does not affect which Receiver in a Unified Address should be used by a Sender.</li>\n                     <li>There MUST NOT be additional bytes at the end of the raw encoding that cannot be interpreted as specified above.</li>\n                     <li>If the encoding of a Unified Address/Viewing Key is shown to a user in an abridged form due to lack of space, at least the first 20 characters MUST be included.</li>\n@@ -355,7 +470,7 @@\n                     <span class=\"math\">\\(\\mathsf{ovk}\\)</span>\n                  components from the transparent FVK\n                     <span class=\"math\">\\((\\mathsf{c}, \\mathsf{pk})\\)</span>\n-                 (described in <a href=\"#encoding-of-unified-full-incoming-viewing-keys\">Encoding of Unified Full/Incoming Viewing Keys</a>) as follows:</p>\n+                 (described in <a href=\"#raw-encoding-of-unified-full-incoming-viewing-keys\">Raw Encoding of Unified Full/Incoming Viewing Keys</a>) as follows:</p>\n                 <ul>\n                     <li>Let\n                         <span class=\"math\">\\(I_\\mathsf{ovk} = \\mathsf{PRF^{expand}}_{\\mathsf{LEOS2BSP}_{256}(\\mathsf{c})}\\big([\\mathtt{0xd0}] \\,||\\, \\mathsf{ser_P}(\\mathsf{pk})\\big)\\)</span>\n@@ -419,7 +534,7 @@\n                 <p>However, the specification of which outgoing viewing key should be used is left somewhat open in <a id=\"footnote-reference-40\" class=\"footnote_reference\" href=\"#protocol-saplingsend\">6</a> and <a id=\"footnote-reference-41\" class=\"footnote_reference\" href=\"#protocol-orchardsend\">7</a>; in particular, it was unclear whether transfers should be considered as being sent from an address, or from a ZIP 32 account <a id=\"footnote-reference-42\" class=\"footnote_reference\" href=\"#zip-0032-specification-wallet-usage\">20</a>. The adoption of multiple shielded protocols that support outgoing viewing keys (i.e. Sapling and Orchard) further complicates this question, since from NU5 activation, nothing at the consensus level prevents a wallet from spending both Sapling and Orchard notes in the same transaction. (Recommendations about wallet usage of multiple pools will be given in ZIP 315 <a id=\"footnote-reference-43\" class=\"footnote_reference\" href=\"#zip-0315\">25</a>.)</p>\n                 <p>Here we refine the protocol specification in order to allow more precise determination of viewing authority for UFVKs.</p>\n                 <p>A Sender will attempt to determine a \"sending Account\" for each transfer. The preferred approach is for the API used to perform a transfer to directly specify a sending Account. Otherwise, if the Sender can ascertain that all funds used in the transfer are from addresses associated with some Account, then it SHOULD treat that as the sending Account. If not, then the sending Account is undetermined.</p>\n-                <p>The Sender also determines a \"preferred sending protocol\" —one of \"transparent\", \"Sapling\", or \"Orchard\"— corresponding to the most preferred Receiver Type (as given in <a href=\"#encoding-of-unified-addresses\">Encoding of Unified Addresses</a>) of any funds sent in the transaction.</p>\n+                <p>The Sender also determines a \"preferred sending protocol\" —one of \"transparent\", \"Sapling\", or \"Orchard\"— corresponding to the most preferred Receiver Type (as given in <a href=\"#raw-encoding-of-unified-addresses\">Raw Encoding of Unified Addresses</a>) of any funds sent in the transaction.</p>\n                 <p>If the sending Account has been determined, then the Sender SHOULD use the external or internal\n                     <span class=\"math\">\\(\\mathsf{ovk}\\)</span>\n                  (according to the type of transfer), as specified by the preferred sending protocol, of the full viewing key for that Account (i.e. at the ZIP 32 Account level).</p>\n@@ -715,6 +830,15 @@\n                     <p><a href=\"https://www.cl.cam.ac.uk/~rja14/Papers/bear-lion.pdf\">LIONESS</a> is a similarly structured 4-round unbalanced Feistel cipher.</p>\n                 </section>\n             </section>\n+            <section id=\"qr-encodings-of-unified-addresses-and-unified-viewing-keys\"><h3><span class=\"section-heading\">QR Encodings of Unified Addresses and Unified Viewing Keys</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#qr-encodings-of-unified-addresses-and-unified-viewing-keys\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>The Unified QR Encoding of a UA or UVK is as specified in <a id=\"footnote-reference-44\" class=\"footnote_reference\" href=\"#protocol-addressandkeyencoding\">8</a>; that is, the QR code representation of its Unified String Encoding converted to uppercase, using the Alphanumeric mode specified in sections 7.3.4 and 7.4.4 of <a id=\"footnote-reference-45\" class=\"footnote_reference\" href=\"#iso-qr-codes\">37</a>.</p>\n+                <p>A Consumer MAY support parsing multiple kinds of address and/or viewing key from a QR code, in which case it SHOULD use the HRP to provisionally recognize (subject to further validation) potential address or viewing key types.</p>\n+                <p>When a Consumer recognizes the content of a QR code as a potential Unified String Encoding, it SHOULD NOT present the resulting content to the user as representing an address, without first checking that it is a valid Unified String Encoding by attempting to decode it.</p>\n+                <p>A Producer MUST NOT generate a QR Encoding directly from a Unified Raw Encoding (using the QR \"Byte mode\" defined in <a id=\"footnote-reference-46\" class=\"footnote_reference\" href=\"#iso-qr-codes\">37</a> section 7.3.5, or otherwise).</p>\n+                <section id=\"rationale-for-not-using-byte-mode\"><h4><span class=\"section-heading\">Rationale for not using Byte mode</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#rationale-for-not-using-byte-mode\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h4>\n+                    <p>A QR code using the Unified Raw Encoding in Byte mode could be slightly smaller, however we believe this choice would be likely to cause interoperability problems. It also would not satisfy the requirement that the Unified Raw Encoding not be shown to users, since it is common for software that supports QR codes to try to decode the content and present it as a string. (Clause 6.1 b) 3) of <a id=\"footnote-reference-47\" class=\"footnote_reference\" href=\"#iso-qr-codes\">37</a> specifies that the default interpretation of byte data is as an ISO-8859-1 text string.)</p>\n+                </section>\n+            </section>\n         </section>\n         <section id=\"reference-implementation\"><h2><span class=\"section-heading\">Reference implementation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#reference-implementation\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <ul>\n@@ -1014,6 +1138,14 @@\n                     </tr>\n                 </tbody>\n             </table>\n+            <table id=\"iso-qr-codes\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>37</th>\n+                        <td><a href=\"https://raw.githubusercontent.com/yansikeim/QR-Code/master/ISO%20IEC%2018004%202015%20Standard.pdf\">ISO/IEC. International Standard ISO/IEC 18004:2015(E): Information Technology – Automatic identification and data capture techniques – QR Code bar code symbology specification. Third edition. February 1, 2015.</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n         </section>\n     </section>\n </body>"},{"filename":"zip-0316.rst","status":"modified","additions":216,"deletions":78,"patch":"@@ -43,20 +43,20 @@ Receiver\n   The necessary information to transfer an asset to a Recipient that generated\n   that Receiver using a specific Transfer Protocol. Each Receiver is associated\n   unambiguously with a specific Receiver Type, identified by an integer Typecode.\n-Receiver Encoding\n+Receiver Item\n   An encoding of a Receiver as a byte sequence.\n Viewing Key\n   The necessary information to view information about payments to an Address,\n   or (in the case of a Full Viewing Key) from an Address. An Incoming Viewing\n   Key can be derived from a Full Viewing Key, and an Address can be derived\n   from an Incoming Viewing Key.\n-Viewing Key Encoding\n+Viewing Key Item\n   An encoding of a Viewing Key as a byte sequence.\n-Metadata Encoding\n+Metadata Item\n   An encoding of metadata that is not a Receiver or Viewing Key, but may affect\n   the interpretation of the overall Unified Address/Viewing Key.\n Item\n-  An Receiver Encoding, Viewing Key Encoding, or Metadata Encoding.\n+  An Receiver Item, Viewing Key Item, or Metadata Item.\n Legacy Address\n   A Transparent, Sprout, or Sapling Address.\n Unified Address (or UA)\n@@ -86,13 +86,21 @@ Unified QR Encoding\n   An encoding of a UA/UVK as a QR code, intended for display and transfer\n   by Zcash end-users in situations where usability advantages of a 2D bar\n   code may be relevant.\n+Unified Raw Encoding\n+  An encoding of a UA/UVK as a byte sequence, intended *only* for internal\n+  use by Zcash-related software. Unified Raw Encodings MUST NOT be exposed\n+  to end-users, since they lack resilience and nonmalleability properties\n+  necessary for that purpose.\n Address Encoding\n-  The externally visible encoding of an Address (e.g. as a string of\n-  characters or a QR code).\n+  An externally visible encoding of an Address.\n \n Notation for sequences, conversions, and arithmetic operations follows the\n Zcash protocol specification [#protocol-notation]_.\n \n+The notation :math:`\\mathtt{X}[i\\,..\\!=j]`, where :math:`\\mathtt{X}` is a byte\n+sequence, means the subsequence of bytes of :math:`\\mathtt{X}` at zero-based\n+indices :math:`i` to :math:`j` inclusive.\n+\n \n Abstract\n ========\n@@ -195,8 +203,8 @@ different means. Zero or more Consumers may import Addresses. Zero or more of\n those (that are Senders) may execute a Transfer. A single Sender may execute\n multiple Transfers over time from a single import.\n \n-Steps 1 to 5 inclusive also apply to Interaction Flows for Unified Full Viewing\n-Keys and Unified Incoming Viewing Keys.\n+Steps 1 to 5 inclusive also apply to Interaction Flows for Full Viewing Keys\n+and Incoming Viewing Keys.\n \n Addresses\n ---------\n@@ -219,8 +227,11 @@ Unified Viewing Key containing unrecognized Items.\n A wallet may process unrecognized Items by indicating to the user their\n presence or similar information for usability or diagnostic purposes.\n \n-Transport Encoding\n-------------------\n+Unified Addresses and Unified Viewing Keys must support sufficiently many\n+Receiver Types to allow for reasonable future expansion.\n+\n+Transport Encodings\n+-------------------\n \n The Unified String Encoding is “opaque” to human readers: it does *not*\n allow visual identification of which Receivers or Receiver Types are\n@@ -229,22 +240,34 @@ present.\n The Unified String Encoding is resilient against typos, transcription\n errors, cut-and-paste errors, truncation, or other likely UX hazards.\n \n+The Unified String Encoding is resistant, as far as reasonably possible,\n+to “malleability attacks” that attempt to substitute a visually similar\n+string representing a different valid Address in order to lure a user\n+into sending funds to the wrong Address. In particular, as long as a user\n+checks sufficiently many characters of a given Unified String Encoding\n+(say, at least 20 characters) against the corresponding characters of\n+a known-good reference Unified String Encoding, that check should be\n+sufficient to give them confidence that a malleability attack would be\n+infeasible: either they have an invalid Address Encoding, or one\n+identical to the reference.\n+\n There is a well-defined Unified QR Encoding of a Unified Address (or\n UFVK or UIVK) as a QR code, which produces QR codes that are reasonably\n compact and robust.\n \n-There is a well-defined transformation between the Unified QR Encoding\n-and Unified String Encoding of a given UA/UVK in either direction.\n+There is a well-defined Unified Raw Encoding of a UA/UVK which is a\n+compact byte sequence, without excessive encoding overhead. Since\n+the Unified Raw Encoding is intended to only be used internally to\n+Zcash-related software or transmitted over reliable channels, it need\n+not have the resilience and nonmalleability properties described above.\n+\n+There are well-defined transformations between the Unified QR Encoding,\n+Unified String Encoding, and Unified Raw Encoding of a given UA/UVK in\n+any direction.\n \n The Unified String Encoding fits into ZIP-321 Payment URIs [#zip-0321]_\n and general URIs without introducing parse ambiguities.\n \n-The encoding must support sufficiently many Recipient Types to allow\n-for reasonable future expansion.\n-\n-The encoding must allow all wallets to safely and correctly parse out\n-unrecognized Receiver Types well enough to ignore them.\n-\n Transfers\n ---------\n \n@@ -303,17 +326,66 @@ associated UX issues, will be addressed in ZIP 315 (in preparation).\n Specification\n =============\n \n-Encoding of Unified Addresses\n------------------------------\n-\n Rather than defining a Bech32 string encoding of Orchard Shielded\n Payment Addresses, we instead define a Unified Address format that\n is able to encode a set of Receivers of different types. This enables\n the Consumer of a Unified Address to choose the Receiver of the best\n type it supports, providing a better user experience as new Receiver\n Types are added in the future.\n \n-Assume that we are given a set of one or more Receiver Encodings\n+Similarly, a Unified Full Viewing Key or Unified Incoming Viewing Key\n+provides the corresponding visibility into transactions that may use\n+addresses of different types. Typically these will be the addresses\n+for each pool derived from an Account as defined in [#zip-0032-specification-wallet-usage]_.\n+(It is not possible for a UVK to include viewing keys for multiple\n+addresses of the same type.)\n+\n+When a string encoding of a Unified Address or Unified Viewing Key is\n+shown to a user, it MUST be encoded using the Unified String Encoding\n+defined in `String Encodings of Unified Addresses and Unified Viewing Keys`_.\n+The main reason for this is to satisfy the requirements concerning\n+resilience to user error and resistance to malleability attacks, as\n+described in `Transport Encodings`_.\n+\n+In cases where Unified Addresses or Unified Viewing Keys are not\n+shown directly to users but need to be encoded as machine-readable\n+data, the Unified Raw Encoding MAY be used instead of the\n+Unified String Encoding. This has the advantage of reducing the\n+space requirement, and may also reduce computational costs and\n+implementation complexity.\n+\n+A Unified Raw Encoding has no resilience to data corruption and\n+transcription errors, or resistance to malleability attacks, and\n+therefore MUST NOT be used in situations where these properties are\n+required. For clarity, this includes *all* situations where Address\n+Encodings are exposed directly to end-users.\n+\n+\n+Encoding Prefixes\n+-----------------\n+\n+The following HRPs (Human-Readable Parts as defined in [#bip-0350]_)\n+and Lead Bytes are defined to tag particular Unified Address or\n+Viewing Key types on a specific network.\n+\n+======================================== ============= =========\n+Meaning                                  HRP           Lead Byte\n+======================================== ============= =========\n+Unified Addresses on Mainnet             ``u``         0\n+Unified Addresses on Testnet             ``utest``     1\n+Unified Incoming Viewing Keys on Mainnet ``uivk``      2\n+Unified Incoming Viewing Keys on Testnet ``uivktest``  3\n+Unified Full Viewing Keys on Mainnet     ``uview``     4\n+Unified Full Viewing Keys on Testnet     ``uviewtest`` 5\n+======================================== ============= =========\n+\n+Their usage is defined in the following sections.\n+\n+\n+Raw Encoding of Unified Addresses\n+---------------------------------\n+\n+Assume that we are given a set of one or more Receiver Items\n for distinct types. That is, the set may optionally contain one\n Receiver of each of the Receiver Types in the following fixed\n Priority List:\n@@ -345,66 +417,42 @@ preferred over Orchard. If that wallet is given a UA that includes an\n Orchard Receiver and possibly other Receivers, it MUST send to the\n Orchard Receiver.\n \n-The raw encoding of a Unified Address is a concatenation of\n-:math:`(\\mathtt{typecode}, \\mathtt{length}, \\mathtt{addr})` encodings\n-of the consituent Receivers, in ascending order of Typecode:\n+A Binary HRP is a single length byte :math:`\\mathtt{hrp\\_len}` from\n+0 to 16 inclusive, followed by :math:`\\mathtt{hrp\\_len}` bytes of a\n+US-ASCII-encoded HRP.\n+\n+The Unified Raw Encoding of a Unified Address is a Binary HRP followed by a\n+concatenation of :math:`(\\mathtt{typecode}, \\mathtt{length}, \\mathtt{addr})`\n+encodings of the constituent Receivers, in ascending order of Typecode:\n \n * :math:`\\mathtt{typecode} : \\mathtt{compactSize}` — the Typecode from the\n   above Priority List;\n \n * :math:`\\mathtt{length} : \\mathtt{compactSize}` — the length in bytes of\n   :math:`\\mathtt{addr};`\n \n-* :math:`\\mathtt{addr} : \\mathtt{byte[length]}` — the Receiver Encoding.\n+* :math:`\\mathtt{addr} : \\mathtt{byte[length]}` — the Receiver Item.\n \n The values of the :math:`\\mathtt{typecode}` and :math:`\\mathtt{length}`\n fields MUST be less than or equal to :math:`\\mathtt{0x2000000}.`\n (The limitation on the total length of encodings described below imposes\n a smaller limit for :math:`\\mathtt{length}` in practice.)\n \n-A Receiver Encoding is the raw encoding of a Shielded Payment Address,\n+A Receiver Item is the raw encoding of a Shielded Payment Address,\n or the :math:`160\\!`-bit script hash of a P2SH address [#P2SH]_, or the\n :math:`160\\!`-bit validating key hash of a P2PKH address [#P2PKH]_.\n \n-Let ``padding`` be the Human-Readable Part of the Unified Address in\n-US-ASCII, padded to 16 bytes with zero bytes. We append ``padding`` to\n-the concatenated encodings, and then apply the :math:`\\mathsf{F4Jumble}`\n-algorithm as described in `Jumbling`_. (In order for the limitation on\n-the :math:`\\mathsf{F4Jumble}` input size to be met, the total length of\n-encodings MUST be at most :math:`\\ell^\\mathsf{MAX}_M - 16` bytes, where\n-:math:`\\ell^\\mathsf{MAX}_M` is defined in `Jumbling`_.)\n-The output is then encoded with Bech32m [#bip-0350]_, ignoring any length\n-restrictions. This is chosen over Bech32 in order to better handle\n-variable-length inputs.\n-\n-To decode a Unified Address Encoding, a Consumer MUST use the following\n-procedure:\n-\n-* Decode using Bech32m, rejecting any address with an incorrect checksum.\n-* Apply :math:`\\mathsf{F4Jumble}^{-1}` (this can also reject if the input\n-  is not in the correct range of lengths).\n-* Let ``padding`` be the Human-Readable Part, padded to 16 bytes as for\n-  encoding. If the result ends in ``padding``, remove these 16 bytes;\n-  otherwise reject.\n-* Parse the result as a raw encoding as described above, rejecting the\n-  entire Unified Address if it does not parse correctly.\n-\n-For Unified Addresses on Mainnet, the Human-Readable Part (as defined\n-in [#bip-0350]_) is “``u``”. For Unified Addresses on Testnet, the\n-Human-Readable Part is “``utest``”.\n-\n A wallet MAY allow its user(s) to configure which Receiver Types it\n can send to. It MUST NOT allow the user(s) to change the order of the\n Priority List used to choose the Receiver Type, except by opting into\n experiments.\n \n \n-Encoding of Unified Full/Incoming Viewing Keys\n-----------------------------------------------\n+Raw Encoding of Unified Full/Incoming Viewing Keys\n+--------------------------------------------------\n \n Unified Full or Incoming Viewing Keys are encoded and decoded\n-analogously to Unified Addresses. A Consumer MUST use the decoding\n-procedure from the previous section. For Viewing Keys, a Consumer\n+analogously to Unified Addresses. For Viewing Keys, a Consumer\n will normally take the union of information provided by all contained\n Receivers, and therefore the Priority List defined in the previous\n section is not used.\n@@ -415,21 +463,21 @@ Unified Address. Additional FVK Types and IVK Types MAY be defined in\n future, and these will not necessarily use the same Typecode as the\n corresponding Unified Address.\n \n-The following FVK or IVK Encodings are used in place of the\n+The following FVK or IVK Items are used in place of the\n :math:`\\mathtt{addr}` field:\n \n-* An Orchard FVK or IVK Encoding, with Typecode :math:`\\mathtt{0x03},` is\n+* An Orchard FVK or IVK Item, with Typecode :math:`\\mathtt{0x03},` is\n   is the raw encoding of the Orchard Full Viewing Key or Orchard Incoming\n   Viewing Key respectively.\n \n-* A Sapling FVK Encoding, with Typecode :math:`\\mathtt{0x02},` is the\n+* A Sapling FVK Item, with Typecode :math:`\\mathtt{0x02},` is the\n   encoding of :math:`(\\mathsf{ak}, \\mathsf{nk}, \\mathsf{ovk}, \\mathsf{dk})`\n   given by :math:`\\mathsf{EncodeExtFVKParts}(\\mathsf{ak}, \\mathsf{nk}, \\mathsf{ovk}, \\mathsf{dk})`,\n   where :math:`\\mathsf{EncodeExtFVKParts}` is defined in [#zip-0032-sapling-helper-functions]_.\n   This SHOULD be derived from the Extended Full Viewing Key at the Account\n   level of the ZIP 32 hierarchy.\n \n-* A Sapling IVK Encoding, also with Typecode :math:`\\mathtt{0x02},`\n+* A Sapling IVK Item, also with Typecode :math:`\\mathtt{0x02},`\n   is an encoding of :math:`(\\mathsf{dk}, \\mathsf{ivk})` given by\n   :math:`\\mathsf{I2LEOSP}_{88}(\\mathsf{dk})\\,||\\,\\mathsf{I2LEOSP}_{256}(\\mathsf{ivk}).`\n \n@@ -440,7 +488,7 @@ The following FVK or IVK Encodings are used in place of the\n   treated as unrecognized by Consumers.\n \n * For Transparent P2PKH Addresses that are derived according to BIP 32\n-  [#bip-0032]_ and BIP 44 [#bip-0044]_, the FVK and IVK Encodings have\n+  [#bip-0032]_ and BIP 44 [#bip-0044]_, the FVK and IVK Items have\n   Typecode :math:`\\mathtt{0x00}.` Both of these are encodings of the\n   chain code and public key :math:`(\\mathsf{c}, \\mathsf{pk})` given by\n   :math:`\\mathsf{c}\\,||\\,\\mathsf{ser_P}(\\mathsf{pk})`. (This is the\n@@ -451,14 +499,6 @@ The following FVK or IVK Encodings are used in place of the\n   external (non-change) child key at the Change level, i.e. at path\n   :math:`m / 44' / coin\\_type' / account' / 0`.\n \n-The Human-Readable Parts (as defined in [#bip-0350]_) of Unified Viewing\n-Keys are defined as follows:\n-\n-* “``uivk``” for Unified Incoming Viewing Keys on Mainnet;\n-* “``uivktest``” for Unified Incoming Viewing Keys on Testnet;\n-* “``uview``” for Unified Full Viewing Keys on Mainnet;\n-* “``uviewtest``” for Unified Full Viewing Keys on Testnet.\n-\n Rationale for address derivation\n ''''''''''''''''''''''''''''''''\n \n@@ -472,6 +512,68 @@ Note that it may be difficult to retain this property for Metadata Items,\n and this should be taken into account in the design of such Items.\n \n \n+String Encodings of Unified Addresses and Unified Viewing Keys\n+--------------------------------------------------------------\n+\n+A Unified String Encoding for a UA/UIVK is obtained by constructing\n+the corresponding Unified Raw Encoding as defined in previous sections,\n+and then converting it to a Unified String Encoding as follows.\n+\n+Let :math:`\\ell^\\mathsf{MAX}_M` be as defined in `Jumbling`_.\n+\n+1. Parse and strip the Binary HRP from the start of the Unified Raw Encoding\n+   :math:`\\mathtt{R}`, as follows:\n+\n+   * If :math:`\\mathsf{length}(\\mathtt{R}) = 0\\!`, reject.\n+\n+   * Let :math:`\\mathtt{hrp\\_len} = \\mathtt{R}[0]\\!`.\n+     If :math:`\\mathtt{hrp\\_len} > 16`\n+     or :math:`\\mathtt{hrp\\_len} > \\mathsf{length}(\\mathtt{R}) - 1`\n+     or :math:`\\mathsf{length}(\\mathtt{R}) - 1 - \\mathtt{hrp\\_len} > \\ell^\\mathsf{MAX}_M - 16\\!`, reject.\n+\n+   * Let :math:`\\mathtt{hrp} = \\mathtt{R}[1\\;..\\!=\\mathtt{hrp\\_len}]\\!`.\n+\n+   * Let :math:`\\mathtt{raw\\_items} = \\mathtt{R}[1 + \\mathtt{hrp\\_len}\\;..\\!=\\mathsf{length}(\\mathtt{R}) - 1]`\n+     (i.e. the remainder of the Unified Raw Encoding after the Binary HRP).\n+\n+2. Let :math:`\\mathtt{padded\\_hrp}` be :math:`\\mathtt{hrp}` padded to\n+   16 bytes with zero bytes.\n+3. Apply the :math:`\\mathsf{F4Jumble}` algorithm described in\n+   `Jumbling`_ to :math:`\\mathtt{raw\\_items} \\,||\\, \\mathtt{padded\\_hrp}\\!`.\n+4. Encode the output with Bech32m [#bip-0350]_, ignoring any length\n+   restrictions.\n+\n+Any equivalent procedure MAY be used, for example, a Producer MAY\n+construct :math:`\\mathtt{padded\\_hrp}` and :math:`\\mathtt{raw\\_items}`\n+directly rather than explicitly obtaining them from a Unified Raw Encoding.\n+\n+The check that :math:`\\mathsf{length}(\\mathtt{R}) - 1 - \\mathtt{hrp\\_len} > \\ell^\\mathsf{MAX}_M - 16`\n+in step 1 ensures that :math:`\\mathtt{raw\\_items} \\,||\\, \\mathtt{padded\\_hrp}`\n+never exceeds the :math:`\\mathsf{F4Jumble}` input size limitation in step 3.\n+\n+Bech32m is chosen over Bech32 in order to better handle variable-length\n+inputs.\n+\n+To decode a Unified String Encoding, a Consumer MUST use the following\n+procedure:\n+\n+1. Decode using Bech32m, rejecting any address with an incorrect checksum.\n+   This yields HRP and data fields. If the HRP is not recognised or not\n+   supported (for example, a Testnet HRP for a Consumer that only supports\n+   Mainnet), reject.\n+2. Let :math:`\\mathtt{expected\\_padded\\_hrp}` be the US-ASCII-encoded HRP,\n+   padded to 16 bytes with zero bytes.\n+3. Apply :math:`\\mathsf{F4Jumble}^{-1}` to the payload (this can also\n+   reject if the input is not in the correct range of lengths).\n+4. If the output ends in :math:`\\mathtt{expected\\_padded\\_hrp}`, remove\n+   these 16 bytes; otherwise reject.\n+5. Parse the result as :math:`\\mathtt{raw\\_items}` described above,\n+   rejecting the entire Unified Address if it does not parse correctly.\n+\n+The Human-Readable Parts of Unified Addresses and Unified Viewing Keys\n+are defined as given in `Encoding Prefixes`_.\n+\n+\n Requirements for both Unified Addresses and Unified Viewing Keys\n ----------------------------------------------------------------\n \n@@ -484,15 +586,15 @@ Requirements for both Unified Addresses and Unified Viewing Keys\n \n * The :math:`\\mathtt{typecode}` and :math:`\\mathtt{length}` fields are\n   encoded as :math:`\\mathtt{compactSize}.` [#Bitcoin-CompactSize]_\n-  (Although existing Receiver Encodings and Viewing Key Encodings are\n+  (Although existing Receiver Items and Viewing Key Items are\n   all less than 256 bytes and so could use a one-byte length field,\n   encodings for experimental types may be longer.)\n \n * Within a single UA or UVK, all HD-derived Receivers, FVKs, and IVKs\n   SHOULD represent an Address or Viewing Key for the same account (as\n   used in the ZIP 32 or BIP 44 Account level).\n \n-* For Transparent Addresses, the Receiver Encoding does not include\n+* For Transparent Addresses, the Receiver Item does not include\n   the first two bytes of a raw encoding.\n \n * There is intentionally no Typecode defined for a Sprout Shielded\n@@ -510,9 +612,9 @@ Requirements for both Unified Addresses and Unified Viewing Keys\n   Address.\n \n * Consumers MUST reject Unified Addresses/Viewing Keys in which *any*\n-  constituent Item does not meet the validation requirements of its\n-  encoding, as specified in this ZIP and the Zcash Protocol Specification\n-  [#protocol-nu5]_.\n+  constituent Item that the Consumer recognizes does not meet the\n+  validation requirements of its encoding, as specified in this ZIP\n+  and the Zcash Protocol Specification [#protocol-nu5]_.\n \n * Consumers MUST reject Unified Addresses/Viewing Keys in which the\n   constituent Items are not ordered in ascending Typecode order. Note\n@@ -617,7 +719,7 @@ sections of ZIP 32.\n To satisfy the above properties for transparent (P2PKH) keys, we derive\n the external and internal :math:`\\mathsf{ovk}` components from the\n transparent FVK :math:`(\\mathsf{c}, \\mathsf{pk})` (described in\n-`Encoding of Unified Full/Incoming Viewing Keys`_) as follows:\n+`Raw Encoding of Unified Full/Incoming Viewing Keys`_) as follows:\n \n - Let :math:`I_\\mathsf{ovk} = \\mathsf{PRF^{expand}}_{\\mathsf{LEOS2BSP}_{256}(\\mathsf{c})}\\big([\\mathtt{0xd0}] \\,||\\, \\mathsf{ser_P}(\\mathsf{pk})\\big)`\n   where :math:`\\mathsf{ser_P}(pk)` is :math:`33` bytes, as specified in [#bip-0032-serialization-format]_.\n@@ -735,7 +837,7 @@ undetermined.\n \n The Sender also determines a \"preferred sending protocol\" —one of\n \"transparent\", \"Sapling\", or \"Orchard\"— corresponding to the\n-most preferred Receiver Type (as given in `Encoding of Unified Addresses`_)\n+most preferred Receiver Type (as given in `Raw Encoding of Unified Addresses`_)\n of any funds sent in the transaction.\n \n If the sending Account has been determined, then the Sender\n@@ -982,6 +1084,41 @@ Related work\n similarly structured 4-round unbalanced Feistel cipher.\n \n \n+QR Encodings of Unified Addresses and Unified Viewing Keys\n+----------------------------------------------------------\n+\n+The Unified QR Encoding of a UA or UVK is as specified in\n+[#protocol-addressandkeyencoding]_; that is, the QR code representation\n+of its Unified String Encoding converted to uppercase, using the\n+Alphanumeric mode specified in sections 7.3.4 and 7.4.4 of [#iso-qr-codes]_.\n+\n+A Consumer MAY support parsing multiple kinds of address and/or viewing\n+key from a QR code, in which case it SHOULD use the HRP to provisionally\n+recognize (subject to further validation) potential address or viewing key\n+types.\n+\n+When a Consumer recognizes the content of a QR code as a potential\n+Unified String Encoding, it SHOULD NOT present the resulting content to\n+the user as representing an address, without first checking that it is\n+a valid Unified String Encoding by attempting to decode it.\n+\n+A Producer MUST NOT generate a QR Encoding directly from a\n+Unified Raw Encoding (using the QR \"Byte mode\" defined in [#iso-qr-codes]_\n+section 7.3.5, or otherwise).\n+\n+Rationale for not using Byte mode\n+'''''''''''''''''''''''''''''''''\n+\n+A QR code using the Unified Raw Encoding in Byte mode could be slightly\n+smaller, however we believe this choice would be likely to cause\n+interoperability problems. It also would not satisfy the requirement\n+that the Unified Raw Encoding not be shown to users, since it is common\n+for software that supports QR codes to try to decode the content and\n+present it as a string. (Clause 6.1 b) 3) of [#iso-qr-codes]_ specifies\n+that the default interpretation of byte data is as an ISO-8859-1 text\n+string.)\n+\n+\n Reference implementation\n ========================\n \n@@ -1037,3 +1174,4 @@ References\n .. [#P2PKH] `Transactions: P2PKH Script Validation — Bitcoin Developer Guide <https://developer.bitcoin.org/devguide/transactions.html#p2pkh-script-validation>`_\n .. [#P2SH] `Transactions: P2SH Scripts — Bitcoin Developer Guide <https://developer.bitcoin.org/devguide/transactions.html#pay-to-script-hash-p2sh>`_\n .. [#Bitcoin-CompactSize] `Variable length integer. Bitcoin Wiki <https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer>`_\n+.. [#iso-qr-codes] `ISO/IEC. International Standard ISO/IEC 18004:2015(E): Information Technology – Automatic identification and data capture techniques – QR Code bar code symbology specification. Third edition. February 1, 2015. <https://raw.githubusercontent.com/yansikeim/QR-Code/master/ISO%20IEC%2018004%202015%20Standard.pdf>`_"}],"filesTruncated":false,"preview":{"filename":"zip-0316.rst","title":"Unified Addresses and Unified Viewing Keys","status":"Final","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":2,"id":"overview","text":"Overview"},{"level":2,"id":"concepts","text":"Concepts"},{"level":2,"id":"addresses","text":"Addresses"},{"level":2,"id":"receivers","text":"Receivers"},{"level":2,"id":"transport-encodings","text":"Transport Encodings"},{"level":2,"id":"transfers","text":"Transfers"},{"level":2,"id":"experimental-usage","text":"Experimental Usage"},{"level":2,"id":"viewing-keys","text":"Viewing Keys"},{"level":2,"id":"open-issues-and-known-concerns","text":"Open Issues and Known Concerns"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"encoding-prefixes","text":"Encoding Prefixes"},{"level":2,"id":"raw-encoding-of-unified-addresses","text":"Raw Encoding of Unified Addresses"},{"level":2,"id":"raw-encoding-of-unified-full-incoming-viewing-keys","text":"Raw Encoding of Unified Full/Incoming Viewing Keys"},{"level":3,"id":"rationale-for-address-derivation","text":"Rationale for address derivation"},{"level":2,"id":"string-encodings-of-unified-addresses-and-unified-viewing-keys","text":"String Encodings of Unified Addresses and Unified Viewing Keys"},{"level":2,"id":"requirements-for-both-unified-addresses-and-unified-viewing-keys","text":"Requirements for both Unified Addresses and Unified Viewing Keys"},{"level":3,"id":"rationale-for-item-ordering","text":"Rationale for Item ordering"},{"level":3,"id":"rationale-for-showing-at-least-the-first-20-characters","text":"Rationale for showing at least the first 20 characters"},{"level":2,"id":"adding-new-types","text":"Adding new types"},{"level":2,"id":"metadata-items","text":"Metadata Items"},{"level":2,"id":"deriving-internal-keys","text":"Deriving Internal Keys"},{"level":2,"id":"deriving-a-uivk-from-a-ufvk","text":"Deriving a UIVK from a UFVK"},{"level":2,"id":"deriving-a-unified-address-from-a-uivk","text":"Deriving a Unified Address from a UIVK"},{"level":2,"id":"usage-of-outgoing-viewing-keys","text":"Usage of Outgoing Viewing Keys"},{"level":2,"id":"jumbling","text":"Jumbling"},{"level":3,"id":"discussion","text":"Discussion"},{"level":3,"id":"solution","text":"Solution"},{"level":3,"id":"usage-for-unified-addresses-ufvks-and-uivks","text":"Usage for Unified Addresses, UFVKs, and UIVKs"},{"level":3,"id":"heuristic-analysis","text":"Heuristic analysis"},{"level":3,"id":"efficiency","text":"Efficiency"},{"level":3,"id":"dependencies","text":"Dependencies"},{"level":3,"id":"related-work","text":"Related work"},{"level":2,"id":"qr-encodings-of-unified-addresses-and-unified-viewing-keys","text":"QR Encodings of Unified Addresses and Unified Viewing Keys"},{"level":3,"id":"rationale-for-not-using-byte-mode","text":"Rationale for not using Byte mode"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":216,"title":"[WIP] [TZE] [ZIP 309] Add zkChannels (or BOLT) support","author":"jakinyele","createdAt":"2019-03-30T06:41:12Z","updatedAt":"2026-05-19T20:50:56Z","url":"https://github.com/zcash/zips/pull/216","diffUrl":"https://github.com/zcash/zips/pull/216.diff","headRef":"master","headRepo":"boltlabs-inc/zips","headSha":"054d76f7fee314b291209f1ce93f27559d354b94","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Describes three potential approaches for integrating Bolt with Zcash","zipRefs":[309],"draftRefs":[],"mapped":true,"claimed":true,"files":[{"filename":"zip-0309.rst","status":"added","additions":858,"deletions":0,"patch":"@@ -0,0 +1,858 @@\n+::\n+\n+  ZIP: 309\n+  Title: Add support for zk-Channels protocol\n+  Authors: J. Ayo Akinyele <ayo@boltlabs.io>\n+           Colleen Swanson <swan@boltlabs.io>\n+           Raphael Toledo <contact@raphael-toledo.com>\n+  Credits: Ian Miers <imiers@z.cash>\n+           Matthew Green <mgreen@z.cash>\n+  Category: Informational\n+  Created: 2019-07-15\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\" and \"MUST NOT\" in this document are to be interpreted as described in RFC 2119. [#RFC2119]_\n+\n+Abstract\n+========\n+\n+This proposal specifies three possible approaches for integrating the zk-Channels protocol [#zk-Channels-paper]_ into Zcash.\n+\n+Motivation\n+==========\n+\n+Layer 2 protocols like Lightning enable scalable payments for Bitcoin but lack the mechanisms to provide strong privacy guarantees on the payment network. Zcash offers private transactions but currently lacks features that would enable a Lightning-style payment channel. This proposal specifies an integration of the zkChannel privacy-preserving payment channel protocol on top of Zcash [#zk-Channels-paper]_.\n+\n+zk-Channels high level Overview\n+===============================\n+\n+zk-Channels enables two parties, a customer and a merchant, to open an assymetric anonymous payment channel where only the customer may initiate channel opening and payments. While the channel opening and closure are broadcast on-chain, making the channel's participants at most pseudonymous, the payments are done directly between the customer and merchant offchain in an anonymous and unlinkable fashion.\n+\n+The channel opening can be split in three phases: initialisation, activation and unlink:\n+\n+1. in the initialisation phase, the customer and merchant set up the channel by agreeing on initial balances and auxiliary information.\n+2. in the activation phase, the merchant ensures that the channel has been properly funded and sends to the client a payment token to allow the customer to transfer funds.\n+3. the unlink phase goal is to unlink the channel opening with futher payments by performing a first unlinkable payment with null amount.\n+\n+The off-chain payments is also split in three phases: setup, update state, and unmask.\n+\n+1. in the setup phase, the customer generates a new channel state privately and sends to the customer a payment request. The merchant checks checks this is not a double spend, and if so either accept or reject the new payment.\n+2. in the update state phase, the customer and merchant participate in a zk-protocol where the merchant agrees on the new payment, by signing blindy on the new channel state, while revoking the current state.\n+3. in the unmask phase, the customer and merchant gets the payment and revocation token to close the zk-channel onchain.\n+\n+The channel can be closed in three different ways:\n+\n+- unilaterally by the merchant. In that case, the merchant uses the closing information exchanged in the channel initialisation to broadcast an expiry transaction onchain; the total amount is time-locked.\n+\n+  a. during the time lock, the customer can broadcast a customer close which nullify the expiry transaction;\n+  b. after the time lock, the merchant can claim the funds.\n+\n+- unilaterally by the customer. In that case, the customer uses the close authorization message received in a update state phase to broadcase a customer close transaction onchain; the merchant balance is sent directly to the merchant while the customer balance is time-locked.\n+\n+  a. during the time lock, the customer balance can be disputed by the merchant, in case the customer closed on an outdated state, by revealing the revocation secret. In that case, all funds go to the merchant;\n+  b. after the time lock, the customer can claim the funds.\n+\n+- mutually. In that case, the customer and merchant exchange information to generate a multisig transaction closing the channel.\n+\n+1. Requirements\n+---------------\n+\n+1.1 General requirements for zk-Channels protocol\n+-------------------------------------------------\n+\n+zk-Channels private payment channels require the following capabilities to provide anonymity properties for users on a payment network:\n+\n+1. Ability to create an escrow transaction such that the transaction inputs are anonymous.\n+2. Ability to escrow funds to a multi-signature style address via non-malleable transactions.\n+3. Ability to specify relative time locks for commitment transactions to support unilateral channel closing.\n+4. Ability to validate zk-Channels-specific opening and closing transactions:\n+\n+  1. check the validity of randomized/blinded signature on the channel state in closing token;\n+\n+  2. check the validity of revocation lock in the event of a channel dispute by merchant.\n+\n+5. Ability to enforce a transaction flow thanks to Transparent ZCash Extensions [#zip-0222]\n+6. Ability to verify transaction outputs using TZEs such that:\n+\n+  1. if customer-initiated closing, one output pays out to customer with a time lock (to allow merchant to dispute customer balance) and one output pays out to merchant immediately;\n+\n+  2. if merchant-initiated closing, a single output pays the merchant the full balance of the channel with a time lock that allows for customer dispute.\n+\n+**Channel Operation Assumptions.**\n+  - Channels funded by the customer alone and dual-funded channels are both supported.\n+  - Either the customer or the merchant can initiate channel closing.\n+  - If the customer initiates closing, then the merchant can dispute the closing transaction if they disagrees with the closing token in the closing transaction.\n+  - If the merchant initiates closing, the merchant posts a transaction claiming all the funds in the channel for themselves with a timelock. This gives the customer the opportunity to post their own valid closing transaction with the current channel balances. If the customer posts their own closing transaction, the merchant has an additional opportunity to dispute if necessary.\n+\n+1.2 TZEs requirement for zk-Channels protocol\n+---------------------------------------------\n+\n+1. Ability to check the sender of the current transaction;\n+2. Ability to check the owner of an address;\n+3. Ability to check the amount of an address;\n+4. Ability to check the current block height;\n+5. Ability to check information about a transaction of an input TZE address;\n+6. Ability to verify Pointcheval-Sanders signatures on specific messages;\n+7. Ability to assert a hash lock result;\n+\n+2 Algorithms and variables\n+----------------------------\n+\n+2.1 Customer and Merchant Signing Keys\n+--------------------------------------\n+\n+The customer and the merchant both have key pairs from a suitable signature scheme to send and receive transactions. e.g. ECDSA or RedDSA signature key pairs. These are denoted as: ``<cust-pk>``, ``<cust-sk>`` and ``<merch-pk>``, ``<merch-sk>``, respectively, where ``pk`` stands for \"public key\" and ``sk`` stands for the corresponding \"secret key\".\n+\n+The merchant must be able to issue blind signatures, for instance Pointcheval-Sanders (PS) blind signature, so they have an additional keypair; this keypair is denoted as:\n+``<merch-ps-pk>``, ``<merch-ps-sk>``. To verify a PS signature, we will use the following function: ``PS_sig.Verify(merch-ps-pk, message, signature)``.\n+\n+The customer key pair is specific to the channel and must not be reused. The merchant key pair is long term and should be used for all customer channels. \n+\n+2.2 Channel States\n+------------------\n+\n+zk-Channels relies on the concept of *states*. When the customer initiates a payment, they query the merchant to accept a new state while revoking the current one.\n+\n+The only zk-channel's invariants are the channel identifier ``<cid>``, and its participants' public keys. A channel state consists of:\n+\n+- the channel id: ``<cid>``;\n+- the merchant's public keys: ``<merch-pk>, <merch-ps-pk>`` (defined in 1.1);\n+- the customer's public keys: ``<cust-pk>`` (defined in 1.1);\n+- the customer's balance: ``<cust-balance>``;\n+- the merchant's balance: ``<merch-balance>``;\n+- the revocation lock: ``<rev-lock>`` which is instantiated as a hash lock, ``<rev-lock> = SHA256(<rev-secret>)`` where ``<rev-secret>`` is the revocation secret.\n+\n+The revocation lock is needed by the merchant to dispute a customer close on an outdated state.\n+\n+\n+3 Opening a channel\n+-------------------\n+\n+To open a channel, the customer and merchant exchange key information and set the channel identifier ``<cid>``. \n+\n+They agree on their respective initial balances ``<cust-balance>`` and ``<merch-balance>``.\n+\n+The customer picks an inital revocation lock ``<rev-lock>``.\n+\n+The customer and merchant escrow the necessary funds in a funding transaction, denoted ``escrow-tx``.\n+\n+As such, the escrow transaction presents:\n+\n+- an opening channel state ``<state> = <cid>, <merch-pk>, <merch-ps-pk>, <cust-pk>, <merch-balance>, <cust-balance>, <rev-lock>``;\n+- potentially additional information such as a delay <delay>.\n+\n+4 Closing a channel\n+---------------------\n+\n+A customer should be able to unilaterally close the channel by posting a ``close-tx`` transaction. This transaction presents:\n+\n+- a closing channel state ``<state> = <cid>, <merch-pk>, <merch-ps-pk>, <cust-pk>, <cust-balance>, <merch-balance>, <rev-lock>``;\n+- a unblinded signature from the merchant on the channel state: ``<merch-sig>`` such that ``PS_sig.Verify(<merch-ps-pk>, <state>, <merch-sig>) = 1``.\n+\n+If the transaction is validated and mined, <merch-balance> is sent to the merchant and <cust-balance> is locked for a delay <delay> during which the merchant can dispute the channel closing thanks to a ``dispute-tx`` transaction, and after which the customer can claim it thanks to a ``cust-claim-tx`` transaction.\n+\n+\n+\n+The merchant can dispute a customer close by posting a ``dispute-tx`` transaction. This transaction presents:\n+\n+- a revocation secret ``<rev-secret>`` such that for the revocation lock ``<rev-lock>`` posted in ``close-tx`` we have ``<rev-lock> = SHA256(<rev-secret>)``.\n+\n+If the transaction is validated and mined, the total balance (<cust-balance> + <merch-balance>) is sent to the merchant.\n+\n+\n+\n+A merchant should be able to unilaterally close the channel by posting a closing transaction ``expiry-tx``.\n+\n+If the transaction is validated and mined, the total balance is locked for a delay <delay> during which the customer can overwrite the spend with a ``close-tx`` transaction, and after which the merchant can claim it thanks to a ``merch-claim-tx`` transaction.\n+\n+\n+\n+The customer and merchant may also negotiate off-chain to form a *mutual close transaction*, ``mutual-close-tx``. This transaction presents:\n+\n+- a truncated closing channel state ``<state>`` without the revocation lock and the merchant's blind signature public key;\n+- two signatures from the customer ``<cust-sig>`` and the merchant ``<merch-sig>`` on this truncated state such that ``Verify(<cust-pk>, <state>, <cust-sig>) = 1`` and ``Verify(<merch-ps-pk>, <state>, <merch-sig>) = 1``.\n+\n+If the transaction is validated and mined, the balance <cust-balance> is sent to the customer and the balance <merch-balance> is sent to the merchant.\n+\n+\n+zk-Channels TZE transactions\n+===============================\n+We present in this section the individual transactions for opening and closing a zk-Channel as well as disputing and claiming funds of a zkChannel.\n+\n+For simplicity, we describe the minimal information needed to understand the workflow, that is we show only the relevant part of the verify_tze functions and do not detail more than necessary the transaction structure.\n+\n+1 Escrow transaction\n+--------------------\n+We present here the escrow transaction ``escrow-tx``:\n+\n+\n+============= ==============================\n+Field         Value\n+============= ==============================\n+...           ...\n+tx_in_count   2\n+tx_in         [<cust-input>, <merch-input>]\n+tx_out_count  0\n+tx_out        []\n+tze_in_count  0\n+tze_in        []\n+tze_out_count 1\n+tze_out       [<escrow-out>]\n+lock_time     0\n+...           ...\n+============================================\n+\n+With,\n+ \n+- <cust-input> being a transparent address comprising an initial amount of <cust-balance>.\n+- <merch-input> being a transparent address comprising an initial amount of <merch-balance>.\n+- with <escrow-out> being a tze input:\n+\n+  - amount = <merch-balance> + <cust-balance>\n+  - precondition =\n+\n+    - tze_id = 0 // *zk-Channels TZE type*\n+    - tze_mode = 0 // *escrow mode*\n+    - tze_data_payload_len = \n+    - tze_data_payload = <cid> || <merch-pk> || <merch-ps-pk> || <cust-pk>\n+\n+\n+An escrow transaction can either be spent by a mutual close, merchant expiry or customer close transaction.\n+\n+2 Mutual close\n+--------------\n+We present here the mutual close transaction ``mutual-close-tx``:\n+\n+============= ===============================\n+Field         Value\n+============= ===============================\n+...           ...\n+tx_in_count   0\n+tx_in         []\n+tx_out_count  2\n+tx_out        [<merch-output>, <cust-output>]\n+tze_in_count  1\n+tze_in        [<escrow-in>]\n+tze_out_count 0\n+tze_out       []\n+lock_time     0 \n+...           ...\n+=============================================\n+\n+\n+With,\n+  - <escrow-in>:\n+\n+    - prevout_hash = <escrow-tx>\n+    - prevout_in = 0\n+    - witness :\n+\n+      - tze_id = 0 // *zkChannels TZE type*\n+      - tze_mode = 0 // *escrow mode>*\n+      - tze_data_payload_len = \n+      - tze_data_payload = <merch-sig> || <cust-sig>\n+\n+  - <merch-output> being a transparent address comprising an amount of <merch-balance>;\n+  - <cust-output> being a transparent address comprising an amount of <cust-balance>.\n+\n+The tze_verify function works as follow:\n+\n+**tze_verify(mode, precondition, witness):**\n+\n+- ...\n+- if mode == 0 AND context.tx.tx_out_counts == 2:\n+\n+  - // *Check the transaction's structure*\n+  - if context.tx.tx_in_count != 0 OR context.tx.tze_in_count != 1 OR context.tx.tze_out_count != 0 OR context.tx.nShieldedSpend !=0 OR context.tx.nShieldedOutput != 0 OR context.tx.nJoinsplit != 0:\n+  \n+    - Return false\n+  \n+  \n+  - // *Define the variables*\n+  - cid || merch-pk || merch-ps-pk || cust-pk = precondition\n+  - merch-sig || cust-sig = witness \n+  - msg = TODO // *hash of tx without witnesses*\n+\n+\n+  - // *Check the tx signatures*\n+\n+  - if Verify(merch-pk, msg, merch-sig) != 1\n+\n+    - Return False\n+  \n+  - if Verify(cust-pk, msg, cust-sig) != 1\n+\n+    - Return False\n+\n+  - Return True\n+- ...\n+\n+\n+\n+3 Expiry\n+--------\n+\n+3.1 Expiry\n+------------------\n+\n+We present here the unilateral merchant close transaction, also called expiry, ``expiry-tx``:\n+\n+============= ===============================\n+Field         Value\n+============= ===============================\n+...           ...\n+tx_in_count   0\n+tx_in         []\n+tx_out_count  0\n+tx_out        []\n+tze_in_count  1\n+tze_in        [<escrow-in>]\n+tze_out_count 1\n+tze_out       [<expiry-out>]\n+lock_time     0 \n+...           ...\n+=============================================\n+\n+With,\n+\n+- <escrow-in>:\n+\n+  - prevout_hash = <escrow-tx>\n+  - prevout_in = 0\n+\n+  - witness:\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 0 // *escrow mode*\n+    - tze_data_payload_len =\n+    - tze_data_payload = <merch-sig>\n+\n+- <expiry-out>:\n+\n+  - amount = <merch-balance> + <cust-balance>\n+  - precondition:\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 2 // *expiry mode*\n+    - tze_data_payload_len =\n+    - tze_data_payload = <cid> || <merch-pk> || <merch-ps-pk> || <cust-pk>\n+\n+The tze_verify functions works as follows:\n+\n+**tze_verify(mode, precondition, witness):**\n+\n+- ...\n+- if mode == 0 AND context.tx.tze_out_count == 1 AND context.tx.tx_out[0].precondition.mode == EXPIRY:\n+\n+  - // *Check the transaction's structure*\n+  -  if context.tx.tx_in_count != 0 OR context.tx.tx_out_count != 0 OR context.tx.tze_in_count != 1  OR context.tx.nShieldedSpend !=0 OR context.tx.nShildedOutput != 0 OR context.tx.nJoinsplit != 0:\n+    - Return false\n+\n+  - // *Define the variables*\n+  - init-cid || init-merch-pk || init-merch-ps-pk || init-cust-pk = precondition\n+  - merch-sig = witness\n+  - cid || merch-pk || merch-ps-pk || cust-pk = context.tx.tze_out[0].precondition.tze_data_payload\n+  - msg = TODO // *hash of tx without witnesses*\n+\n+  - // *Check the variables consistency* \n+  - if init-cid != cid:\n+\n+    - Return False\n+\n+  - if init-merch-pk != merch-pk:\n+\n+    - Return False\n+\n+  - if init-merch-ps-pk != merch-ps-pk:\n+\n+    - Return False\n+\n+  - if init-cust-pk != cust-pk:\n+\n+    - Return False\n+  \n+  - // *Verify the tx signature* \n+  - if Verify(merch-pk, msg, merch-sig) = 0\n+\n+    - Return False\n+\n+  - Return True\n+\n+- ...\n+\n+3.2 Merchant claim\n+------------------\n+\n+We present here the merchant claim transaction ``merch-claim-tx`` following a ``expiry-tx`` transaction.\n+\n+============= ===============================\n+Field         Value\n+============= ===============================\n+...           ...\n+tx_in_count   0\n+tx_in         []\n+tx_out_count  1\n+tx_out        [<merch-claim]\n+tze_in_count  1\n+tze_in        [<expiry-in>]\n+tze_out_count 0\n+tze_out       []\n+lock_time     0\n+...           ...\n+=============================================\n+\n+With,\n+\n+- <expiry-in>:\n+\n+  - prevout_hash = <expiry-tx>\n+  - prevout_in = 0\n+  - witness:\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 2 // *expiry mode*\n+    - tze_data_payload_len =\n+    - tze_data_payload =  <merch-sig>\n+\n+  - <merch-claim> being a transparent address comprising an amount of <cust-balance> + <merch-balance>.\n+\n+\n+The tze_verify function works as follow:\n+\n+**tze_verify(type, mode, precondition, witness)**\n+\n+- ...\n+- if mode == 2:\n+\n+  - // *Check the transaction's structure*\n+  - if context.tx.tx_in_count != 0 OR context.tx.tx_in_count != 1 OR context.tx.tze_in_count != 1 OR context.tx.tze_out_count != 0 OR context.tx.nShieldedSpend !=0 OR context.tx.nShieldedOutput != 0 OR context.tx.nJoinsplit != 0:\n+    - Return false\n+\n+  - // *Define the variables*\n+  - cid || merch-pk || merch-ps-pk || cust-pk = precondition\n+  - sig = witness\n+  - msg = TODO // *hash of the tx without witnesses*\n+\n+  - // *Verify the tx signature*\n+  - if Verify(merch-pk, msg, sig) == 1:\n+\n+    - // *Check the delay*\n+    - expiry-height = find_block_height(context.tx.tze_input[0].prevout_hash) // TODO\n+    - if context.block-height < expiry-height + DELAY: \n+\n+      - Return False\n+\n+    - Return True\n+\n+- ...\n+\n+4 Customer close\n+----------------\n+\n+4.1 Customer close\n+------------------\n+\n+We present here the customer close transaction ``close-tx`` following either a ``expiry-tx`` or a ``escrow-tx`` transaction.\n+\n+\n+============= ===============================\n+Field         Value\n+============= ===============================\n+...           ...\n+tx_in_count   0\n+tx_in         []\n+tx_out_count  1\n+tx_out        [<merch-output]\n+tze_in_count  1\n+tze_in        [<expiry-in> OR <escrow-in>]\n+tze_out_count 1\n+tze_out       [<close-out]\n+lock_time     0\n+...           ...\n+=============================================\n+\n+With,\n+\n+- <escrow-in>:\n+\n+  - prevout_hash = <escrow-tx>\n+  - prevout_in = 0 \n+  - witness:\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 0 // *escrow mode*\n+    - tze_data_payload_len = \n+    - tze_data_payload =  <merch-ps-sig> || <cust-sig>\n+\n+- <expiry-in>:\n+\n+  - prevout_hash = <escrow-tx>\n+  - prevout_in = 0 \n+  - witness :\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 2 // *expiry mode*\n+    - tze_data_payload_len =\n+    - tze_data_payload = <merch-ps-sig> || <cust-sig>\n+\n+- <merch-output> being a transaprent address comprising an amount of <merch-balance>;\n+- <close-out>:\n+\n+  - amount = <cust-balance>\n+  - precondition:\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 1 // *close mode* \n+    - tze_data_payload_len =  \n+    - tze_data_payload = <cid> || <merch-pk> || <cust-pk> || <rev-lock>\n+\n+\n+The tze_verify function works as follow:\n+\n+**tze_verify(mode, precondition, witness)**\n+\n+- ...\n+- if (mode == 0 OR mode == 2) AND context.tx.tze_out_count == 1 AND context.tx.tze_out[0].precondition.mode = CLOSE:\n+\n+  - // *Check the transaction's structure*\n+  - if context.tx.tx_in_count != 0 OR context.tx.tx_out_count != 1 OR context.tx.tze_in_count != 1 OR context.tx.nShieldedSpend !=0 OR context.tx.nShildedOutput != 0 OR context.tx.nJoinsplit != 0:\n+    - Return false\n+\n+  - // *Define the variables*\n+  - init-cid || init-merch-pk || init-merch-ps-pk || init-cust-pk = precondition\n+  - merch-ps-sig || cust-sig = witness\n+  - cid || merch-pk || cust-pk || rev-lock = context.tx.tze_out[0].precondition.tze_data_payload\n+  - merch-balance = context.tx.tx_out[0].value\n+  - cust-balance = context.tx.tze_out[0].amount\n+  - state = cid || cust-pk || rev-lock || cust-balance || merch-balance\n+  - msg = TODO // *hash of tx without witnesses*\n+\n+  - // *Check the variables' consistency* \n+  - if init-cid != cid:\n+\n+    - Return False\n+\n+  - if init-cust-pk != cust-pk:\n+\n+    - Return False\n+\n+  - if init-merch-pk != merch-pk:\n+\n+    - Return False\n+\n+\n+  - // *Verify the PS signature*\n+  - if PS_sig.Verify(merch-ps-pk, state, merch-ps-sig) != 1:\n+\n+    - Return False\n+\n+  - // *Verify the tx signature*\n+\n+  - if Verify(cust-pk, msg, cust-sig) != 1:\n+\n+    - Return False\n+\n+  - // *Check merch-balance is sent to the merchant*\n+  - if context.tx.tx_out[0] != merch-balance || <ZCash P2PKH version bytes (0x1C || 0xB8 for mainnet)> || Hash_160(merch-pk)\n+\n+    - Return False\n+\n+  - Return True\n+- ...\n+\n+4.2 Customer claim\n+------------------\n+\n+We present here the customer claim transaction ``cust-claim-tx`` following a ``close-tx`` transaction.\n+\n+============= ===============================\n+Field         Value\n+============= ===============================\n+...           ...\n+tx_in_count   0\n+tx_in         []\n+tx_out_count  1\n+tx_out        [<cust-claim]\n+tze_in_count  1\n+tze_in        [<close-in>]\n+tze_out_count 0\n+tze_out       []\n+lock_time     0\n+...           ...\n+=============================================\n+\n+With,\n+\n+- <close-in>:\n+\n+  - prevout_hash = <close-tx>\n+  - prevout_in = 0\n+  - witness:\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 1 // *close mode*\n+    - tze_data_payload_len =\n+    - tze_data_payload =  <cust-sig>\n+\n+  - <cust-claim> being a transparent address comprising an amount of <cust-balance>.\n+\n+\n+The tze_verify function works as follow:\n+\n+**tze_verify(type, mode, precondition, witness)**\n+\n+- ...\n+- if mode == 1:\n+\n+  - // *Check the transaction's structure*\n+  - if context.tx.tx_in_count != 0 OR context.tx.tx_out_count == 1 OR context.tx.tze_in_count != 1 OR context.tx.tze_out_count != 0 OR context.tx.nShieldedSpend !=0 OR context.tx.nShieldedOutput != 0 OR context.tx.nJoinsplit != 0:\n+\n+    - Return false\n+\n+  - // *Define the variables*\n+  - cid, merch-pk, cust-pk, rev-lock = precondition\n+  - sig = witness\n+  - msg = TODO // *hash of tx without witnesses*\n+\n+  - // *Check the tx signature*\n+  - if Verify(cust-pk, msg, sig) == 1:\n+\n+\n+    - // *Check the delay*\n+    - close-height = find_block_height(context.tx.tze_input[0].prevout_hash) // TODO\n+    - if context.block-height < close-height + DELAY:\n+\n+      - Return False\n+\n+    - Return True\n+\n+- ...\n+\n+4.3 Dispute\n+--------------------\n+\n+We present here how the merchant dispute the transaction ``close-tx`` with a ``dispute-tx`` transaction.\n+\n+============= ===============================\n+Field         Value\n+============= ===============================\n+...           ...\n+tx_in_count   0\n+tx_in         []\n+tx_out_count  1\n+tx_out        [<merch-disp]\n+tze_in_count  1\n+tze_in        [<close-in>]\n+tze_out_count 0\n+tze_out       []\n+lock_time     0\n+...           ...\n+=============================================\n+\n+With,\n+\n+- <close-in>:\n+\n+  - prevout_hash = <close-tx>\n+  - prevout_in = 0\n+  - witness:\n+\n+    - tze_id = 0 // *zkChannels' TZE type*\n+    - tze_mode = 1 // *close mode*\n+    - tze_data_payload_len =\n+    - tze_data_payload =  <merch-sig> || <rev-secret>\n+\n+  - <merch-disp> being a transparent address comprising an amount of <cust-balance>.\n+\n+\n+The tze_function works as follow:\n+\n+**tze_verify(zkChannels-type, close-mode, precondition, witness)**\n+\n+- ...\n+- if mode == 1:\n+\n+  - // *Check the transaction's structure*\n+  - if context.tx.tx_in_count != 0 OR context.tx.tx_out_count == 1 OR context.tx.tze_in_count != 1 OR context.tx.tze_out_count != 0 OR context.tx.nShieldedSpend !=0 OR context.tx.nShieldedOutput != 0 OR context.tx.nJoinsplit != 0:\n+\n+    - Return false\n+\n+  - // *Define the variables*\n+  - cid, merch-pk, cust-pk, rev-lock = precondition\n+  - sig = witness\n+  - msg = TODO // hash of tx without witnesses\n+\n+  - // *Check the tx signature*\n+  - if Verify(merch-pk, msg, sig) == 1:\n+\n+    - // *Check the hash lock*\n+    - if rev-lock != SHA256(rev-secret):\n+\n+      - Return False\n+\n+    - Return True\n+\n+- ...\n+\n+\n+TZE_VERIFY function\n+===================\n+\n+\n+**tze_verify(mode, precondition, witness):**\n+\n+- // *Check the transaction structure*\n+- if context.tx.tx_in_count != 0 OR context.tx.tze_in_count != 1 OR context.tx.nShieldedSpend !=0 OR context.tx.nShieldedOutput != 0 OR context.tx.nJoinsplit != 0:\n+\n+  - Return False\n+\n+- msg = TODO // hash of tx without witnesses\n+\n+- if mode == 0: // *escrow mode*\n+\n+  - init-cid || init-merch-pk || init-merch-ps-pk || init-cust-pk = precondition\n+\n+  - if context.tx.tze_out_count == 1:\n+\n+    - if context.tx.tze_out[0].precondition.tze_mode == 1: // *This is a close transaction*\n+\n+      - // *Check the transaction's structure*\n+      - if context.tx.tx_out_count != 1:\n+    \n+        - Return False\n+\n+      - merch-ps-sig || cust-sig = witness\n+      - cid || merch-pk || cust-pk || rev-lock = context.tx.tze_out[0].precondition.tze_data_payload\n+      - merch-balance = context.tx.tx_out[0].value\n+      - cust-balance = context.tx.tze_out[0].amount\n+      - state = cid || cust-pk || rev-lock || cust-balance || merch-balance\n+\n+      - if init-cid != cid OR init-merch-pk != merch-pk OR init-cust-pk != cust-pk:\n+\n+        - Return False\n+\n+      - if Verify(cust-pk, msg, cust-sig) != 1 OR PS_sig.Verify(merch-ps-pk, state, merch-ps-sig) != 1:\n+\n+        - Return False\n+\n+      - if context.tx.tx_out[0] == merch-balance || <ZCash P2PKH version bytes (0x1C || 0xB8 for mainnet)> || Hash_160(merch-pk)\n+\n+        - Return True\n+\n+    - if context.tx.tze_out[0].precondition.tze_mode == 2: // *This is an expiry transaction*\n+\n+      - // *Check the transaction's structure*\n+      - if context.tx.tx_out_count != 0:\n+    \n+        - Return False\n+\n+      - merch-sig = witness\n+      - cid || merch-pk || merch-ps-pk || cust-pk = context.tx.tze_out[0].precondition.tze_data_payload\n+\n+      - if init-cid != cid OR init-merch-pk != merch-pk OR init-merch-ps-pk != merch-ps-pk OR init-cust-pk != cust-pk:\n+\n+        - Return False\n+\n+      - if Verify(merch-pk, msg, merch-sig) == 1:\n+\n+        - Return True\n+\n+  - if context.tx.tze_out_count == 0 AND if context.tx.tx_out_count == 2: // *This is a mutual close transaction*\n+\n+    - merch-sig || cust-sig = witness\n+\n+    - if Verify(init-merch-pk, msg, merch-sig) == 1 AND Verify(init-cust-pk, msg, cust-sig) == 1:\n+    \n+      - Return True\n+\n+\n+- else if mode == 1: // *close mode*\n+\n+  - cid, merch-pk, cust-pk, rev-lock = precondition\n+  - sig, (rev-secret) = witness\n+\n+  - if context.tx.tx_out.count == 1:\n+\n+    - // *Check the transaction's structure*\n+    - if context.tx.tze_out_count != 0:\n+\n+      - Return False\n+\n+    - if Verify(cust-pk, msg, sig) == 1: // *This is a customer claim transaction*\n+\n+      - expiry-height = find_block_height(context.tx.tze_input[0].prevout_hash) // TODO\n+      - if context.block-height >= expiry-height + DELAY:\n+\n+        - Return True\n+\n+\n+    - if Verify(merch-pk, msg, sig) == 1:// *This is a dispute transaction*\n+\n+      - if rev-lock == SHA256(rev-secret):\n+\n+        - Return True\n+\n+\n+\n+- else if mode == 2: // *expiry mode*\n+\n+  - init-cid || init-merch-pk || init-merch-ps-pk || init-cust-pk = precondition\n+\n+  - if context.tx.tze_out_count == 1: // *This is a close transaction*\n+\n+    - // *Check the transaction's structure*\n+    - if context.tx.tx_out_count != 1:\n+\n+      - Return False\n+\n+    - merch-ps-sig || cust-sig = witness\n+    - cid || merch-pk || cust-pk || rev-lock = context.tx.tze_out[0].precondition.tze_data_payload\n+    - merch-balance = context.tx.tx_out[0].value\n+    - cust-balance = context.tx.tze_out[0].amount\n+    - state = cid || cust-pk || rev-lock || cust-balance || merch-balance\n+\n+    - if init-cid != cid OR init-merch-pk != merch-pk OR init-cust-pk != cust-pk:\n+\n+      - Return False\n+\n+    - if Verify(cust-pk, msg, cust-sig) != 1 OR PS_sig.Verify(merch-ps-pk, state, merch-ps-sig) != 1:\n+\n+      - Return False\n+\n+    - if context.tx.tx_out[0] == merch-balance || <ZCash P2PKH version bytes (0x1C || 0xB8 for mainnet)> || Hash_160(merch-pk)\n+\n+      - Return True\n+\n+  - else if context.tx.tx_out_count == 1: // *This is a merchant claim transaction*\n+\n+    - sig = witness\n+\n+    - if Verify(merch-pk, msg, sig) == 1:\n+\n+      - expiry-height = find_block_height(context.tx.tze_input[0].prevout_hash) // TODO\n+      - if context.block-height >= expiry-height + DELAY:\n+\n+        - Return True\n+\n+\n+- Return False\n+\n+Transaction Malleability\n+========================\n+\n+As the transaction id is not built upon the TZE witnesses, any change to the transaction's signature does not modify its id.\n+\n+The preconditions are non-malleable as they are signed with a non-malleable signature.\n+\n+To prevent witness malleability, we intend to fix the witness payload's size in the tze_verify such that the addition or removal of any bit would result in a verification failure. Any change to the bits should either result in a verification failure, when the revocation secret or a signature is corrupted, or a pass, when the PS signature is randomized.\n+\n+References\n+==========\n+.. [#BoltTZE] _`Bolt TZE implementation for Zcash <https://github.com/boltlabs-inc/librustzcash>`\n+.. [#RFC2119] `Key words for use in RFCs to Indicate Requirement Levels <https://tools.ietf.org/html/rfc2119>`_\n+.. [#bolt-paper]  `Bolt: Anonymous Payment Channels for Decentralized Currencies <https://eprint.iacr.org/2016/701>`_\n+.. [#zip-0222]  `ZIP 222: Transparent Zcash Extensions (Draft) <https://github.com/zcash/zips/pull/248>`_\n\\ No newline at end of file"},{"filename":"zip-bolt-support.rst","status":"added","additions":334,"deletions":0,"patch":"@@ -0,0 +1,334 @@\n+::\n+\n+  ZIP: XXX\n+  Title: Add support for Blind Off-chain Lightweight Transactions (Bolt) protocol\n+  Authors: J. Ayo Akinyele <ayo@boltlabs.io>\n+           Colleen Swanson <swan@boltlabs.io>\n+  Credits: Ian Miers <imiers@z.cash>\n+           Matthew Green <mgreen@z.cash>\n+  Category: Consensus\n+  Created: 2019-07-15\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\" and \"MUST NOT\" in this document are to be interpreted as described in RFC 2119. [#RFC2119]_\n+\n+Abstract\n+========\n+\n+This proposal specifies three possible approaches for integrating the blind off-chain lightweight transaction (Bolt) protocol [#bolt-paper]_ into Zcash.\n+\n+Motivation\n+==========\n+\n+Layer 2 protocols like Lightning enable scalable payments for Bitcoin but lack the mechanisms to provide strong privacy guarantees on the payment network. Zcash offers private transactions but currently lacks features that would enable a lightning-style payment channel. This proposal specifies an integration of the Bolt privacy-preserving Lightning protocol on top of Zcash [#bolt-paper]_.\n+\n+Specification\n+=============\n+\n+This specification details an initial approach to integrating the features of Bolt into Zcash in a future network upgrade and depends on the WTP ZIP [#wtp-programs]_ that introduces Whitelisted Transparent Programs (WTPs). Our prototype implementation can be found here: [#BoltWTP]_.\n+\n+1. General requirements for Bolt protocol\n+--------------------------\n+\n+Bolt private payment channels require the following capabilities to provide anonymity properties for users on a payment network:\n+\n+(1) Ability to create a escrow transaction such that the transaction inputs are anonymous.\n+(2) Ability to escrow funds to a multi-signature style address via non-malleable transactions.\n+(3) Ability to specify relative time locks for commitment transactions to support unilateral channel closing.\n+(4) Ability to specify absolute and relative time locks to support HTLCs for multi-hop payments.\n+(5) Ability to validate Bolt-specific opening and closing transactions:\n+\n+    - check the validity of randomized/blinded signature on the wallet commitment in closing token\n+    - check the validity of revocation token in the event of a channel dispute by merchant\n+\n+(6) Ability to verify transaction outputs using WTPs such that:\n+\n+    - if customer-initiated closing, one output pays out to customer with a time lock (to allow merchant to dispute customer balance) and one output pays out to merchant immediately\n+    - if merchant-initiated closing, a single output pays the merchant the full balance of the channel with a time lock that allows for customer dispute\n+\n+**Channel Operation Assumptions.**\n+ - Channels funded by the customer alone and dual-funded channels are both supported.\n+ - Either the customer or the merchant can initiate channel closing.\n+ - If the customer initiates closing, then the merchant can dispute the closing transaction if they disagrees with the closing token in the closing transaction.\n+ - If the merchant initiates closing, the merchant posts a transaction claiming all the funds in the channel for themselves with a timelock. This gives the customer the opportunity to post their own valid closing transaction with the current channel balances. If the customer posts their own closing transaction, the merchant has an additional opportunity to dispute if necessary.\n+\n+1.1 Customer and Merchant Signing Keys\n+-------------\n+\n+The customer and the merchant both have key pairs from a suitable signature scheme. These are denoted as:\n+``<cust-pk>, <cust-sk>`` and \n+``<merch-pk>, <merch-sk>``, respectively, where ``pk`` stands for \"public key\" and ``sk`` stands for the corresponding \"secret key\".\n+\n+The merchant must be able to issue blind signatures, so they have an additional keypair; this keypair is denoted as:\n+``<MERCH-PK>, <MERCH-SK>``.\n+\n+The customer key pair is specific to the channel and must not be reused. The merchant key pair is long term and should be used for all customer channels. \n+\n+1.2 Wallets\n+-------------\n+A Bolt channel allows a customer to make or receive a sequence of payments off chain. These payments are tracked and validated using a sequence of *wallets*. A wallet consists of the customer's public key (which ties the wallet to the channel), a wallet-specific public key (which can be from any suitable signature scheme), denoted ``<wpk>``, and the current customer and merchant balances.\n+\n+After each payment, the customer receives an updated wallet and blind signatures from the merchant on the wallet contents allowing channel close as specified below.\n+\n+1.2 Opening a Channel: Overview\n+-------------\n+To open a channel, the customer and merchant exchange key information and set the channel token ``<channel-token> = <cust-pk>, <merch-pk>, <MERCH-PK>``. \n+\n+They agree on their respective initial balances ``initial-cust-balance`` and ``initial-merch-balance``.\n+\n+The customer picks an inital wallet public key ``<wpk>``.\n+\n+The customer and merchant escrow the necessary funds in a funding transaction, denoted ``escrow-tx``. \n+\n+1.3 Closing a Channel: Overview\n+-------------\n+\n+A customer should be able to close the channel by posting a *closing token* ``close-token``, which is a blind signature from the merchant under ``<MERCH-PK>`` on a special closing wallet that contains ``<cust-pk>, <wpk>, <balance-cust>, <balance-merch>, CLOSE``. We use ``cust-close-tx`` to denote the transaction posted by the customer to initiate channel closure.\n+\n+A merchant should be able to close the channel by either posting a special closing transaction ``merch-close-tx`` (detailed in Section 2.3.2) or, if the customer posts an outdated version of their closing token, a signed revocation token, ``revocation-token`` as detailed below. The revocation token ``revocation-token`` is a signature under the wallet public key ``<wpk>`` on the special revocation message ``<wpk> || REVOKED``. The transaction posted by the merchant to dispute is denoted ``dispute-tx``.\n+\n+The customer and merchant may also negotiate off-chain to form a *mutual close transaction*, ``mutual-close-tx``. Off-chain collaboration to create ``mutual-close-tx`` reduces the required number of on-chain transactions and eliminates the time delays.\n+\n+2. Transparent/Shielded Tx: Using T/Z-addresses and WTPs\n+-------------\n+\n+We assume the following specific features are present:\n+\n+(1) Support for whitelisted transparent programs (WTPs) that enables 2-of-2 multi-sig style transactions\n+(2) Can specify absolute lock time in transaction\n+(3) Can specify relative lock time in transparent program\n+(4) Can specify shielded inputs and outputs\n+(5) A non-SegWit approach that fixes transaction malleability\n+(6) ``OP_BOLT`` logic expressed as WTPs. We will use the Bolt WTPs defined in Section 2.1: ``open-channel``, ``cust-close``, and ``merch-close``.\n+\n+**Privacy Limitations**. The aggregate balance of the channel will be revealed in the funding transaction ``escrow-tx``. Similarly, the final splitting of funds will be revealed to the network. However, for channel opening and closing, the identity of the participants remains hidden. Channel opening and closing will also be distinguishable on the network due to use of WTPs.\n+\n+**Channel Opening**. The funding transaction ``escrow-tx`` spends ZEC from one or more shielded addresses to a transparent output that is encumbered by a Bolt transparent program. See Section 2.1 for what the funding transaction looks like when instantiated using WTPs.\n+\n+2.1 Bolt WTPs\n+--------------\n+\n+Transparent programs take as input a ``predicate``, ``witness``, and ``context`` and then output a ``True`` or ``False`` on the stack. Bolt-specific transparent programs are deterministic and any malleation of the ``witness`` will result in a ``False`` output. The WTPs are as follows:\n+\n+1. ``open-channel`` program. The purpose of this WTP is to encumber the funding transaction such that either party may initiate channel closing as detailed above in Section 1.3. The program is structured as follows:\n+\n+\ta. ``predicate``: The predicate consists of ``<<channel-token> || <merch-close-address>>``, where ``<channel-token> = <<cust-pk> || <merch-pk> || <MERCH-PK>>`` contains three public keys, one for the customer and two for the merchant, and an address ``<merch-close-address>`` for the merchant at which to receive funds from a customer-initiated close.\n+\t\n+\tb. ``witness``: The witness is defined as follows, where the first byte is used to denote witness type:\n+\t\n+    \t\t1. ``<<0x0> || <balance-cust> || <balance-merch> || <cust-sig> || <merch-sig>>``\n+    \t\t2. ``<<0x1> || <balance-cust> || <balance-merch> || <cust-sig> || <wpk> || <closing-token>>``\n+  \t\n+\tc. ``verify_program`` behaves as follows:\n+\t\n+    \t\t1. If witness is of type ``0x0``, check that 2 new outputs are created, with the specified balances (unless one of the balances is zero), and that the signatures verify.\n+    \t\t2. If witness is of type ``0x1``, check that 2 new outputs are created (unless one of the balances is zero), with the specified balances:\n+\t\t\n+      \t\t\t+ one paying ``<balance-merch>`` to ``<merch-close-address>`` \n+      \t\t\t+ one paying a ``cust-close`` WTP containing ``<channel-token>`` and ``<wallet> = <<wpk> || <balance-cust> || <balance-merch>>``\n+\t\t\t\n+      \t\t\tAlso check that ``<cust-sig>`` is a valid signature and that ``<closing-token>`` contains a valid signature under ``<MERCH-PK>`` on ``<<cust-pk> || <wpk> || <balance-cust> || <balance-merch> || CLOSE>``.\n+\n+2. ``cust-close`` program. The purpose of this WTP is to allow the customer to initiate channel closure as specified in Section 1.3. The program is specified as follows:\n+\n+\ta. ``predicate``: ``<<channel-token> || <block-height> || <wallet>>``, where\n+\t\n+\t\t1. ``<channel-token> = <<cust-pk> || <merch-pk> || <MERCH-PK>>``,\n+\t\t2. ``<block_height>`` is the earliest block-height when balance can be spend, and\n+\t\t3. ``<wallet> = <<wpk> || <balance-cust> || <balance-merch>>``.\n+\tb. ``witness``: The witness is defined as one of the following, where the first byte is used to denote witness type:\n+\t\n+\t\t1. ``<<0x0> || <cust-sig>>``\n+\t\t2. ``<<0x1> || <merch-sig> || <address> || <revocation-token>>``\n+\tc. ``verify_program`` behaves as follows:\n+\t\n+\t\t1. If witness is of type ``0x0``, check that ``<cust-sig>`` is valid and ``<block-height>`` has been reached\n+\t\t2. If witness is of type ``0x1``, check that 1 output is created paying ``<balance-cust>`` to ``<address>``. Also check that ``<merch-sig>`` is a valid signature on ``<<address> || <revocation-token>>`` and that ``<revocation-token>`` contains a valid signature under ``<wpk>`` on ``<<wpk> || REVOKED>``.\n+\n+3. ``merch-close``. The purpose of this WTP is to allow a merchant to initiate channel closure as specified in Section 1.3. The program is specified as follows:\n+\n+\ta. ``predicate``: ``<<channel-token> || <block-height> || <merch-close-address>>``.\n+\tb. ``witness`` is defined as one of the following, where the first byte is used to denote witness type:\n+\t\n+\t\t1. ``<<0x0> || <merch-sig>>``\n+\t\t2. ``<<0x1> || <cust-sig> || <wallet> || <closing-token>>``, where ``<wallet> = <<wpk> || <balance-cust> || <balance-merch>>``.\n+\tc. ``verify_program`` behaves as follows:\n+\t\t\n+\t\t\t1. If witness is of type ``0x0``, check that ``<merch-sig>`` is valid and ``<block-height>`` has been reached\n+\t\t\t2. If witness is of type ``0x1``, check that 2 new outputs are created (unless one of the balances is zero), with the specified balances:\n+\t\t\t\n+\t\t\t\t+ one paying ``<balance-merch>`` to ``<merch-close-address>`` \n+ \t\t\t\t+ one paying a ``cust_close`` WTP containing ``<wallet> = <<wpk> || <balance-cust> || <balance-merch>>``  and ``<channel-token>``. \n+\t\t\t\t\n+\t\t\t\tAlso check that ``<cust-sig>`` is a valid signature and that ``<closing-token>`` contains a valid signature under ``<MERCH-PK>`` on ``<<cust-pk> || <wpk> || <balance-cust> || <balance-merch> || CLOSE>``.\n+\n+\n+2.2 Channel establishment and Funding Transaction\n+-------------\n+The funding transaction ``escrow-tx`` by default has 2 shielded inputs (but can be up to some N) and an ``open-channel`` WTP output with predicate ``<<channel-token> <merch-close-address>>``. \n+\n+* ``lock_time``: 0\n+* ``nExpiryHeight``: 0\n+* ``valueBalance``: funding amount + transaction fee\n+* ``nShieldedSpend``: 1 or N (if funded by both customer and merchant)\n+* ``vShieldedSpend[0]``: tx for customer’s note commitment and nullifier for the coins\n+\n+  - ``cv``: commitment for the input note\n+  - ``root``: root hash of note commitment tree at some block height\n+  - ``nullifier``: unique serial number of the input note\n+  - ``rk``: randomized pubkey for spendAuthSig\n+  - ``zkproof``: zero-knowledge proof for the note\n+  - ``spendAuthSig``: signature authorizing the spend\n+\n+* ``vShieldedSpend[1..N]``: additional tx for customer's note commitment and nullifier for the coins\n+\n+  - ``cv``: commitment for the input note\n+  - ``root``: root hash of note commitment tree at some block height\n+  - ``nullifier``: unique serial number of the input note\n+  - ``rk``: randomized pubkey for spendAuthSig\n+  - ``zkproof``: zero-knowledge proof for the note\n+  - ``spendAuthSig``: signature authorizing the spend\n+* ``tx_out_count``: 1\n+* ``tx_out``: (via a transparent program)\n+\n+  - ``scriptPubKey``: ``PROGRAM PUSHDATA( <open-channel> || <<channel-token> || <merch-close-address>> )``\n+\n+* ``bindingSig``: a signature that proves that (1) the total value spent by Spend transfers - Output transfers = value balance field.\n+\n+The customer and merchant collaborate to create the customer's initial closing token ``closing-token`` and the merchant closing transaction ``merch-close-tx`` before signing and sending ``escrow-tx`` to the network. Once the transaction has been confirmed, the payment channel is established.\n+\n+2.3 Channel Closing\n+-------------\n+2.3.1 Customer-initiated channel closing.\n+----\n+To initiated channel closure, a customer posts the transaction ``cust-close-tx`` that spends from ``escrow-tx`` and contains two outputs: (1) an output that can be spent immediately by the merchant and (2) a ``cust-close`` WTP output that can be spent either by the customer after a relative timeout or by the merchant with a revocation token. This approach allows the merchant to dispute if the customer posts a transaction containing a spent closing token (i.e., a closing token that is valid from the network's perspective but outdated from the merchant's perspective).\n+\n+The transaction ``cust-close-tx`` is as follows:\n+\n+* ``version``: specify version number\n+* ``groupid``: specify group id\n+* ``locktime``: should be set such that closing transactions can be included in a current block.\n+* ``txin`` count: 1\n+\n+   - ``txin[0]`` outpoint: references the funding transaction txid and output_index\n+   - ``txin[0]`` script bytes: 0\n+   - ``txin[0]`` scriptSig: ``PROGRAM PUSHDATA( <open-channel> || <<0x1> || <balance-cust> || <balance-merch> || <cust-sig> || <wpk> || <closing-token>> )``\n+\n+* ``txout`` count: 2\n+* ``txouts``:\n+\n+  * ``to_customer``: a ``cust-close`` WTP output.\n+  \n+      - ``amount``: ``<balance-cust>``\n+      - ``nSequence: <time-delay>``\n+      - ``scriptPubKey``: ``PROGRAM PUSHDATA( <cust-close> || <<channel-token> || <wallet>>  )``\n+\n+  * ``to_merchant``: a P2PKH output sending funds to the merchant, i.e.\n+  \n+      * ``scriptPubKey``: ``0 <20-byte-key-hash of merch-close-address>``\n+      * ``amount``: ``<balance-merch>``\n+      * ``nSequence``: 0\n+\n+To redeem the ``to_customer`` output, the customer posts a secondary closing transaction after the appropriate time delay with the following ``scriptSig``:\n+\n+\t``PROGRAM PUSHDATA( <cust-close> || <<0x0> || <cust-sig> || <block-height>> )``\n+\n+where the ``witness`` consists of a first byte ``0x0`` to indicate the witness type followed by the customer signature and the current block height (used to ensure that timeout reached). \n+\n+If the customer posts a spent closing token, the merchant can dispute and redeem the ``to_customer`` output by posting a transaction ``dispute-tx`` that spends from ``cust-close-tx`` with the following ``scriptSig``:\n+\n+\t``PROGRAM PUSHDATA( <cust-close> || <<0x1> || <merch-sig> || <revocation-token>> )``\n+\n+where the ``witness`` consists of a first byte ``0x1`` to indicate the witness type followed by the merchant signature and the revocation token.\n+\n+2.3.2 Merchant-initiated channel closure\n+----\n+To initiate channel closure, the merchant posts the following transaction ``merch-close-tx`` (formed and signed during channel establishment) that spends from ``escrow-tx``:\n+\n+* ``version``: specify version number\n+* ``groupid``: specify group id\n+* ``locktime``: should be set such that closing transactions can be included in a current block.\n+* ``txin`` count: 1\n+\n+   - ``txin[0]`` outpoint: references the funding transaction txid and output_index\n+   - ``txin[0]`` script bytes: 0\n+   - ``txin[0]`` scriptSig: ``PROGRAM PUSHDATA( <open-channel> || <<0x0> || <balance-cust> || <balance-merch> || <cust-sig> || <merch-sig>> )``\n+\n+* ``txout`` count: 1\n+* ``txouts``:\n+\n+  * ``to_merchant``: a ``merch-close`` WTP output.\n+  \n+      - ``amount``: sum of ``<balance-cust>`` and ``<balance-merch>``\n+      - ``nSequence: <time-delay>``\n+      - ``scriptPubKey``: ``PROGRAM PUSHDATA( <merch-close> || <<channel-token> || <merch-close-address>> )``\n+\n+To spend this output, the merchant posts a secondary closing transaction after the appropriate time delay with the following ``scriptSig``:\n+\n+\t``PROGRAM PUSHDATA( <merch-close> || <<0x0> || <merch-sig> || <block-height>> )``\n+\n+where the ``witness`` consists of a first byte ``0x0`` to indicate witness type, followed by the merchant signature and the current block height (used to ensure that the timeout has been reached). \n+\n+If the customer sees ``merch-close-tx`` on chain, and the current customer balance in the channel is actually non-zero, the customer should post their own closing transaction. This closing transaction is nearly identical to that specified in the customer-initiated channel closure section above and allows for merchant dispute in the same way:\n+\n+* ``version``: specify version number\n+* ``groupid``: specify group id\n+* ``locktime``: should be set such that closing transactions can be included in a current block.\n+* ``txin`` count: 1\n+\n+   - ``txin[0]`` outpoint: references the ``merch-close-tx`` txid and output_index\n+   - ``txin[0]`` script bytes: 0\n+   - ``txin[0]`` scriptSig: ``PROGRAM PUSHDATA( <merch-close> || <<0x1> || <balance-cust> || <balance-merch> || <cust-sig> || <wpk> || <closing-token>> )``\n+\n+* ``txout`` count: 2\n+* ``txouts``:\n+\n+  * ``to_customer``: a ``cust-close`` WTP output.\n+  \n+      - ``amount``: ``<balance-cust>``\n+      - ``nSequence: <time-delay>``\n+      - ``scriptPubKey``: ``PROGRAM PUSHDATA( <cust-close> || <<channel-token> || <wallet>>  )``\n+\n+  * ``to_merchant``: a P2PKH output sending funds to the merchant, i.e.\n+  \n+      * ``scriptPubKey``: ``0 <20-byte-key-hash of merch-close-address>``\n+      * ``amount``: ``<balance-merch>``\n+      * ``nSequence``: 0\n+\n+\n+2.3.3 Mutual closing\n+-------------\n+The customer and merchant can alternatively collaborate off-chain to create a mutual closing transaction ``mutual-close-tx`` that spends from ``escrow-tx``. This transaction is as follows:\n+\n+\n+* ``version``: specify version number\n+* ``groupid``: specify group id\n+* ``locktime``: should be set such that closing transactions can be included in a current block.\n+* ``txin`` count: 1\n+\n+   - ``txin[0]`` outpoint: references the funding transaction txid and output_index\n+   - ``txin[0]`` script bytes: 0\n+   - ``txin[0]`` scriptSig: ``PROGRAM PUSHDATA( <open-channel> || <<0x0> || <balance-cust> || <balance-merch> || <cust-sig> || <merch-sig>> )``\n+\n+* ``txout`` count: 2\n+* ``txouts``:\n+\n+  * ``to_customer``: an output paying ``<balance-cust>``\n+  * ``to_merchant``: an output paying ``<balance-merch>``\n+     \n+\n+Reference Implementation\n+========================\n+\n+.. [#BoltWTP] _`Bolt WTP implementation for Zcash <https://github.com/boltlabs-inc/librustzcash>`\n+\n+References\n+==========\n+\n+.. [#RFC2119] `Key words for use in RFCs to Indicate Requirement Levels <https://tools.ietf.org/html/rfc2119>`_\n+.. [#bolt-paper]  `Bolt: Anonymous Payment Channels for Decentralized Currencies <https://eprint.iacr.org/2016/701>`_\n+.. [#wtp-programs]  `ZIP XXX: Whitelisted Transparent Programs (Draft) <https://github.com/zcash/zips/pull/248>`_"}],"filesTruncated":false,"preview":{"filename":"zip-0309.rst","title":"Add support for zk-Channels protocol","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"zk-channels-high-level-overview","text":"zk-Channels high level Overview"},{"level":2,"id":"requirements","text":"1. Requirements"},{"level":2,"id":"general-requirements-for-zk-channels-protocol","text":"1.1 General requirements for zk-Channels protocol"},{"level":2,"id":"tzes-requirement-for-zk-channels-protocol","text":"1.2 TZEs requirement for zk-Channels protocol"},{"level":2,"id":"algorithms-and-variables","text":"2 Algorithms and variables"},{"level":2,"id":"customer-and-merchant-signing-keys","text":"2.1 Customer and Merchant Signing Keys"},{"level":2,"id":"channel-states","text":"2.2 Channel States"},{"level":2,"id":"opening-a-channel","text":"3 Opening a channel"},{"level":2,"id":"closing-a-channel","text":"4 Closing a channel"},{"level":1,"id":"zk-channels-tze-transactions","text":"zk-Channels TZE transactions"},{"level":2,"id":"escrow-transaction","text":"1 Escrow transaction"},{"level":2,"id":"mutual-close","text":"2 Mutual close"},{"level":2,"id":"expiry","text":"3 Expiry"},{"level":2,"id":"expiry-1","text":"3.1 Expiry"},{"level":2,"id":"merchant-claim","text":"3.2 Merchant claim"},{"level":2,"id":"customer-close","text":"4 Customer close"},{"level":2,"id":"customer-close-1","text":"4.1 Customer close"},{"level":2,"id":"customer-claim","text":"4.2 Customer claim"},{"level":2,"id":"dispute","text":"4.3 Dispute"},{"level":1,"id":"tze-verify-function","text":"TZE_VERIFY function"},{"level":1,"id":"transaction-malleability","text":"Transaction Malleability"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":316,"title":"[WIP] ZIP: regular periodic turnstiles","author":"nathan-at-least","createdAt":"2020-01-23T15:14:03Z","updatedAt":"2026-05-19T20:50:32Z","url":"https://github.com/zcash/zips/pull/316","diffUrl":"https://github.com/zcash/zips/pull/316.diff","headRef":"WIP.ZIP.regular-periodic-turnstiles","headRepo":"nathan-at-least/zips","headSha":"8b6740ea247e8854664ed3dc6839ca3180611932","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This is an incomplete ZIP. I'd appreciate it being merged and assigned a number early in the process to facilitate discussion. In particular a number assignment greatly helps with discussions across different forums. Visitors can see a [rendering of the current revision here](https://github.com/zcas","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -70,4 +70,5 @@ Index of ZIPs\n     <tr> <td>1013</td> <td class=\"left\"><a href=\"zip-1013.rst\">Keep It Simple, Zcashers: 10% to ECC, 10% to ZF</a></td> <td>Draft</td>\n     <tr> <td>1014</td> <td class=\"left\"><a href=\"zip-1014.rst\">Dev Fund to ECC + ZF + Major Grants</a></td> <td>Draft</td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td>\n+    <tr> <td>regular-periodic-turnstiles</td> <td class=\"left\"><a href=\"zip-regular-periodic-turnstiles.rst\">Regular Periodic Turnstiles</a></td> <td>Draft</td>\n   </table></embed>"},{"filename":"index.html","status":"modified","additions":1,"deletions":0,"patch":"@@ -58,6 +58,7 @@ <h2>Index of ZIPs</h2>\n   <tr> <td>1013</td> <td class=\"left\"><a href=\"zip-1013\">Keep It Simple, Zcashers: 10% to ECC, 10% to ZF</a></td> <td>Draft</td>\n   <tr> <td>1014</td> <td class=\"left\"><a href=\"zip-1014\">Dev Fund to ECC + ZF + Major Grants</a></td> <td>Draft</td>\n   <tr> <td>guide</td> <td class=\"left\"><a href=\"zip-guide\">{Something Short and To the Point}</a></td> <td>Draft</td>\n+  <tr> <td>regular-periodic-turnstiles</td> <td class=\"left\"><a href=\"zip-regular-periodic-turnstiles\">Regular Periodic Turnstiles</a></td> <td>Draft</td>\n </table></embed></section>\n     </section>\n </body>"},{"filename":"zip-regular-periodic-turnstiles.html","status":"added","additions":146,"deletions":0,"patch":"@@ -0,0 +1,146 @@\n+<!DOCTYPE html>\n+<html>\n+<head>\n+    <title>ZIP regular-periodic-turnstiles: Regular Periodic Turnstiles</title>\n+    <meta charset=\"utf-8\" />\n+<link rel=\"stylesheet\" href=\"css/zip-style.css\"><link rel=\"stylesheet\" href=\"assets/css/style.css\"></head>\n+<body>\n+    <section>\n+        <pre>ZIP: ???\n+Title: Regular Periodic Turnstiles\n+Owner: Nathan Wilcox &lt;nathan@electriccoin.co&gt;\n+Original-Authors: Nathan Wilcox\n+Status: Draft\n+Category: Protocol Design Policy\n+Created: 2020-01-20\n+License: MIT</pre>\n+        <section id=\"terminology\">\n+            <h2>Terminology</h2>\n+            <p>The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to be interpreted as described in RFC 2119. <a refname=\"rfc2119\" href=\"#id4\" class=\"footnote_reference\" id=\"id1\">1</a></p>\n+            <p>The terms below are to be interpreted as follows:</p>\n+            <dl>\n+                <dt>Sprout protocol</dt>\n+                <dd>Code-name for the Zcash shielded protocol at launch.</dd>\n+                <dt>Sapling protocol</dt>\n+                <dd>Code-name for the Zcash shielded protocol added by the second Zcash network upgrade, also known as Network Upgrade 1.</dd>\n+                <dt>Blossom protocol</dt>\n+                <dd>Code-name for the Zcash shielded protocol added by the third Zcash network upgrade, also known as Network Upgrade 2.</dd>\n+            </dl>\n+        </section>\n+        <section id=\"abstract\">\n+            <h2>Abstract</h2>\n+            <p>This ZIP establishes a protocol design policy to make Zcash <cite>shielded pool turnstiles</cite> a regular process on a predictable schedule. The schedule introduces a new shielded pool once per year, as well as an established <cite>retirement process</cite> for older pools.</p>\n+        </section>\n+        <section id=\"motivation\">\n+            <h2>Motivation</h2>\n+            <p>…</p>\n+        </section>\n+        <section id=\"requirements\">\n+            <h2>Requirements</h2>\n+            <section id=\"transaction-semantics\">\n+                <h3>Transaction Semantics</h3>\n+                <p>This specification requires the following transaction semantics remain in place in future upgrades. These semantics are already present since the Network Upgrade 1 Sapling upgrade:</p>\n+                <ol type=\"1\">\n+                    <li>A transaction's interactions with any given pool are publicly verifiable in the consensus representation. For example, if a transaction involves the Sprout (original) pool and the Sapling NU1 pool, then separate Sprout and Sapling fields are present. Note that if this ZIP is standardized, some pools may have identical field layouts and semantics, and MUST never-the-less be distinguishable in the transaction representation.</li>\n+                    <li>ZEC may not enter or leave a pool except through an unencrypted, unobfuscated value encoding in the transaction field representing interactions with that pool. Note that this value representation does not require interacting with Transparent addresses, and these semantics can remain in place even should Transparent Addresses be retired from the protocol.</li>\n+                </ol>\n+            </section>\n+            <section id=\"schedule\">\n+                <h3>Schedule</h3>\n+                <p>Once per year, during a network upgrade, a new <cite>shielded pool</cite> is established in the consensus protocol. Such network upgrades are referred to as <cite>major network upgrades</cite>. Other upgrades are referred to as <cite>minor network upgrades</cite>.</p>\n+                <p>These shielded pools are referred to by the major network upgrade that introduces them, as in <cite>shielded pool NU5</cite> would be the shielded pool introduced in <cite>Network Upgrade 5</cite> which implies NU5 is a major upgrade.</p>\n+                <p>Only a subset of network upgrades, the major upgrades, introduce shielded pools, since more than one network upgrade may occur in one year. Also note that this policy requires <em>at least one</em> network upgrade per year.</p>\n+            </section>\n+            <section id=\"shielded-pool-lifecycles\">\n+                <h3>Shielded Pool Lifecycles</h3>\n+                <p>Shielded pools are retired through a well-known <cite>retirement process</cite> on a predictable schedule so that they adhere to the following <cite>shielded pool lifecycle</cite> which is a sequence of <cite>stages</cite>. Shielded pools transition to subsequent stages only via the activation of a major network upgrade. At the activation of each major network upgrade, a shielded pool always transitions to the subsequent stage, unless it is in the frozen stage in which it remains permanently.</p>\n+                <p>The stages are:</p>\n+                <ol type=\"1\">\n+                    <li><cite>Current Shielded Pool Stage</cite></li>\n+                    <li><cite>Previous Shielded Pool Stage</cite></li>\n+                    <li><cite>Restricted Shielded Pool Stage</cite></li>\n+                    <li><cite>Retired Shielded Pool Stage</cite></li>\n+                    <li><cite>Frozen Shielded Pool Stage</cite></li>\n+                </ol>\n+                <section id=\"protocol-lifecycle-states\">\n+                    <h4>Protocol Lifecycle States</h4>\n+                    <p>Each lifecycle <cite>stage</cite> has an associated <cite>protocol lifecycle state</cite> (or <cite>protocol state</cite> for short) which determines a standard set of consensus rule semantics for interacting with that shielded pool.</p>\n+                    <ol type=\"1\">\n+                        <li>Both the <cite>current</cite> and <cite>previous</cite> stages are in the <cite>active protocol state</cite>. In this state the full functionality of the associated pool is available, including transfering funds into the pool from any other active pool or transparent address, transfering funds within the pool, and transfering funds out of the active pool either to a Transparent Address or any other active pool.</li>\n+                        <li>The <cite>restricted</cite> stage is in the <cite>restricted protocol state</cite>, which prevents any transfers of funds <em>into</em> the pool. Funds may still be transferred out of the pool, and between accounts in the pool. <a href=\"#id8\" id=\"id9\" class=\"problematic\">[#]_</a></li>\n+                        <li>The <cite>retired</cite> stage is in the <cite>retired protocol state</cite>, which disallows any transfer of funds <em>except</em> out of the pool. This explicitly prohibits transfers of funds between addresses within the pool. <a href=\"#id8\" id=\"id10\" class=\"problematic\">[#]_</a></li>\n+                        <li>The <cite>frozen</cite> stage is the <cite>frozen protocol state</cite>, in which no transactions may interact with the pool.</li>\n+                    </ol>\n+                    <table id=\"id4\" class=\"footnote\">\n+                        <tbody>\n+                            <tr>\n+                                <th>1</th>\n+                                <td>Implementation note: Because of the transaction semantic requirements, this rule can generally be enforced without circuit changes by merely requiring the transparent funds input to the pool to equal 0.</td>\n+                            </tr>\n+                        </tbody>\n+                    </table>\n+                    <section id=\"protocol-states-and-future-compatibility\">\n+                        <h5>Protocol States and Future Compatibility</h5>\n+                        <p>Zcash is likely to extend shielded functionality in the future in a manner of ways, potentially including even general purpose stateful extensions. If this ZIP is standardized, all such new functionality specifications MUST define appropriate semantics for the four <cite>protocol lifecycle states</cite> above.</p>\n+                    </section>\n+                </section>\n+            </section>\n+            <section id=\"user-interface-interactions\">\n+                <h3>User Interface Interactions</h3>\n+                <p>This ZIP standardizes several user interface requirements:</p>\n+                <section id=\"addresses\">\n+                    <h4>Addresses</h4>\n+                    <p>Some major upgrades will require address format changes, and some will not. It is also possible a future pool may support multiple distinct address types due to different kinds of functionality. Therefore, user interfaces must account for managing funds across multiple pools for a single address.</p>\n+                </section>\n+                <section id=\"migration\">\n+                    <h4>Migration</h4>\n+                    <p>Because fund amounts must be transparently publicly revealed in transfers between pools by design, this can negatively impact user privacy. User interfaces should provide tools to help users migrate funds between pools as safely as possible.</p>\n+                    <p>This ZIP requires that user interfaces support the <a href=\"#zip-0308\" id=\"id5\" class=\"footnote_reference\">2</a> migration design from any non-frozen pool to the active pool only. If the <a href=\"#zip-0308\" id=\"id6\" class=\"footnote_reference\">2</a> migration design is superceded, user interfaces MUST adopt the newer migration design upon the next major activation.</p>\n+                    <aside class=\"admonition\">\n+                        <h1>TODO</h1>\n+                        <p>Introduce an improvement ZIP based on <a href=\"#zip-0308\" id=\"id7\" class=\"footnote_reference\">2</a> that is general between pools and thus compatible with this ZIP.</p>\n+                    </aside>\n+                </section>\n+            </section>\n+        </section>\n+        <section id=\"non-requirements\">\n+            <h2>Non-requirements</h2>\n+            <p>…</p>\n+        </section>\n+        <section id=\"specification\">\n+            <h2>Specification</h2>\n+            <p>…</p>\n+            <section id=\"related-zips\">\n+                <h3>Related ZIPs</h3>\n+                <p>…</p>\n+            </section>\n+            <section id=\"open-questions\">\n+                <h3>Open questions</h3>\n+                <p>…</p>\n+            </section>\n+        </section>\n+        <section id=\"consensus-node-support\">\n+            <h2>Consensus Node Support</h2>\n+            <p>…</p>\n+        </section>\n+        <section id=\"references\">\n+            <h2>References</h2>\n+            <table id=\"zip-0308\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>2</th>\n+                        <td><a href=\"zip-0308\">ZIP 308: Sprout to Sapling Migration</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+        </section>\n+    </section>\n+    <section class=\"system-messages\">\n+        <h1>Docutils System Messages</h1>\n+        <div id=\"id8\">\n+            <h1>System Message: ERROR/3 (zip-regular-periodic-turnstiles.rst line 80) <a href=\"#id9\">id9</a> <a href=\"#id10\">id10</a></h1>\n+            <p>Too many autonumbered footnote references: only 1 corresponding footnotes available.</p>\n+        </div>\n+    </section>\n+</body>\n+</html>\n\\ No newline at end of file"},{"filename":"zip-regular-periodic-turnstiles.rst","status":"added","additions":141,"deletions":0,"patch":"@@ -0,0 +1,141 @@\n+::\n+\n+  ZIP: ???\n+  Title: Regular Periodic Turnstiles\n+  Owner: Nathan Wilcox <nathan@electriccoin.co>\n+  Original-Authors: Nathan Wilcox\n+  Status: Draft\n+  Category: Protocol Design Policy\n+  Created: 2020-01-20\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to\n+be interpreted as described in RFC 2119. [#RFC2119]_\n+\n+The terms below are to be interpreted as follows:\n+\n+Sprout protocol\n+  Code-name for the Zcash shielded protocol at launch.\n+Sapling protocol\n+  Code-name for the Zcash shielded protocol added by the second Zcash network\n+  upgrade, also known as Network Upgrade 1.\n+Blossom protocol\n+  Code-name for the Zcash shielded protocol added by the third Zcash network\n+  upgrade, also known as Network Upgrade 2.\n+\n+\n+Abstract\n+========\n+\n+This ZIP establishes a protocol design policy to make Zcash `shielded pool turnstiles` a regular process on a predictable schedule. The schedule introduces a new shielded pool once per year, as well as an established `retirement process` for older pools.\n+\n+Motivation\n+==========\n+\n+…\n+\n+Requirements\n+============\n+\n+Transaction Semantics\n+---------------------\n+\n+This specification requires the following transaction semantics remain in place in future upgrades. These semantics are already present since the Network Upgrade 1 Sapling upgrade:\n+\n+#. A transaction's interactions with any given pool are publicly verifiable in the consensus representation. For example, if a transaction involves the Sprout (original) pool and the Sapling NU1 pool, then separate Sprout and Sapling fields are present. Note that if this ZIP is standardized, some pools may have identical field layouts and semantics, and MUST never-the-less be distinguishable in the transaction representation.\n+#. ZEC may not enter or leave a pool except through an unencrypted, unobfuscated value encoding in the transaction field representing interactions with that pool. Note that this value representation does not require interacting with Transparent addresses, and these semantics can remain in place even should Transparent Addresses be retired from the protocol.\n+\n+Schedule\n+--------\n+\n+Once per year, during a network upgrade, a new `shielded pool` is established in the consensus protocol. Such network upgrades are referred to as `major network upgrades`. Other upgrades are referred to as `minor network upgrades`.\n+\n+These shielded pools are referred to by the major network upgrade that introduces them, as in `shielded pool NU5` would be the shielded pool introduced in `Network Upgrade 5` which implies NU5 is a major upgrade.\n+\n+Only a subset of network upgrades, the major upgrades, introduce shielded pools, since more than one network upgrade may occur in one year. Also note that this policy requires *at least one* network upgrade per year.\n+\n+Shielded Pool Lifecycles\n+------------------------\n+\n+Shielded pools are retired through a well-known `retirement process` on a predictable schedule so that they adhere to the following `shielded pool lifecycle` which is a sequence of `stages`. Shielded pools transition to subsequent stages only via the activation of a major network upgrade. At the activation of each major network upgrade, a shielded pool always transitions to the subsequent stage, unless it is in the frozen stage in which it remains permanently.\n+\n+The stages are:\n+\n+#. `Current Shielded Pool Stage`\n+#. `Previous Shielded Pool Stage`\n+#. `Restricted Shielded Pool Stage`\n+#. `Retired Shielded Pool Stage`\n+#. `Frozen Shielded Pool Stage`\n+\n+Protocol Lifecycle States\n+~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+Each lifecycle `stage` has an associated `protocol lifecycle state` (or `protocol state` for short) which determines a standard set of consensus rule semantics for interacting with that shielded pool.\n+\n+#. Both the `current` and `previous` stages are in the `active protocol state`. In this state the full functionality of the associated pool is available, including transfering funds into the pool from any other active pool or transparent address, transfering funds within the pool, and transfering funds out of the active pool either to a Transparent Address or any other active pool.\n+#. The `restricted` stage is in the `restricted protocol state`, which prevents any transfers of funds *into* the pool. Funds may still be transferred out of the pool, and between accounts in the pool. [#]_\n+#. The `retired` stage is in the `retired protocol state`, which disallows any transfer of funds *except* out of the pool. This explicitly prohibits transfers of funds between addresses within the pool. [#]_\n+#. The `frozen` stage is the `frozen protocol state`, in which no transactions may interact with the pool.\n+\n+.. [#] Implementation note: Because of the transaction semantic requirements, this rule can generally be enforced without circuit changes by merely requiring the transparent funds input to the pool to equal 0.\n+\n+\n+Protocol States and Future Compatibility\n+++++++++++++++++++++++++++++++++++++++++\n+\n+Zcash is likely to extend shielded functionality in the future in a manner of ways, potentially including even general purpose stateful extensions. If this ZIP is standardized, all such new functionality specifications MUST define appropriate semantics for the four `protocol lifecycle states` above.\n+\n+User Interface Interactions\n+---------------------------\n+\n+This ZIP standardizes several user interface requirements:\n+\n+Addresses\n+~~~~~~~~~\n+\n+Some major upgrades will require address format changes, and some will not. It is also possible a future pool may support multiple distinct address types due to different kinds of functionality. Therefore, user interfaces must account for managing funds across multiple pools for a single address.\n+\n+Migration\n+~~~~~~~~~\n+\n+Because fund amounts must be transparently publicly revealed in transfers between pools by design, this can negatively impact user privacy. User interfaces should provide tools to help users migrate funds between pools as safely as possible.\n+\n+This ZIP requires that user interfaces support the [#zip-0308]_ migration design from any non-frozen pool to the active pool only. If the [#zip-0308]_ migration design is superceded, user interfaces MUST adopt the newer migration design upon the next major activation.\n+\n+.. admonition:: TODO\n+\n+   Introduce an improvement ZIP based on [#zip-0308]_ that is general between pools and thus compatible with this ZIP.\n+\n+Non-requirements\n+================\n+\n+…\n+\n+Specification\n+=============\n+\n+…\n+\n+Related ZIPs\n+------------\n+\n+…\n+\n+Open questions\n+--------------\n+\n+…\n+\n+Consensus Node Support\n+======================\n+\n+…\n+\n+References\n+==========\n+\n+.. [#zip-0308] `ZIP 308: Sprout to Sapling Migration <zip-0308.rst>`_"}],"filesTruncated":false,"preview":null},{"number":853,"title":"Coin Voting ZIP","author":"hhanh00","createdAt":"2024-05-28T05:22:51Z","updatedAt":"2026-05-17T02:45:12Z","url":"https://github.com/zcash/zips/pull/853","diffUrl":"https://github.com/zcash/zips/pull/853.diff","headRef":"main","headRepo":"hhanh00/zips","headSha":"d006de1e7a0d1910ebd07fbf792a8e030c1dbbdf","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"I added the rendered html file because I couldn't find a suitable markdown renderer. mdbook renders multiple files github flavored markdown doesn't support callouts","zipRefs":[340],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"index.html","status":"modified","additions":98,"deletions":109,"patch":"@@ -24,121 +24,110 @@\n             <p>To start contributing, first read <a href=\"zip-0000\">ZIP 0</a> which documents the ZIP process. Then clone <a href=\"https://github.com/zcash/zips\">this repo</a> from GitHub, and start adding your draft ZIP, formatted either as reStructuredText or as Markdown.</p>\n             <p>For example, if using reStructuredText, use a filename matching <code>draft-*.rst</code>. Use <code>make</code> to check that you are using correct <a href=\"https://docutils.sourceforge.io/rst.html\">reStructuredText</a> or <a href=\"https://pandoc.org/MANUAL.html#pandocs-markdown\">Markdown</a> syntax, and double-check the generated <code>draft-*.html</code> file before filing a Pull Request. See <a href=\"protocol/README\">here</a> for the project dependencies.</p>\n         </section>\n-        <section id=\"nu5-zips\"><h2><span class=\"section-heading\">NU5 ZIPs</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#nu5-zips\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n-            <p>This is the list of ZIPs relevant to the NU5 Upgrade, which <a href=\"https://z.cash/upgrade/nu5/\">activated on 31st May 2022</a>:</p>\n-            <ul>\n-                <li><a href=\"zip-0032\">ZIP 32: Shielded Hierarchical Deterministic Wallets</a> (updated)</li>\n-                <li><a href=\"zip-0203\">ZIP 203: Transaction Expiry</a> (updated)</li>\n-                <li><a href=\"zip-0209\">ZIP 209: Prohibit Negative Shielded Chain Value Pool Balances</a> (updated)</li>\n-                <li><a href=\"zip-0212\">ZIP 212: Allow Recipient to Derive Ephemeral Secret from Note Plaintext</a> (updated)</li>\n-                <li><a href=\"zip-0213\">ZIP 213: Shielded Coinbase</a> (updated)</li>\n-                <li><a href=\"zip-0216\">ZIP 216: Require Canonical Jubjub Point Encodings</a></li>\n-                <li><a href=\"zip-0221\">ZIP 221: FlyClient - Consensus-Layer Changes</a> (updated)</li>\n-                <li><a href=\"zip-0224\">ZIP 224: Orchard Shielded Protocol</a></li>\n-                <li><a href=\"zip-0225\">ZIP 225: Version 5 Transaction Format</a></li>\n-                <li><a href=\"zip-0239\">ZIP 239: Relay of Version 5 Transactions</a></li>\n-                <li><a href=\"zip-0244\">ZIP 244: Transaction Identifier Non-Malleability</a></li>\n-                <li><a href=\"zip-0252\">ZIP 252: Deployment of the NU5 Network Upgrade</a></li>\n-                <li><a href=\"zip-0316\">ZIP 316: Unified Addresses and Unified Viewing Keys</a></li>\n-                <li><a href=\"zip-0401\">ZIP 401: Addressing Mempool Denial-of-Service</a> (clarified)</li>\n-            </ul>\n-        </section>\n         <section id=\"license\"><h2><span class=\"section-heading\">License</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#license\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>Unless otherwise stated in this repository’s individual files, the contents of this repository are released under the terms of the MIT license. See <a href=\"COPYING\">COPYING</a> for more information or see <a href=\"https://opensource.org/licenses/MIT\">https://opensource.org/licenses/MIT</a> .</p>\n         </section>\n         <section id=\"index-of-zips\"><h2><span class=\"section-heading\">Index of ZIPs</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#index-of-zips\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n         <embed><table>\n   <tr> <th>ZIP</th> <th>Title</th> <th>Status</th> </tr>\n-  <tr> <td>0</td> <td class=\"left\"><a href=\"zip-0000\">ZIP Process</a></td> <td>Active</td>\n-  <tr> <td><span class=\"reserved\">1</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0001\">Network Upgrade Policy and Scheduling</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">2</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0002\">Design Considerations for Network Upgrades</a></td> <td>Reserved</td>\n-  <tr> <td>32</td> <td class=\"left\"><a href=\"zip-0032\">Shielded Hierarchical Deterministic Wallets</a></td> <td>Final</td>\n-  <tr> <td><span class=\"reserved\">76</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0076\">Transaction Signature Validation before Overwinter</a></td> <td>Reserved</td>\n-  <tr> <td>143</td> <td class=\"left\"><a href=\"zip-0143\">Transaction Signature Validation for Overwinter</a></td> <td>Final</td>\n-  <tr> <td>155</td> <td class=\"left\"><a href=\"zip-0155\">addrv2 message</a></td> <td>Proposed</td>\n-  <tr> <td>173</td> <td class=\"left\"><a href=\"zip-0173\">Bech32 Format</a></td> <td>Final</td>\n-  <tr> <td>200</td> <td class=\"left\"><a href=\"zip-0200\">Network Upgrade Mechanism</a></td> <td>Final</td>\n-  <tr> <td>201</td> <td class=\"left\"><a href=\"zip-0201\">Network Peer Management for Overwinter</a></td> <td>Final</td>\n-  <tr> <td>202</td> <td class=\"left\"><a href=\"zip-0202\">Version 3 Transaction Format for Overwinter</a></td> <td>Final</td>\n-  <tr> <td>203</td> <td class=\"left\"><a href=\"zip-0203\">Transaction Expiry</a></td> <td>Final</td>\n-  <tr> <td><span class=\"reserved\">204</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0204\">Zcash P2P Network Protocol</a></td> <td>Reserved</td>\n-  <tr> <td>205</td> <td class=\"left\"><a href=\"zip-0205\">Deployment of the Sapling Network Upgrade</a></td> <td>Final</td>\n-  <tr> <td>206</td> <td class=\"left\"><a href=\"zip-0206\">Deployment of the Blossom Network Upgrade</a></td> <td>Final</td>\n-  <tr> <td>207</td> <td class=\"left\"><a href=\"zip-0207\">Funding Streams</a></td> <td>Final</td>\n-  <tr> <td>208</td> <td class=\"left\"><a href=\"zip-0208\">Shorter Block Target Spacing</a></td> <td>Final</td>\n-  <tr> <td>209</td> <td class=\"left\"><a href=\"zip-0209\">Prohibit Negative Shielded Chain Value Pool Balances</a></td> <td>Final</td>\n-  <tr> <td><strike>210</strike></td> <td class=\"left\"><strike><a href=\"zip-0210\">Sapling Anchor Deduplication within Transactions</a></strike></td> <td>Withdrawn</td>\n-  <tr> <td>211</td> <td class=\"left\"><a href=\"zip-0211\">Disabling Addition of New Value to the Sprout Chain Value Pool</a></td> <td>Final</td>\n-  <tr> <td>212</td> <td class=\"left\"><a href=\"zip-0212\">Allow Recipient to Derive Ephemeral Secret from Note Plaintext</a></td> <td>Final</td>\n-  <tr> <td>213</td> <td class=\"left\"><a href=\"zip-0213\">Shielded Coinbase</a></td> <td>Final</td>\n-  <tr> <td>214</td> <td class=\"left\"><a href=\"zip-0214\">Consensus rules for a Zcash Development Fund</a></td> <td>Final</td>\n-  <tr> <td>215</td> <td class=\"left\"><a href=\"zip-0215\">Explicitly Defining and Modifying Ed25519 Validation Rules</a></td> <td>Final</td>\n-  <tr> <td>216</td> <td class=\"left\"><a href=\"zip-0216\">Require Canonical Jubjub Point Encodings</a></td> <td>Final</td>\n-  <tr> <td><span class=\"reserved\">217</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0217\">Aggregate Signatures</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">219</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0219\">Disabling Addition of New Value to the Sapling Chain Value Pool</a></td> <td>Reserved</td>\n-  <tr> <td><strike>220</strike></td> <td class=\"left\"><strike><a href=\"zip-0220\">Zcash Shielded Assets</a></strike></td> <td>Withdrawn</td>\n-  <tr> <td>221</td> <td class=\"left\"><a href=\"zip-0221\">FlyClient - Consensus-Layer Changes</a></td> <td>Final</td>\n-  <tr> <td>222</td> <td class=\"left\"><a href=\"zip-0222\">Transparent Zcash Extensions</a></td> <td>Draft</td>\n-  <tr> <td>224</td> <td class=\"left\"><a href=\"zip-0224\">Orchard Shielded Protocol</a></td> <td>Final</td>\n-  <tr> <td>225</td> <td class=\"left\"><a href=\"zip-0225\">Version 5 Transaction Format</a></td> <td>Final</td>\n-  <tr> <td>226</td> <td class=\"left\"><a href=\"zip-0226\">Transfer and Burn of Zcash Shielded Assets</a></td> <td>Draft</td>\n-  <tr> <td>227</td> <td class=\"left\"><a href=\"zip-0227\">Issuance of Zcash Shielded Assets</a></td> <td>Draft</td>\n-  <tr> <td><span class=\"reserved\">228</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0228\">Asset Swaps for Zcash Shielded Assets</a></td> <td>Reserved</td>\n-  <tr> <td>230</td> <td class=\"left\"><a href=\"zip-0230\">Version 6 Transaction Format</a></td> <td>Draft</td>\n-  <tr> <td><span class=\"reserved\">231</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0231\">Decouple Memos from Transaction Outputs</a></td> <td>Reserved</td>\n-  <tr> <td>239</td> <td class=\"left\"><a href=\"zip-0239\">Relay of Version 5 Transactions</a></td> <td>Final</td>\n-  <tr> <td>243</td> <td class=\"left\"><a href=\"zip-0243\">Transaction Signature Validation for Sapling</a></td> <td>Final</td>\n-  <tr> <td>244</td> <td class=\"left\"><a href=\"zip-0244\">Transaction Identifier Non-Malleability</a></td> <td>Final</td>\n-  <tr> <td>245</td> <td class=\"left\"><a href=\"zip-0245\">Transaction Identifier Digests & Signature Validation for Transparent Zcash Extensions</a></td> <td>Draft</td>\n-  <tr> <td>250</td> <td class=\"left\"><a href=\"zip-0250\">Deployment of the Heartwood Network Upgrade</a></td> <td>Final</td>\n-  <tr> <td>251</td> <td class=\"left\"><a href=\"zip-0251\">Deployment of the Canopy Network Upgrade</a></td> <td>Final</td>\n-  <tr> <td>252</td> <td class=\"left\"><a href=\"zip-0252\">Deployment of the NU5 Network Upgrade</a></td> <td>Final</td>\n-  <tr> <td>300</td> <td class=\"left\"><a href=\"zip-0300\">Cross-chain Atomic Transactions</a></td> <td>Proposed</td>\n-  <tr> <td>301</td> <td class=\"left\"><a href=\"zip-0301\">Zcash Stratum Protocol</a></td> <td>Final</td>\n-  <tr> <td>302</td> <td class=\"left\"><a href=\"zip-0302\">Standardized Memo Field Format</a></td> <td>Draft</td>\n-  <tr> <td><span class=\"reserved\">303</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0303\">Sprout Payment Disclosure</a></td> <td>Reserved</td>\n-  <tr> <td>304</td> <td class=\"left\"><a href=\"zip-0304\">Sapling Address Signatures</a></td> <td>Draft</td>\n-  <tr> <td><span class=\"reserved\">305</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0305\">Best Practices for Hardware Wallets supporting Sapling</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">306</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0306\">Security Considerations for Anchor Selection</a></td> <td>Reserved</td>\n-  <tr> <td>307</td> <td class=\"left\"><a href=\"zip-0307\">Light Client Protocol for Payment Detection</a></td> <td>Draft</td>\n-  <tr> <td>308</td> <td class=\"left\"><a href=\"zip-0308\">Sprout to Sapling Migration</a></td> <td>Final</td>\n-  <tr> <td><span class=\"reserved\">309</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0309\">Blind Off-chain Lightweight Transactions (BOLT)</a></td> <td>Reserved</td>\n-  <tr> <td>310</td> <td class=\"left\"><a href=\"zip-0310\">Security Properties of Sapling Viewing Keys</a></td> <td>Draft</td>\n-  <tr> <td><span class=\"reserved\">311</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0311\">Sapling Payment Disclosure</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">312</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0312\">Shielded Multisignatures using FROST</a></td> <td>Reserved</td>\n-  <tr> <td><strike>313</strike></td> <td class=\"left\"><strike><a href=\"zip-0313\">Reduce Conventional Transaction Fee to 1000 zatoshis</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><span class=\"reserved\">314</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0314\">Privacy upgrades to the Zcash light client protocol</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">315</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0315\">Best Practices for Wallet Handling of Multiple Pools</a></td> <td>Reserved</td>\n-  <tr> <td>316</td> <td class=\"left\"><a href=\"zip-0316\">Unified Addresses and Unified Viewing Keys</a></td> <td>Revision 0: Final, Revision 1: Proposed</td>\n-  <tr> <td>317</td> <td class=\"left\"><a href=\"zip-0317\">Proportional Transfer Fee Mechanism</a></td> <td>Active</td>\n-  <tr> <td><span class=\"reserved\">318</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0318\">Associated Payload Encryption</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">319</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0319\">Options for Shielded Pool Retirement</a></td> <td>Reserved</td>\n-  <tr> <td>320</td> <td class=\"left\"><a href=\"zip-0320\">Defining an Address Type to which funds can only be sent from Transparent Addresses</a></td> <td>Draft</td>\n-  <tr> <td>321</td> <td class=\"left\"><a href=\"zip-0321\">Payment Request URIs</a></td> <td>Proposed</td>\n-  <tr> <td><span class=\"reserved\">322</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0322\">Generic Signed Message Format</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">323</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0323\">Specification of getblocktemplate for Zcash</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">332</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0332\">Wallet Recovery from zcashd HD Seeds</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">339</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0339\">Wallet Recovery Words</a></td> <td>Reserved</td>\n-  <tr> <td>400</td> <td class=\"left\"><a href=\"zip-0400\">Wallet.dat format</a></td> <td>Draft</td>\n-  <tr> <td>401</td> <td class=\"left\"><a href=\"zip-0401\">Addressing Mempool Denial-of-Service</a></td> <td>Active</td>\n-  <tr> <td><span class=\"reserved\">402</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0402\">New Wallet Database Format</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">403</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0403\">Verification Behaviour of zcashd</a></td> <td>Reserved</td>\n-  <tr> <td><span class=\"reserved\">416</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zip-0416\">Support for Unified Addresses in zcashd</a></td> <td>Reserved</td>\n-  <tr> <td><strike>1001</strike></td> <td class=\"left\"><strike><a href=\"zip-1001\">Keep the Block Distribution as Initially Defined — 90% to Miners</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1002</strike></td> <td class=\"left\"><strike><a href=\"zip-1002\">Opt-in Donation Feature</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1003</strike></td> <td class=\"left\"><strike><a href=\"zip-1003\">20% Split Evenly Between the ECC and the Zcash Foundation, and a Voting System Mandate</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1004</strike></td> <td class=\"left\"><strike><a href=\"zip-1004\">Miner-Directed Dev Fund</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1005</strike></td> <td class=\"left\"><strike><a href=\"zip-1005\">Zcash Community Funding System</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1006</strike></td> <td class=\"left\"><strike><a href=\"zip-1006\">Development Fund of 10% to a 2-of-3 Multisig with Community-Involved Third Entity</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1007</strike></td> <td class=\"left\"><strike><a href=\"zip-1007\">Enforce Development Fund Commitments with a Legal Charter</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1008</strike></td> <td class=\"left\"><strike><a href=\"zip-1008\">Fund ECC for Two More Years</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1009</strike></td> <td class=\"left\"><strike><a href=\"zip-1009\">Five-Entity Strategic Council</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1010</strike></td> <td class=\"left\"><strike><a href=\"zip-1010\">Compromise Dev Fund Proposal With Diverse Funding Streams</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1011</strike></td> <td class=\"left\"><strike><a href=\"zip-1011\">Decentralize the Dev Fee</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1012</strike></td> <td class=\"left\"><strike><a href=\"zip-1012\">Dev Fund to ECC + ZF + Major Grants</a></strike></td> <td>Obsolete</td>\n-  <tr> <td><strike>1013</strike></td> <td class=\"left\"><strike><a href=\"zip-1013\">Keep It Simple, Zcashers: 10% to ECC, 10% to ZF</a></strike></td> <td>Obsolete</td>\n-  <tr> <td>1014</td> <td class=\"left\"><a href=\"zip-1014\">Establishing a Dev Fund for ECC, ZF, and Major Grants</a></td> <td>Active</td>\n-  <tr> <td>guide</td> <td class=\"left\"><a href=\"zip-guide\">{Something Short and To the Point}</a></td> <td>Draft</td>\n+  <tr> <td>0</td> <td class=\"left\"><a href=\"./zip-0000\">ZIP Process</a></td> <td>Active</td>\n+  <tr> <td><span class=\"reserved\">1</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0001\">Network Upgrade Policy and Scheduling</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">2</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0002\">Design Considerations for Network Upgrades</a></td> <td>Reserved</td>\n+  <tr> <td>32</td> <td class=\"left\"><a href=\"./zip-0032\">Shielded Hierarchical Deterministic Wallets</a></td> <td>Final</td>\n+  <tr> <td><span class=\"reserved\">76</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0076\">Transaction Signature Validation before Overwinter</a></td> <td>Reserved</td>\n+  <tr> <td>143</td> <td class=\"left\"><a href=\"./zip-0143\">Transaction Signature Validation for Overwinter</a></td> <td>Final</td>\n+  <tr> <td>155</td> <td class=\"left\"><a href=\"./zip-0155\">addrv2 message</a></td> <td>Proposed</td>\n+  <tr> <td>173</td> <td class=\"left\"><a href=\"./zip-0173\">Bech32 Format</a></td> <td>Final</td>\n+  <tr> <td>200</td> <td class=\"left\"><a href=\"./zip-0200\">Network Upgrade Mechanism</a></td> <td>Final</td>\n+  <tr> <td>201</td> <td class=\"left\"><a href=\"./zip-0201\">Network Peer Management for Overwinter</a></td> <td>Final</td>\n+  <tr> <td>202</td> <td class=\"left\"><a href=\"./zip-0202\">Version 3 Transaction Format for Overwinter</a></td> <td>Final</td>\n+  <tr> <td>203</td> <td class=\"left\"><a href=\"./zip-0203\">Transaction Expiry</a></td> <td>Final</td>\n+  <tr> <td><span class=\"reserved\">204</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0204\">Zcash P2P Network Protocol</a></td> <td>Reserved</td>\n+  <tr> <td>205</td> <td class=\"left\"><a href=\"./zip-0205\">Deployment of the Sapling Network Upgrade</a></td> <td>Final</td>\n+  <tr> <td>206</td> <td class=\"left\"><a href=\"./zip-0206\">Deployment of the Blossom Network Upgrade</a></td> <td>Final</td>\n+  <tr> <td>207</td> <td class=\"left\"><a href=\"./zip-0207\">Funding Streams</a></td> <td>Final</td>\n+  <tr> <td>208</td> <td class=\"left\"><a href=\"./zip-0208\">Shorter Block Target Spacing</a></td> <td>Final</td>\n+  <tr> <td>209</td> <td class=\"left\"><a href=\"./zip-0209\">Prohibit Negative Shielded Chain Value Pool Balances</a></td> <td>Final</td>\n+  <tr> <td><strike>210</strike></td> <td class=\"left\"><strike><a href=\"./zip-0210\">Sapling Anchor Deduplication within Transactions</a></strike></td> <td>Withdrawn</td>\n+  <tr> <td>211</td> <td class=\"left\"><a href=\"./zip-0211\">Disabling Addition of New Value to the Sprout Chain Value Pool</a></td> <td>Final</td>\n+  <tr> <td>212</td> <td class=\"left\"><a href=\"./zip-0212\">Allow Recipient to Derive Ephemeral Secret from Note Plaintext</a></td> <td>Final</td>\n+  <tr> <td>213</td> <td class=\"left\"><a href=\"./zip-0213\">Shielded Coinbase</a></td> <td>Final</td>\n+  <tr> <td>214</td> <td class=\"left\"><a href=\"./zip-0214\">Consensus rules for a Zcash Development Fund</a></td> <td>Final</td>\n+  <tr> <td>215</td> <td class=\"left\"><a href=\"./zip-0215\">Explicitly Defining and Modifying Ed25519 Validation Rules</a></td> <td>Final</td>\n+  <tr> <td>216</td> <td class=\"left\"><a href=\"./zip-0216\">Require Canonical Jubjub Point Encodings</a></td> <td>Final</td>\n+  <tr> <td><span class=\"reserved\">217</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0217\">Aggregate Signatures</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">219</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0219\">Disabling Addition of New Value to the Sapling Chain Value Pool</a></td> <td>Reserved</td>\n+  <tr> <td><strike>220</strike></td> <td class=\"left\"><strike><a href=\"./zip-0220\">Zcash Shielded Assets</a></strike></td> <td>Withdrawn</td>\n+  <tr> <td>221</td> <td class=\"left\"><a href=\"./zip-0221\">FlyClient - Consensus-Layer Changes</a></td> <td>Final</td>\n+  <tr> <td>222</td> <td class=\"left\"><a href=\"./zip-0222\">Transparent Zcash Extensions</a></td> <td>Draft</td>\n+  <tr> <td>224</td> <td class=\"left\"><a href=\"./zip-0224\">Orchard Shielded Protocol</a></td> <td>Final</td>\n+  <tr> <td>225</td> <td class=\"left\"><a href=\"./zip-0225\">Version 5 Transaction Format</a></td> <td>Final</td>\n+  <tr> <td>226</td> <td class=\"left\"><a href=\"./zip-0226\">Transfer and Burn of Zcash Shielded Assets</a></td> <td>Draft</td>\n+  <tr> <td>227</td> <td class=\"left\"><a href=\"./zip-0227\">Issuance of Zcash Shielded Assets</a></td> <td>Draft</td>\n+  <tr> <td><span class=\"reserved\">228</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0228\">Asset Swaps for Zcash Shielded Assets</a></td> <td>Reserved</td>\n+  <tr> <td>230</td> <td class=\"left\"><a href=\"./zip-0230\">Version 6 Transaction Format</a></td> <td>Draft</td>\n+  <tr> <td><span class=\"reserved\">231</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0231\">Decouple Memos from Transaction Outputs</a></td> <td>Reserved</td>\n+  <tr> <td>239</td> <td class=\"left\"><a href=\"./zip-0239\">Relay of Version 5 Transactions</a></td> <td>Final</td>\n+  <tr> <td>243</td> <td class=\"left\"><a href=\"./zip-0243\">Transaction Signature Validation for Sapling</a></td> <td>Final</td>\n+  <tr> <td>244</td> <td class=\"left\"><a href=\"./zip-0244\">Transaction Identifier Non-Malleability</a></td> <td>Final</td>\n+  <tr> <td>245</td> <td class=\"left\"><a href=\"./zip-0245\">Transaction Identifier Digests & Signature Validation for Transparent Zcash Extensions</a></td> <td>Draft</td>\n+  <tr> <td>250</td> <td class=\"left\"><a href=\"./zip-0250\">Deployment of the Heartwood Network Upgrade</a></td> <td>Final</td>\n+  <tr> <td>251</td> <td class=\"left\"><a href=\"./zip-0251\">Deployment of the Canopy Network Upgrade</a></td> <td>Final</td>\n+  <tr> <td>252</td> <td class=\"left\"><a href=\"./zip-0252\">Deployment of the NU5 Network Upgrade</a></td> <td>Final</td>\n+  <tr> <td><span class=\"reserved\">253</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0253\">Deployment of the NU6 Network Upgrade</a></td> <td>Reserved</td>\n+  <tr> <td>300</td> <td class=\"left\"><a href=\"./zip-0300\">Cross-chain Atomic Transactions</a></td> <td>Proposed</td>\n+  <tr> <td>301</td> <td class=\"left\"><a href=\"./zip-0301\">Zcash Stratum Protocol</a></td> <td>Final</td>\n+  <tr> <td>302</td> <td class=\"left\"><a href=\"./zip-0302\">Standardized Memo Field Format</a></td> <td>Draft</td>\n+  <tr> <td><span class=\"reserved\">303</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0303\">Sprout Payment Disclosure</a></td> <td>Reserved</td>\n+  <tr> <td>304</td> <td class=\"left\"><a href=\"./zip-0304\">Sapling Address Signatures</a></td> <td>Draft</td>\n+  <tr> <td><span class=\"reserved\">305</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0305\">Best Practices for Hardware Wallets supporting Sapling</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">306</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0306\">Security Considerations for Anchor Selection</a></td> <td>Reserved</td>\n+  <tr> <td>307</td> <td class=\"left\"><a href=\"./zip-0307\">Light Client Protocol for Payment Detection</a></td> <td>Draft</td>\n+  <tr> <td>308</td> <td class=\"left\"><a href=\"./zip-0308\">Sprout to Sapling Migration</a></td> <td>Final</td>\n+  <tr> <td><span class=\"reserved\">309</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0309\">Blind Off-chain Lightweight Transactions (BOLT)</a></td> <td>Reserved</td>\n+  <tr> <td>310</td> <td class=\"left\"><a href=\"./zip-0310\">Security Properties of Sapling Viewing Keys</a></td> <td>Draft</td>\n+  <tr> <td><span class=\"reserved\">311</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0311\">Sapling Payment Disclosure</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">312</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0312\">Shielded Multisignatures using FROST</a></td> <td>Reserved</td>\n+  <tr> <td><strike>313</strike></td> <td class=\"left\"><strike><a href=\"./zip-0313\">Reduce Conventional Transaction Fee to 1000 zatoshis</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><span class=\"reserved\">314</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0314\">Privacy upgrades to the Zcash light client protocol</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">315</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0315\">Best Practices for Wallet Handling of Multiple Pools</a></td> <td>Reserved</td>\n+  <tr> <td>316</td> <td class=\"left\"><a href=\"./zip-0316\">Unified Addresses and Unified Viewing Keys</a></td> <td>Revision 0: Final, Revision 1: Proposed</td>\n+  <tr> <td>317</td> <td class=\"left\"><a href=\"./zip-0317\">Proportional Transfer Fee Mechanism</a></td> <td>Active</td>\n+  <tr> <td><span class=\"reserved\">318</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0318\">Associated Payload Encryption</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">319</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0319\">Options for Shielded Pool Retirement</a></td> <td>Reserved</td>\n+  <tr> <td>320</td> <td class=\"left\"><a href=\"./zip-0320\">Defining an Address Type to which funds can only be sent from Transparent Addresses</a></td> <td>Proposed</td>\n+  <tr> <td>321</td> <td class=\"left\"><a href=\"./zip-0321\">Payment Request URIs</a></td> <td>Proposed</td>\n+  <tr> <td><span class=\"reserved\">322</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0322\">Generic Signed Message Format</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">323</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0323\">Specification of getblocktemplate for Zcash</a></td> <td>Reserved</td>\n+  <tr> <td>324</td> <td class=\"left\"><a href=\"./zip-0324\">URI-Encapsulated Payments</a></td> <td>Draft</td>\n+  <tr> <td><span class=\"reserved\">332</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0332\">Wallet Recovery from zcashd HD Seeds</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">339</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0339\">Wallet Recovery Words</a></td> <td>Reserved</td>\n+  <tr> <td>340</td> <td class=\"left\"><a href=\"./zip-0340.md\"></a></td> <td></td>\n+  <tr> <td>400</td> <td class=\"left\"><a href=\"./zip-0400\">Wallet.dat format</a></td> <td>Draft</td>\n+  <tr> <td>401</td> <td class=\"left\"><a href=\"./zip-0401\">Addressing Mempool Denial-of-Service</a></td> <td>Active</td>\n+  <tr> <td><span class=\"reserved\">402</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0402\">New Wallet Database Format</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">403</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0403\">Verification Behaviour of zcashd</a></td> <td>Reserved</td>\n+  <tr> <td><span class=\"reserved\">416</span></td> <td class=\"left\"><a class=\"reserved\" href=\"./zip-0416\">Support for Unified Addresses in zcashd</a></td> <td>Reserved</td>\n+  <tr> <td><strike>1001</strike></td> <td class=\"left\"><strike><a href=\"./zip-1001\">Keep the Block Distribution as Initially Defined — 90% to Miners</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1002</strike></td> <td class=\"left\"><strike><a href=\"./zip-1002\">Opt-in Donation Feature</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1003</strike></td> <td class=\"left\"><strike><a href=\"./zip-1003\">20% Split Evenly Between the ECC and the Zcash Foundation, and a Voting System Mandate</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1004</strike></td> <td class=\"left\"><strike><a href=\"./zip-1004\">Miner-Directed Dev Fund</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1005</strike></td> <td class=\"left\"><strike><a href=\"./zip-1005\">Zcash Community Funding System</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1006</strike></td> <td class=\"left\"><strike><a href=\"./zip-1006\">Development Fund of 10% to a 2-of-3 Multisig with Community-Involved Third Entity</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1007</strike></td> <td class=\"left\"><strike><a href=\"./zip-1007\">Enforce Development Fund Commitments with a Legal Charter</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1008</strike></td> <td class=\"left\"><strike><a href=\"./zip-1008\">Fund ECC for Two More Years</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1009</strike></td> <td class=\"left\"><strike><a href=\"./zip-1009\">Five-Entity Strategic Council</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1010</strike></td> <td class=\"left\"><strike><a href=\"./zip-1010\">Compromise Dev Fund Proposal With Diverse Funding Streams</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1011</strike></td> <td class=\"left\"><strike><a href=\"./zip-1011\">Decentralize the Dev Fee</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1012</strike></td> <td class=\"left\"><strike><a href=\"./zip-1012\">Dev Fund to ECC + ZF + Major Grants</a></strike></td> <td>Obsolete</td>\n+  <tr> <td><strike>1013</strike></td> <td class=\"left\"><strike><a href=\"./zip-1013\">Keep It Simple, Zcashers: 10% to ECC, 10% to ZF</a></strike></td> <td>Obsolete</td>\n+  <tr> <td>1014</td> <td class=\"left\"><a href=\"./zip-1014\">Establishing a Dev Fund for ECC, ZF, and Major Grants</a></td> <td>Active</td>\n+  <tr> <td>guide</td> <td class=\"left\"><a href=\"./zip-guide\">{Something Short and To the Point}</a></td> <td>Draft</td>\n+</table></embed></section>\n+        <section id=\"drafts-without-assigned-zip-numbers\"><h2><span class=\"section-heading\">Drafts without assigned ZIP numbers</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#drafts-without-assigned-zip-numbers\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <p>These are works-in-progress, and may never be assigned ZIP numbers if their ideas become obsoleted or abandoned. Do not assume that these drafts will exist in perpetuity; instead assume that they will either move to a numbered ZIP, or be deleted.</p>\n+        <embed><table>\n+  <tr> <th>Title</th> </tr>\n </table></embed></section>\n     </section>\n </body>"},{"filename":"protocol/blossom.pdf","status":"modified","additions":0,"deletions":0,"patch":null},{"filename":"protocol/canopy.pdf","status":"modified","additions":0,"deletions":0,"patch":null},{"filename":"protocol/heartwood.pdf","status":"modified","additions":0,"deletions":0,"patch":null},{"filename":"protocol/nu5.pdf","status":"modified","additions":0,"deletions":0,"patch":null},{"filename":"protocol/protocol.pdf","status":"modified","additions":0,"deletions":0,"patch":null},{"filename":"protocol/sapling.pdf","status":"modified","additions":0,"deletions":0,"patch":null},{"filename":"zip-0216.html","status":"modified","additions":2,"deletions":2,"patch":"@@ -29,9 +29,9 @@\n                 <span class=\"math\">\\(u\\!\\)</span>\n             -coordinate is zero, there are two encodings that will be accepted:</p>\n             <pre data-language=\"rust\"><span class=\"c1\">// Fix the sign of `u` if necessary</span>\n-<span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"n\">flip_sign</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"n\">Choice</span>::<span class=\"n\">from</span><span class=\"p\">((</span><span class=\"n\">u</span><span class=\"p\">.</span><span class=\"n\">to_bytes</span><span class=\"p\">()[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">^</span><span class=\"w\"> </span><span class=\"n\">sign</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">&amp;</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"p\">);</span>\n+<span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"n\">flip_sign</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"n\">Choice</span><span class=\"p\">::</span><span class=\"n\">from</span><span class=\"p\">((</span><span class=\"n\">u</span><span class=\"p\">.</span><span class=\"n\">to_bytes</span><span class=\"p\">()[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">^</span><span class=\"w\"> </span><span class=\"n\">sign</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">&amp;</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"n\">u_negated</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"n\">u</span><span class=\"p\">;</span>\n-<span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"n\">final_u</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"n\">Fq</span>::<span class=\"n\">conditional_select</span><span class=\"p\">(</span><span class=\"o\">&amp;</span><span class=\"n\">u</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"o\">&amp;</span><span class=\"n\">u_negated</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">flip_sign</span><span class=\"p\">);</span></pre>\n+<span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"n\">final_u</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"n\">Fq</span><span class=\"p\">::</span><span class=\"n\">conditional_select</span><span class=\"p\">(</span><span class=\"o\">&amp;</span><span class=\"n\">u</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"o\">&amp;</span><span class=\"n\">u_negated</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">flip_sign</span><span class=\"p\">);</span></pre>\n             <p>This code accepts either sign bit, because <code>u_negated == u</code>.</p>\n             <p>There are two points on the Jubjub curve with\n                 <span class=\"math\">\\(u\\!\\)</span>"},{"filename":"zip-0253.html","status":"added","additions":17,"deletions":0,"patch":"@@ -0,0 +1,17 @@\n+<!DOCTYPE html>\n+<html>\n+<head>\n+    <title>ZIP 253: Deployment of the NU6 Network Upgrade</title>\n+    <meta charset=\"utf-8\" />\n+<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"stylesheet\" href=\"css/style.css\"></head>\n+<body>\n+    <section>\n+        <pre>ZIP: 253\n+Title: Deployment of the NU6 Network Upgrade\n+Owners: Daira-Emma Hopwood &lt;daira-emma@electriccoin.co&gt;\n+Status: Reserved\n+Category: Consensus / Network\n+Discussions-To: &lt;<a href=\"https://github.com/zcash/zips/issues/806\">https://github.com/zcash/zips/issues/806</a>&gt;</pre>\n+    </section>\n+</body>\n+</html>\n\\ No newline at end of file"},{"filename":"zip-0316.html","status":"modified","additions":10,"deletions":2,"patch":"@@ -165,8 +165,16 @@\n                     <li>Revision 1: This version adds support for <a href=\"#must-understand-typecodes\">MUST-understand Typecodes</a> and <a href=\"#address-expiration-metadata\">Address Expiration Metadata</a>. It also drops the restriction that a UA/UVK must contain at least one shielded Item.</li>\n                 </ul>\n                 <p>A Revision 0 UA/UVK is distinguished from a Revision 1 UA/UVK by its Human-Readable Part, as follows.</p>\n-                <p>Let <em>prefix</em> be: * “<code>u</code>”, if this is a UA/UVK prior to <a href=\"#revision-1\">Revision 1</a>; * “<code>ur</code>”, if this is a UA/UVK from <a href=\"#revision-1\">Revision 1</a> onward.</p>\n-                <p>The Human-Readable Parts (as defined in <a id=\"footnote-reference-10\" class=\"footnote_reference\" href=\"#bip-0350\">36</a>) of Unified Addresses are defined as: * <em>prefix</em>, for Unified Addresses on Mainnet; * <em>prefix</em> || “<code>test</code>”, for Unified Addresses on Testnet.</p>\n+                <p>Let <em>prefix</em> be:</p>\n+                <ul>\n+                    <li>“<code>u</code>”, if this is a UA/UVK prior to <a href=\"#revision-1\">Revision 1</a>;</li>\n+                    <li>“<code>ur</code>”, if this is a UA/UVK from <a href=\"#revision-1\">Revision 1</a> onward.</li>\n+                </ul>\n+                <p>The Human-Readable Parts (as defined in <a id=\"footnote-reference-10\" class=\"footnote_reference\" href=\"#bip-0350\">36</a>) of Unified Addresses are defined as:</p>\n+                <ul>\n+                    <li><em>prefix</em>, for Unified Addresses on Mainnet;</li>\n+                    <li><em>prefix</em> || “<code>test</code>”, for Unified Addresses on Testnet.</li>\n+                </ul>\n                 <p>The Human-Readable Parts of Unified Viewing Keys are defined as:</p>\n                 <ul>\n                     <li><em>prefix</em> || “<code>ivk</code>” for Unified Incoming Viewing Keys on Mainnet;</li>"},{"filename":"zip-0320.html","status":"modified","additions":1,"deletions":1,"patch":"@@ -12,7 +12,7 @@\n Owners: Daira-Emma Hopwood &lt;daira@electriccoin.co&gt;\n         Kris Nuttycombe &lt;kris@nutty.land&gt;\n Credits: Hanh\n-Status: Draft\n+Status: Proposed\n Category: Standards / Wallet\n Created: 2024-01-12\n License: MIT"},{"filename":"zip-0324.html","status":"modified","additions":32,"deletions":32,"patch":"@@ -25,16 +25,16 @@\n Category: Standards / Wallet\n Created: 2019-07-17\n License: MIT</pre>\n-        <section id=\"terminology\"><h2><span class=\"section-heading\">Terminology</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#terminology\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"terminology\"><h2><span class=\"section-heading\">Terminology</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#terminology\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to be interpreted as described in BCP 14 <a id=\"footnote-reference-1\" class=\"footnote_reference\" href=\"#bcp14\">1</a> when, and only when, they appear in all capitals.</p>\n             <p>Zcash protocol terms are as defined in the Zcash Protocol Specification. <a id=\"footnote-reference-2\" class=\"footnote_reference\" href=\"#protocol\">3</a></p>\n             <p id=\"applink\">By “applink” we mean a platform mechanism for triggering local applications using HTTPS links, such as App Links <a id=\"footnote-reference-3\" class=\"footnote_reference\" href=\"#app-links\">5</a> on Android, Universal Links <a id=\"footnote-reference-4\" class=\"footnote_reference\" href=\"#universal-links\">6</a> on iOS, or App URI handlers <a id=\"footnote-reference-5\" class=\"footnote_reference\" href=\"#app-uri-handlers\">7</a> on Windows.</p>\n         </section>\n-        <section id=\"abstract\"><h2><span class=\"section-heading\">Abstract</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#abstract\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"abstract\"><h2><span class=\"section-heading\">Abstract</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#abstract\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>This proposal specifies a mechanism for sending Zcash payments via any unmodified (secure) messaging service, e.g., Signal or WhatsApp. The sender need not know the recipient's address and the recipient need not have a Zcash wallet already installed. Payments occur via an ordinary text/WhatsApp/Signal message containing a URI. The URI encodes the secret spending key of an ephemeral Zcash “wallet address”, to which the funds have been transferred. Anyone who learns the URI can accept this payment, by a “finalization” process which uses the key given in the URI to transfer the encapsulated funds to their own wallet. After the payment is finalized, via a suitable on-chain transaction by the recipient, it becomes irrevocable.</p>\n             <p>The proposal specifies the syntax and semantics of URI-Encapsulated Payments, the workflow of generating and handling them, and requisite infrastructure.</p>\n             <p>At its core, a URI encapsulated payment communicates the existence of a transaction (specifically a note committing to an amount of funds) to a receiving client. The URI encodes the amount of the payment and a key used to derive all necessary randomness for note construction including the address and secret key needed to spend it.</p>\n-            <section id=\"usage-story\"><h3><span class=\"section-heading\">Usage Story</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#usage-story\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"usage-story\"><h3><span class=\"section-heading\">Usage Story</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#usage-story\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>Alice wants to send a 1.23 ZEC payment to Bob. She doesn't have Bob's Zcash address — and Bob may not even have a wallet or have heard of Zcash! However, she does have some end-to-end encrypted channel to Bob on a secure messaging app, such as Signal or WhatsApp. She instructs her own Zcash wallet to send 1.23 ZEC on that channel. Her wallet then automatically generates a new ephemeral Zcash address and sends 1.23001 ZEC to this address. It then constructs a payment URI containing the secret key corresponding to the ephemeral address, and sends it to Bob via the secure messaging app.</p>\n                 <p>Bob receives the message, which looks as follows:</p>\n                 <blockquote>\n@@ -45,15 +45,15 @@\n                 <p>Bob clicks the link. His Zcash mobile wallet app (which is already installed and has configured itself as a handler for URLs of that form) shows up, and tells Bob that a payment of 1.23 ZEC awaits him. The wallet app confirms the existence of the pertinent transactions on the blockchain, and then offers to finalize the payment. Bob clicks the “Finalize” button, and his wallet app generates a transaction moving the money to his own address (using the extra 0.00001 ZEC he has received to pay the transaction fee). When this transaction is confirmed on-chain, Alice's and Bob's wallets both indicate that the payment is finalized, and thus Bob can send the funds to other parties.</p>\n             </section>\n         </section>\n-        <section id=\"motivation\"><h2><span class=\"section-heading\">Motivation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#motivation\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"motivation\"><h2><span class=\"section-heading\">Motivation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#motivation\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>This proposal enables sending of funds without exposing users to the notion of payment addresses and their secure distribution. Instead, funds can be sent using any pre-existing communication channel, by a single message sent unidirectionally from the sender to the recipient. This message conveys all the information needed to obtain control of the funds, compactly expressed as a textual URI.</p>\n             <p>Consequently, all functionality related to contact discovery and secure-channel establishment can be delegated to the message app(s) with which the user is already familiar, and in which the user has already established communication channels to many of their contacts.</p>\n             <p>Moreover, funds can be sent to users who have not yet installed wallet software and who do not yet have a payment address. The recipient can collect the funds at any later time by installing suitable software.</p>\n             <p>Additionally, the proposal greatly improves the scalability of Zcash. If a recipient only receives URI payments, they need not scan the blockchain or delegate their view keys to a third party to do so.</p>\n             <p>Finally, this avoids the fat-finger problem of sending funds to the wrong address. The user sends funds via a known contact on, e.g., WhatsApp, who they have a relationship with or at least can confirm the recipient's identity. Moreover, even once funds are sent via a URI, they can be recovered if the other party does not claim them.</p>\n             <p>The proposal is complementary to ZIP 321 <a id=\"footnote-reference-6\" class=\"footnote_reference\" href=\"#zip-0321\">4</a>, which will standardize <em>Payment Request URIs</em> using which the payment <em>recipient</em> can convey their persistent payment address to the <em>sender</em>, for subsequent fund transfers (to be done using the normal on-chain mechanism rather than the encapsulated payments described in the current proposal).</p>\n         </section>\n-        <section id=\"requirements\"><h2><span class=\"section-heading\">Requirements</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#requirements\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"requirements\"><h2><span class=\"section-heading\">Requirements</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#requirements\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>This proposal's specification of URI-Encapsulated Payments, and the intended protocols for using them, is meant to fulfill the following requirements:</p>\n             <ul>\n                 <li>The protocol must not require the sender of a payment to stay online until the recipient receives the URI (let alone finalizes the payment).</li>\n@@ -66,15 +66,15 @@\n                 <li>Don't lose funds, even if wallets crash, or everything but the sending wallet master secret is lost.</li>\n             </ul>\n         </section>\n-        <section id=\"non-requirements\"><h2><span class=\"section-heading\">Non-requirements</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#non-requirements\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"non-requirements\"><h2><span class=\"section-heading\">Non-requirements</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#non-requirements\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <ul>\n                 <li>It is outside the scope of this proposal to establish a secure communication channel for transmission of URI-Encapsulated Payments, or to protect the parties' devices from security compromise.</li>\n                 <li>Finalizing the payment may involve significant wait times, on the scale of minutes, as the requisite on-chain transactions are generated, mined and confirmed. This proposal does not try to solve this (though it does try to avoid imposing significant additional delays, and it does address how the intermediate state is conveyed to the user).</li>\n             </ul>\n         </section>\n-        <section id=\"specification\"><h2><span class=\"section-heading\">Specification</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#specification\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"specification\"><h2><span class=\"section-heading\">Specification</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#specification\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>A Payment-Encapsulating URI represents the capability to claim the Zcash funds from specific on-chain transactions, as long as they're unspent. See <a href=\"#usage-story\">Usage Story</a> for an example.</p>\n-            <section id=\"syntax\"><h3><span class=\"section-heading\">Syntax</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#syntax\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"syntax\"><h3><span class=\"section-heading\">Syntax</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#syntax\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>A Payment-Encapsulating URI is a Universal Resource Locator (URL), as defined in RFC 3986 <a id=\"footnote-reference-7\" class=\"footnote_reference\" href=\"#rfc3986\">2</a>, of the following form.</p>\n                 <p>Scheme: <code>https</code></p>\n                 <p>Host: <code>pay.withzcash.com</code></p>\n@@ -87,7 +87,7 @@\n                     <li><code>key=</code> is a 256-bit random number encoded with Bech32 as specified in Section 5.6.9 of the Zcash Protocol Specification <a id=\"footnote-reference-8\" class=\"footnote_reference\" href=\"#protocol\">3</a>). MUST be present.</li>\n                 </ul>\n             </section>\n-            <section id=\"semantics\"><h3><span class=\"section-heading\">Semantics</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#semantics\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"semantics\"><h3><span class=\"section-heading\">Semantics</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#semantics\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The values of <code>key</code> and <code>amount</code> deterministically imply a unique <em>payment note</em> corresponding to this URI, which is a Zcash Sapling note that carries the given amount and is spendable by a Sapling spending key derived from <code>key</code>. The derivation of this note is done by the following procedure:</p>\n                 <p><em>DerivePaymentNote(key,amount)</em>:</p>\n                 <ul>\n@@ -119,7 +119,7 @@\n                 <p>The recipient's wallet software SHOULD convey to the user that the <code>desc</code> value is merely a claim made by the party who sent the URI, and may be tentative, inaccurate or malicious.</p>\n                 <p>In particular, the recipient's wallet software SHOULD convey to the user that the amount of ZEC they can successfully transfer to their wallet may be different than that given by the <code>amount</code> parameter, and may change (possibly to zero), until the finalization process has been completed.</p>\n             </section>\n-            <section id=\"centralized-deployment\"><h3><span class=\"section-heading\">Centralized Deployment</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#centralized-deployment\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"centralized-deployment\"><h3><span class=\"section-heading\">Centralized Deployment</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#centralized-deployment\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The owner of the <code>withzcash.com</code> domain name MUST NOT create a DNS record for the <code>pay.withzcash.com</code> domain name, nor a TLS certificate for it. All feasible means SHOULD be taken to ensure this, and to prevent unintended transfer of ownership or control over the <code>withzcash.com</code> domain name. (See <a href=\"#rationale-for-uri-format\">Rationale for URI Format</a> and <a href=\"#security-considerations\">Security Considerations</a> below for discussion.)</p>\n                 <p><a href=\"#applink\">Applink</a> mechanisms let domain name owners provide a whitelist, specifying which apps are authorized to handle URLs with that domain name. This is implemented by serving suitable files at well-known paths on the web server of that domain or, in the case of a subdomain, its parent domain. Thus, the owner of the <code>withzcash.com</code> domain effectively controls the whitelist of apps that may be launched by users' platform to handle URI-Encapsulated Payments (see <a href=\"#security-considerations\">Security Considerations</a>). This whitelist should protect users from installing rogue apps that intercept incoming payments. Thus, the domain owner MUST do the following:</p>\n                 <ul>\n@@ -136,17 +136,17 @@\n                     <li>Strive for the whitelist to include all apps that would not place the user at any greater security risk than reputable state-of-the-art wallet apps.</li>\n                 </ul>\n             </section>\n-            <section id=\"testing\"><h3><span class=\"section-heading\">Testing</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#testing\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"testing\"><h3><span class=\"section-heading\">Testing</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#testing\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>For testing purposes, all of the above specification is duplicated for the Zcash testnet network, substituting <code>TAZ</code> (Zcash testnet coins) for <code>ZEC</code> and <code>testzcash.com</code> for <code>withzcash.com</code>.</p>\n                 <p>A separate “testnet whitelist” MUST be maintained by the owner of the <code>testzcash.com</code> domain name, with a separate policy that SHOULD allow any legitimate third-party developer to add their work-in-progress wallet for testing purposes. Integrity and availability MAY be looser.</p>\n                 <p>Wallets apps MAY support just one type of payments (ZEC or TAZ), and if they support both then they MUST keep separate accounting and must clearly distinguish the type when payments or balances are conveyed to users.</p>\n             </section>\n         </section>\n-        <section id=\"lifecycle-specification\"><h2><span class=\"section-heading\">Lifecycle Specification</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#lifecycle-specification\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"lifecycle-specification\"><h2><span class=\"section-heading\">Lifecycle Specification</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#lifecycle-specification\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <p>The lifecycle of a Payment-Encapsulating URI consists of several stages, which in the usual case culminate in the funds being irrevocably deposited into the recipient's personal wallet irrevocably:</p>\n-            <section id=\"generating-the-notes-and-uri\"><h3><span class=\"section-heading\">Generating the notes and URI</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#generating-the-notes-and-uri\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"generating-the-notes-and-uri\"><h3><span class=\"section-heading\">Generating the notes and URI</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#generating-the-notes-and-uri\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The sender's Zcash wallet app creates an ephemeral spending key, sends ZEC funds to the payment addressed derived from that key, and creates a Payment-Encapsulating URI that contains this ephemeral spending key and the newly-generated note commitments.</p>\n-                <section id=\"ephemeral-key-derivation\"><h4><span class=\"section-heading\">Ephemeral key derivation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#ephemeral-key-derivation\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h4>\n+                <section id=\"ephemeral-key-derivation\"><h4><span class=\"section-heading\">Ephemeral key derivation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#ephemeral-key-derivation\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h4>\n                     <p>The ephemeral keys within payment URIs are derived deterministically from the same seed as the main wallet. This ensures that if a wallet is recovered from backup, sent-but-unfinalized payments can be reclaimed.</p>\n                     <p>The derivation mechanism is as follows:</p>\n                     <ul>\n@@ -159,17 +159,17 @@\n                     </ul>\n                 </section>\n             </section>\n-            <section id=\"uri-transmission\"><h3><span class=\"section-heading\">URI Transmission</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#uri-transmission\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"uri-transmission\"><h3><span class=\"section-heading\">URI Transmission</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#uri-transmission\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The sender conveys the Payment-Encapsulating URI to the intended recipient, over some secure channel (e.g., an end-to-end encrypted messaging platform such as Signal, WhatsApp or Magic Wormhole; or a QR code scanned in person).</p>\n                 <p>If transmitted via a human-readable medium, such as a messaging app, the Payment-Encapsulating URI MAY be accompanied by a contextual explanation that the URI encapsulates a payment, and a suggested action by the recipient to complete the process (see Usage Story above for an example).</p>\n                 <p>When sent via a human-readable medium that consists of discrete messages, the message that contains the URI SHOULD NOT contain any payment-specific or manually-entered information outside the URI itself, since this information may not be visible to the recipient (see “Message Rendering” below).</p>\n                 <p>From this point, and until finalization or cancellation (see below), from the sender's perspective the payment is “in progress”; it SHOULD be conveyed as such to the sender; and MUST NOT be conveyed as “finalized” or other phrasing that conveys successful completion.</p>\n             </section>\n-            <section id=\"message-rendering\"><h3><span class=\"section-heading\">Message Rendering</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#message-rendering\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"message-rendering\"><h3><span class=\"section-heading\">Message Rendering</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#message-rendering\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The recipient's device renders the Payment-Encapsulating URI, or an indication of its arrival, along with the aforementioned contextual explanation (if any). The user has the option of “opening” the URI (i.e., by clicking it), which results in the device opening a Zcash wallet app, using the local platforms app link mechanism.</p>\n                 <p>A messaging app MAY recognize URI-Encapsulated Payments, and render them in a way that conveys their nature more clearly than raw URI strings. If the messaging medium consists of discrete messages, and a message contains one or more URI-Encapsulated Payments, then the messaging app MAY assume that all other content in that message is automatically generated and contains no payment-specific or manually-generated information, and thus may be discarded during rendering.</p>\n             </section>\n-            <section id=\"payment-rendering-and-blockchain-lookup\"><h3><span class=\"section-heading\">Payment Rendering and Blockchain Lookup</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#payment-rendering-and-blockchain-lookup\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"payment-rendering-and-blockchain-lookup\"><h3><span class=\"section-heading\">Payment Rendering and Blockchain Lookup</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#payment-rendering-and-blockchain-lookup\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The recipient's Zcash wallet app SHOULD present the payment amount and MAY present the description, as conveyed in the URI, along with an indication of the tentative nature of this information.</p>\n                 <p>In parallel, the wallet app SHOULD retrieve the relevant transactions from the Zcash blockchain, by looking up the transaction given by the <code>cmu</code> parameter (this MAY use an efficient index, perhaps assisted by a server), and check whether:</p>\n                 <ul>\n@@ -186,22 +186,22 @@\n                 <p>Within the <em>Pending</em> state, the wallet app MAY also consider “0 confirmations” transactions (i.e., transactions that have been broadcast on the node network but are neither mined nor expired), and convey their existence to the user. These do not suffice for entering the <em>Ready-to-finalize</em> state (since unmined notes cannot be immediately spent.)</p>\n                 <p>The aforementioned conditions may change over time (e.g., the transactions may be spent by someone else in the interim), so the status SHOULD be updated periodically.</p>\n             </section>\n-            <section id=\"finalization\"><h3><span class=\"section-heading\">Finalization</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#finalization\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"finalization\"><h3><span class=\"section-heading\">Finalization</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#finalization\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>When the recipient chooses to finalize the payment, the wallet app generates transactions that spends the aforementioned notes (using the ephemeral spending key) and send these Zcash funds to the user's own persistent payment address. These transactions carry the default expiry time (currently 100 blocks).</p>\n                 <p>The recipient's wallet app SHOULD convey the payment status as “Finalizing…” starting at the time that the uses initiates the finalization process. It MAY in addition convey the specific action done or event waited.</p>\n                 <p>The sender's wallet SHOULD convey the payment status as “Finalizing…” as soon as it detects that relevant transactions have been broadcast on the peer-to-peer network, or mined to the blockchain.</p>\n                 <p>Once these transactions are confirmed (to an extent considered satisfactory by the local wallet app; currently 10 confirmations is common practice), their status SHOULD be conveyed as “Finalized”, by both the sender's wallet app and the recipient's wallet app. Both wallets MUST NOT convey the payment as “finalized”, or other phrasing that conveys irrevocability, until this point.</p>\n                 <p>If these transactions expire unmined, or are otherwise rendered irrevocably invalidated (e.g., by a rollback), then both wallets' status SHOULD convey this, and the recipient's wallet SHOULD revert to the “Payment Rendering and Blockchain Lookup” stage above.</p>\n             </section>\n-            <section id=\"payment-cancellation\"><h3><span class=\"section-heading\">Payment Cancellation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#payment-cancellation\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"payment-cancellation\"><h3><span class=\"section-heading\">Payment Cancellation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#payment-cancellation\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>At any time prior to the payment being finalized, the sender is capable of cancelling the payment, by themselves finalizing the payment into their own wallet (thereby “clawing back” the funds). If the wallet has not yet sent, for inclusion in the blockchain, any of the transactions associated with the ephemeral spending key, then cancellation can also be done by discarding these transactions or aborting their generation. The sender's wallet app SHOULD offer this feature, and in this case, MUST appropriately handle the race condition where the recipient initiated finalization concurrently.</p>\n                 <p>Cancellation requires the sender to know the ephemeral spending key. If the sender has lost this state, it can be recovered deterministically (see <a href=\"#recovery-from-wallet-crash\">Recovery From Wallet Crash</a>, below).</p>\n             </section>\n-            <section id=\"status-view\"><h3><span class=\"section-heading\">Status View</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#status-view\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"status-view\"><h3><span class=\"section-heading\">Status View</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#status-view\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>Wallet apps SHOULD let the user view the status of all payments they have generated, as well as all inbound payment (i.e., URI-Encapsulated Payments that have been sent to the app, e.g., by invocation from messaging apps). The status includes the available metadata, and the payment's current state. When pertinent, the wallet app SHOULD offer the ability to finalize any <em>Pending</em> inbound payment, and MAY offer the ability to cancel any outbound payment.</p>\n                 <p>Wallet apps SHOULD actively alert the user (e.g., via status notifications) if a payment that they sent has not been finalized within a reasonable time period (e.g., 1 week), and offer to cancel the payment.</p>\n             </section>\n-            <section id=\"recovery-from-wallet-crash\"><h3><span class=\"section-heading\">Recovery From Wallet Crash</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#recovery-from-wallet-crash\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"recovery-from-wallet-crash\"><h3><span class=\"section-heading\">Recovery From Wallet Crash</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#recovery-from-wallet-crash\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>When recovering from a backed-up wallet phrase, wallet implementations already need to scan the entire chain (from the wallet's birthday) to find transactions that were received by or sent from the wallet. Simultaneously with this, the wallet may recover state about previously-created payment URIs, and regain access to non-finalized funds.</p>\n                 <p>We define a “gap limit” <em>N</em>, similar to the “address gap limit” in BIP 44. If a wallet implementation observes <em>N</em> sequentially-derived payment URIs that have no corresponding on-chain note, they may safely expect that no payment URIs beyond that point have ever been derived.</p>\n                 <p>Given that both the derivation of a payment URI and the action of “filling” it with a note are performed by the same entity (and in most cases sequentially), it is unlikely that there would be a significantly large gap in payment URI usage. As a balance between the cost of scanning multiple <code>ivk</code>s, and the likelihood of missing on-chain funds due to out-of-order payment URI generation, we specify a standard gap limit of <code>N = 3</code>.</p>\n@@ -222,7 +222,7 @@\n                 <p>For this recovery process to succeed, wallet implementations MUST fund payment URIs with a Sapling spending key in the wallet. Alternatively, wallet implementations MAY include the set of payment URI <code>ivk</code>s within the set of <code>ivk</code>s they are using for normal chain scanning, but this will slow down the recovery process by a factor of 4 (for a gap limit of <code>N = 3</code>, and a wallet with one Sapling account).</p>\n             </section>\n         </section>\n-        <section id=\"security-considerations\"><h2><span class=\"section-heading\">Security Considerations</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#security-considerations\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"security-considerations\"><h2><span class=\"section-heading\">Security Considerations</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#security-considerations\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <ul>\n                 <li>Anyone who intercepts the URI-Encapsulated Payments may steal the encapsulated funds. Therefore, URI-Encapsulated Payments should be sent over a secure channel, and should be kept secret from anyone but the intended recipient.\n                     <ul>\n@@ -244,8 +244,8 @@\n                 <li>Usage of URI-Encapsulated Payments may train users to, generally, click on other types of URI/URL links sent in other messaging contexts. Malicious links sent via unauthenticated messaging channels (e.g., emails and SMS texts) are a common attack vector, used for exploiting vulnerabilities in the apps triggered to handle these links. Even though the fault for vulnerabilities lies with those other apps, and even though this ZIP uses deep link URIs in the way intended, there are none the less these negative externalities to encouraging such use.</li>\n             </ul>\n         </section>\n-        <section id=\"design-decisions-and-rationale\"><h2><span class=\"section-heading\">Design Decisions and Rationale</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#design-decisions-and-rationale\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n-            <section id=\"rationale-for-uri-format\"><h3><span class=\"section-heading\">Rationale for URI Format</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#rationale-for-uri-format\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+        <section id=\"design-decisions-and-rationale\"><h2><span class=\"section-heading\">Design Decisions and Rationale</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#design-decisions-and-rationale\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <section id=\"rationale-for-uri-format\"><h3><span class=\"section-heading\">Rationale for URI Format</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#rationale-for-uri-format\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The URI format <code>https://pay.withzcash.com:65536/v1#...</code> was chosen to allow automatic triggering of wallet software on mobile devices, using the platform's <a href=\"#applink\">applink</a> mechanism, while minimizing the risk of payment information being intercepted by third parties. The latter is prevented by a defense in depth, where any of the following suffices to prevent the payment information from being exposed over the network:</p>\n                 <ul>\n                     <li>The <code>pay.withzcash.com</code> domain should not resolve.</li>\n@@ -263,7 +263,7 @@\n                 </ol>\n                 <p>Another option, which can be added to any of the above, is to add a confirmation code outside the URI that needs to be manually entered by the user into the wallet app, so that merely intercepting the URI link would not suffice. This does not seem to significantly reduce risk in the scenarios considered, and so deemed to not justify the reduced usability.</p>\n             </section>\n-            <section id=\"identifying-notes\"><h3><span class=\"section-heading\">Identifying Notes</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#identifying-notes\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"identifying-notes\"><h3><span class=\"section-heading\">Identifying Notes</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#identifying-notes\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The recipient's wallet needs to identify the notes related to the payment (and the on-chain transactions that contain them), in order to verify their validity and then (during the finalization process) spend them.</p>\n                 <p><strong>The following is out of date, and reflects an earlier design choice (“0”), while we have transitioned to a different choice (“4”). To be revised.</strong></p>\n                 <p>In the above description, we explicitly list the notes involved in the payment (which are easily mapped to the transactions containing them, using a suitable index). This results in long URIs when multiple notes are involved (e.g., when using the aforementioned “powers-of-2 amounts” technique).</p>\n@@ -280,16 +280,16 @@ <h1>System Message: INFO/1 (zip-0324.rst line 355)</h1>\n                     <p>Enumerated list start value not ordinal-1: \"0\" (ordinal 0)</p>\n                 </div>\n             </section>\n-            <section id=\"additional-rationale\"><h3><span class=\"section-heading\">Additional rationale</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#additional-rationale\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"additional-rationale\"><h3><span class=\"section-heading\">Additional rationale</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#additional-rationale\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <ol type=\"1\">\n                     <li>The metadata (amount and description) is provided within the URI. An alternative would be to encode the description in the encrypted memo fields of the associated shielded transactions, and compute the amount from those transactions. However, in that case the metadata would not be available for presentation to the user until the transactions have been retrieved from the blockchain.</li>\n                     <li>We support multiple spending keys and multiple notes in one URI, because these payments may be speculatively generated and mined before the payment amount is determined (to allow payments with no latency). For example, the sending wallets may pre-generate transactions for powers-of-2 amounts, and then include only a subset of them in the URI, totalling to the desired amount.</li>\n                     <li>We do not include the sender or receiver's identity in the URI, because the sending wallet many not know the name of who it is sending to (or even from). Moreover there is the risk that fraudulent sender/recipient information could be used. If necessitated by circumstances (e.g., the FinCEN \"Travel Rule\" <a id=\"footnote-reference-15\" class=\"footnote_reference\" href=\"#fincen-travel-rule\">8</a>), claimed sender and recipient identity can be included in <code>desc</code> parameter.</li>\n                 </ol>\n             </section>\n         </section>\n-        <section id=\"open-questions\"><h2><span class=\"section-heading\">Open Questions</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#open-questions\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n-            <section id=\"uri-usability\"><h3><span class=\"section-heading\">URI Usability</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#uri-usability\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+        <section id=\"open-questions\"><h2><span class=\"section-heading\">Open Questions</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#open-questions\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <section id=\"uri-usability\"><h3><span class=\"section-heading\">URI Usability</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#uri-usability\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>The URI could be changed in several ways due to usability concerns:</p>\n                 <ol type=\"1\">\n                     <li>It may be desirable to prevent the <code>amount</code> and <code>desc</code> parameters from being human readable. This is to discourage people from just looking at the URI, seeing the numbers and text, and mistakenly thinking this is already a confirmation of successful receipt (without going through the finalization process).</li>\n@@ -302,15 +302,15 @@ <h1>System Message: INFO/1 (zip-0324.rst line 355)</h1>\n                     </li>\n                 </ol>\n             </section>\n-            <section id=\"note-retrieval\"><h3><span class=\"section-heading\">Note retrieval</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#note-retrieval\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"note-retrieval\"><h3><span class=\"section-heading\">Note retrieval</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#note-retrieval\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>Ideally: a lightweight wallet can receive the funds with the assistance of a more powerful node, with minimal information leakage to that node (e.g., using simple lookups queries that can be implemented via Private Information Retrieval). The open question is how to do this given that most practical PIR are for retrieving an index out of an array, not a key from a key value standpoint.</p>\n             </section>\n-            <section id=\"other-questions\"><h3><span class=\"section-heading\">Other Questions</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#other-questions\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+            <section id=\"other-questions\"><h3><span class=\"section-heading\">Other Questions</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#other-questions\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>Should senders delay admitting a generated transaction by a random amount to prevent traffic analysis (i.e., so the messaging service operator cannot correlate messages with on-chain transactions)?</p>\n                 <p>Consider the behavior in case a chain reorgs invalidates a sent payment. Should we specify a Merkle root or block hash to help detect this reason for payment failure? Or have some servers that maintain a cache of payments that were invalidated by reorgs?</p>\n             </section>\n         </section>\n-        <section id=\"references\"><h2><span class=\"section-heading\">References</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#references\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"references\"><h2><span class=\"section-heading\">References</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#references\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <table id=\"bcp14\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n@@ -376,7 +376,7 @@ <h1>System Message: INFO/1 (zip-0324.rst line 355)</h1>\n                 </tbody>\n             </table>\n         </section>\n-        <section id=\"publication-blockers\"><h2><span class=\"section-heading\">Publication Blockers</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#publication-blockers\"><img width=\"24\" height=\"24\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+        <section id=\"publication-blockers\"><h2><span class=\"section-heading\">Publication Blockers</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#publication-blockers\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <ul>\n                 <li>Clean up semantics.</li>\n                 <li>Clean up rationale.</li>"},{"filename":"zip-0340.html","status":"added","additions":438,"deletions":0,"patch":"@@ -0,0 +1,438 @@\n+<!DOCTYPE html>\n+<html>\n+<head>\n+    <title>ZIP 340</title>\n+    <meta charset=\"utf-8\" />\n+    <script src=\"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js?config=TeX-AMS-MML_HTMLorMML\"></script>\n+    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"stylesheet\" href=\"css/style.css\">\n+</head>\n+<body>\n+<h1 id=\"coin-voting\">Coin Voting</h1>\n+<p>This ZIP describes a mechanism for the organization of coin-weighted\n+election.</p>\n+<h2 id=\"use-case\">Use Case</h2>\n+<p>A person or organization, referred as the Election Board (EB) decides\n+to request the opinion of the zcash holders (ZH) on a particular topic.\n+The EB defines the parameters of the referendum: - when it starts\n+(snapshot height) and stops (closing height) - An optional lockdown\n+period before it closes - the question asked - the criteria for\n+aggregating the votes</p>\n+<p>They publically ask the ZH to participate by sending them ballots.\n+They verify and tally the votes following the aggregation method\n+(typicall linear, but it could be quadratic or something else). Then\n+they publish the outcome.</p>\n+<p>The final result of the election is a list of valid ballots that\n+have: - a value in ZEC - a message</p>\n+<p>The content of the message depends on the type of referendum. The\n+acceptable options should be defined by EB. For example, for a yes/no\n+question, it could simply be a value of 0 or 1.</p>\n+<p>The EB could decide to accept write-in votes too. The format of the\n+message is not part of this ZIP.</p>\n+<p>The value of the ballot MUST be an amount of ZEC that the ZH can\n+spend at the snapshot height.</p>\n+<p>The ZH is not obligated to vote with all the ZEC that they have. But\n+it MUST be impossible to use the same coins to vote twice on the same\n+election.</p>\n+<p>It MUST be possible to use the same ZEC on DIFFERENT elections.</p>\n+<p><strong>See Privacy section on important caveats regarding concurrent\n+voting.</strong></p>\n+<h2 id=\"other-uses-cases\">Other Uses Cases</h2>\n+<h3 id=\"airdrop\">Airdrop</h3>\n+<p>This ZIP can be used to perform air-drops. An organization that wants\n+to give their tokens/coins based on an amount of ZEC can organize an\n+election. The ballots contain the ZH’s address where they want to\n+receive the airdrop.</p>\n+<h3 id=\"proof-of-balance\">Proof of Balance</h3>\n+<p>In this case, the ZH sends an empty message because the ballot value\n+is all that matters.</p>\n+<h1 id=\"requirements\">Requirements</h1>\n+<h2 id=\"participants-actors\">Participants / Actors</h2>\n+<h3 id=\"election-board\">Election Board</h3>\n+<p>The Election Board (EB) is the entity in charge of organizing the\n+election (E). - The EB MUST have a website secured by https that clearly\n+reflects their identity. - They MUST make their announcements through\n+their website. - The EB SHOULD have a forum for election related\n+discussions - The EB MUST set the election parameters described in a\n+later section - The EB MUST publish the election parameters on their\n+website - The election parameters are immutable. If the EB wants to make\n+a modification, another election MUST be created</p>\n+<h3 id=\"voter-zh\">Voter (ZH)</h3>\n+<p>Anyone who has <em>spending authority</em> of ZEC (either transparent\n+or shielded) can participate.</p>\n+<ul>\n+<li>Ballots MUST have a ZEC value</li>\n+<li>Ballots MUST have a message in the format defined by the\n+Election</li>\n+<li>Ballots MUST include a proof of spending authority for the amount\n+specified, valid at the snapshot height</li>\n+<li>Ballots MUST not prevent the ZH from spending the coins in a\n+transaction</li>\n+<li>Coins spent before the snapshot height MUST not be eligible for\n+voting</li>\n+<li>Voting MUST not disclose information that would allow someone to use\n+the coins themselves (in a transaction or an election)</li>\n+<li>Ballots MUST follow the format specified in the section Ballot</li>\n+<li>ZEC held in transparent t3 addresses CANNOT be used. They introduce\n+most complexity and are not used much</li>\n+</ul>\n+<p>Notes: - Having coins on an exchange such as Binance, Bitfinex, etc.\n+does not allow you to vote because the coins are under their custody -\n+The EB MAY set special rules for the election. For example, there MAY be\n+a fee for casting a vote or a minimum ZEC requirement</p>\n+<h2 id=\"privacy\">Privacy</h2>\n+<div class=\"danger\">\n+<p>If the coins used for voting for a given election are not moved\n+before voting for another election, it is possible to link the\n+votes.</p>\n+</div>\n+<ol type=\"1\">\n+<li>Transparent inputs are in clear</li>\n+<li>Shielded inputs reveal their nullifier</li>\n+</ol>\n+<p>The total value is also revealed since it is the weight of the\n+vote.</p>\n+<p>To mitigate this issue, the user SHOULD be offered to move their\n+coins after a vote. However, if multiple elections are carried out\n+concurrently with similar snapshot heights, it will be impossible for a\n+Voter to participate in both without linking their votes.</p>\n+<p>A possible solution is suggested in <a\n+href=\"https://hackmd.io/ujald5ElTMumppM7juk2WA?both\">Air drops,\n+Proof-of-Balance, and Stake-weighted Polling</a></p>\n+<p>Alternatively, homomorphic encryption could be used in order to tally\n+the votes without decrypting them individually. However, this would be\n+less flexible than the above.</p>\n+<h2 id=\"workflow\">Workflow</h2>\n+<p>The election E is carried in several sequential phases.</p>\n+<ol type=\"1\">\n+<li>The EB publishes the E parameters and advertise the E</li>\n+<li>Each V prepares his/her ballot and send it to the EB</li>\n+<li>The EB returns a receipt to the V</li>\n+<li>The E enters a lockdown period</li>\n+<li>The E closes and the results are published</li>\n+</ol>\n+<pre class=\"mermaid\"><code>sequenceDiagram\n+    EB-&gt;&gt;Public: Publish Election\n+    Note over V,EB: When snapshot height reached\n+    loop every Voter, concurrently\n+    V-&gt;&gt;EB: Ballot\n+    EB--&gt;&gt;V: Receipt\n+    end\n+\n+    Note over V,EB: When lockdown height reached\n+    EB-&gt;&gt;Public: List of Ballot ID\n+    Note over V,EB: When expiration height reached\n+    loop every Voter, concurrently\n+    V-&gt;&gt;EB: Ballot Decryption Key\n+    end\n+    EB-&gt;&gt;Public: Publish Decrypted Ballots, E results</code></pre>\n+<h2 id=\"ballots\">Ballots</h2>\n+<p>The message part of a Ballot is encrypted with the V key. The\n+decryption key is sent to the EB after the lockdown period. The EB\n+validates the ballot coin value and returns a receipt. The Receipt\n+contains a signature from the EB.</p>\n+<h2 id=\"lockdown-period-ballot-list-commitment\">Lockdown Period / Ballot\n+List commitment</h2>\n+<p>The lockdown period is optional but RECOMMENDED.</p>\n+<p>It gives time for the Voters to check that their ballot is recorded\n+by the EB publicly and that no additional ballots are added after their\n+opening.</p>\n+<p>If a V does not see their ballot on the list, they MUST contact the\n+EB and resolve the dispute. Their receipt has the EB signature and\n+proves the integrity of the ballot. Failure to recognize a valid ballot\n+is a serious breach of trust.</p>\n+<h2 id=\"counting\">Counting</h2>\n+<p>After the lockdown period, V MUST send their decryption key to the\n+EB.</p>\n+<p>EB decrypts the vote and updates the tally.</p>\n+<p>Finally EB publishes the results for everyone to see.</p>\n+<p>The encrypted ballots and their decryption keys are published for\n+verification.</p>\n+<h2 id=\"verification\">Verification</h2>\n+<ul>\n+<li>Check that the ballot hashes correspond to the ballot published</li>\n+<li>For each ballot,</li>\n+</ul>\n+<ol type=\"1\">\n+<li>Check the validity of the signatures and the zkps</li>\n+<li>Compute the coin value</li>\n+<li>Decrypt the vote</li>\n+<li>Tally the vote weighted by the coin value</li>\n+</ol>\n+<h1 id=\"specifications\">Specifications</h1>\n+<p>The communication uses binary messages in protobuf format.</p>\n+<h2 id=\"eb\">EB</h2>\n+<ul>\n+<li>URL:\n+<ul>\n+<li>MUST be https</li>\n+<li>MUST identify the organization</li>\n+</ul></li>\n+<li>Name\n+<ul>\n+<li>MUST be unique to organization</li>\n+</ul></li>\n+<li>start_height</li>\n+<li>snapshot height</li>\n+<li>lockdown height</li>\n+<li>closing height</li>\n+<li>the EB public key</li>\n+</ul>\n+<p>Example:</p>\n+<pre class=\"toml=\"><code>url = &quot;https://election.methyl.cc&quot;\n+name = &quot;Test Election 2024&quot;\n+start_height = 2397000\n+snapshot_height = 2462430\n+lockdown_height = 2460000\n+closing_height = 2460000\n+msg_size = 64\n+public= &quot;788ff50468e2c095d97f8249e27eef3afe8a07712988a475583eb89494d64abe&quot;\n+cmx_root = &quot;063121a1fed23f154e18d9dfc70a13867f295207fd3c59d0e3f86615b857d332&quot;\n+nf_root = &quot;5341fe36634580cdf8862a106dab2cf7bd39f67d171a2877995babb435a5ee0e&quot;</code></pre>\n+<p>Only notes received and unspent between the start_height and the\n+snapshot height are eligible.</p>\n+<p>The start_height defaults to the Sapling activation height.</p>\n+<p>In addition, the EB MUST define a message in protobuf format. The\n+message MUST have a fixed size.</p>\n+<p><code>public</code> is defined in the section Message Encryption</p>\n+<p>Example:</p>\n+<div class=\"sourceCode\" id=\"cb3\"><pre\n+class=\"sourceCode proto\"><code class=\"sourceCode protobuf\"><span id=\"cb3-1\"><a href=\"#cb3-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"kw\">message</span> BallotContent {</span>\n+<span id=\"cb3-2\"><a href=\"#cb3-2\" aria-hidden=\"true\" tabindex=\"-1\"></a>    uint8 candidate = <span class=\"dv\">1</span>;</span>\n+<span id=\"cb3-3\"><a href=\"#cb3-3\" aria-hidden=\"true\" tabindex=\"-1\"></a>}</span></code></pre></div>\n+<p>From the E definition, the E domain is calculated as</p>\n+<p><code>domain = Blake2b-256(b\"Zcash_WCV_domain\", E)</code></p>\n+<p>The E domain is included in the ballot and ensures that ballots\n+cannot be replayed.</p>\n+<h2 id=\"ballot\">Ballot</h2>\n+<pre class=\"protobuf=\"><code>syntax = &quot;proto3&quot;;\n+package proof_balance;\n+// Protobuf does not support fixed size arrays\n+// This type MUST be 32 byte-long\n+message b256 {\n+    bytes b = 1;\n+}\n+\n+// A ballot is made of 2 parts, \n+// the data part is non malleable and is used to calculate the sighash\n+// the witness part has the signatures and the zkp\n+message Ballot {\n+    BallotData data = 1;\n+    Witness witness = 2;\n+}\n+\n+message Header {\n+    uint32 version = 1; // version = 1\n+    b256 domain = 2; // Election Domain\n+}\n+\n+message BallotData {\n+    Header header = 1;\n+    TransparentData tdata = 2; // transparent inputs\n+    ShieldedData zdata = 3; // sapling inputs\n+    ShieldedData odata = 4; // orchard inputs\n+    EncMessage enc_message = 5; // encrypted vote\n+}\n+\n+message EncMessage {\n+    bytes epk = 1;\n+    bytes data = 2;\n+    bytes tag = 3;\n+}\n+\n+message Witness {\n+    TransparentWitness twitness = 1; // transparent sigs\n+    SaplingWitness zwitness = 2; // sapling sigs &amp; zkp\n+    OrchardWitness owitness = 3; // orchard sigs &amp; zkp\n+}\n+\n+// Transparent Input, i.e UTXO\n+message TxIn {\n+    b256 txid = 1;    // prev txout\n+    uint32 vout = 2;  // index of the output\n+}\n+\n+message TransparentData {\n+    repeated TxIn txin = 1;\n+}\n+\n+// See section 7.3 and 7.5 of [protocol doc]\n+message Spend {\n+    b256 commitment_value = 1;\n+    b256 nullifier = 2;\n+    b256 rk = 3;\n+}\n+\n+message ShieldedData {\n+    // b256 anchor = 1; // MUST be the anchor at snapshot height\n+    repeated Spend spends = 2;\n+    uint64 valuebalance = 3;\n+}\n+\n+message Signature {\n+    bytes b = 1; // 64 bytes, r, s\n+}\n+\n+message TransparentProof {\n+    b256 pk = 1;\n+    Signature signature = 2;\n+}\n+\n+message TransparentWitness {\n+    repeated TransparentProof proofs = 1;\n+}\n+\n+message SaplingSpendProof {\n+    bytes zkproof = 1; // 192 bytes\n+    Signature signature = 2;\n+}\n+\n+message SaplingWitness {\n+    repeated SaplingSpendProof proofs = 1;\n+    Signature binding_signature = 2;\n+}\n+\n+message OrchardWitness {\n+    repeated Signature signatures = 1;\n+    bytes zkproof = 2; // 2720 + 2272 * nOrchard\n+    Signature binding_signature = 3;\n+}\n+\n+message ElectionId {\n+    string name = 1;\n+}\n+\n+message ElectionInfo {\n+    string url = 1;\n+    string name = 2;\n+    uint32 start_height = 3;\n+    uint32 snapshot_height = 4;\n+    uint32 lockdown_height = 5;\n+    uint32 closing_height = 6;\n+    string pk = 7;\n+    uint32 msg_size = 8;\n+    bytes cmx_root = 9;\n+    bytes nf_root = 10;\n+}\n+\n+message ProofDomain {\n+    bytes domain = 1;\n+}\n+\n+message Proof {\n+    bytes cv = 1;\n+    bytes nullifier = 2;\n+    bytes rk = 3;\n+    bytes proof = 4;\n+}\n+</code></pre>\n+<h3 id=\"ballot-hash-sighash\">Ballot Hash / Sighash</h3>\n+<p>For the signatures, the sighash is the blake2b-256 hash of the\n+serialization of the ballot data (in protobuf 3) with personalization\n+string <code>Zcash_WCV___Hash</code>.</p>\n+<p><code>hash = Blake2b-256(\"Zcash_WCVSigHash\", BallotData)</code>.</p>\n+<h2 id=\"message-encryption\">Message Encryption</h2>\n+<p>The EB MUST sign the ballot sighash with a ed25519 signing key and\n+return a receipt as a bech32m encoded signature with hrp\n+<code>pob-receipt-</code></p>\n+<p>The Voter MUST add a message in the format specified by the EB. The\n+message is encrypted into EncMessage. - epk is a 32 byte public key on\n+x25519 - the Voter reveals his own ed25519 signing key after the vote\n+closes - the EB converts the key to a x25519 secret key - the EB uses DH\n+to calculate a shared key KA - SymKey = Blake2b-256(KA,\n+perso=“Zcash_ProofBlnce”) - SymKey is used with Chacha20-Poly1305 to\n+decrypt the message - The tag MUST match the decrypted tag</p>\n+<p>Rationale: This design allows the use of ed25519 SSH keys for both\n+Voter and EB.</p>\n+<div class=\"warning\">\n+<p>The Voter SHOULD not reuse the signing key!</p>\n+</div>\n+<h1 id=\"poc\">POC</h1>\n+<p>The POC is for a modified version specific to Orchard that uses\n+nullifiers scoped by elections.</p>\n+<h2 id=\"pollelection-setup\">Poll/Election Setup</h2>\n+<p>The EB (Election Board) generates an openssh ed25519 signing key</p>\n+<pre class=\"bash=\"><code>openssl genpkey -algorithm ed25519 -out poller.pem</code></pre>\n+<div class=\"info\">\n+<p>The key MAY be reused. It is used for signing ballot receipts. It is\n+RECOMMENDED to use a different key per election.</p>\n+</div>\n+<p>The EB chooses a event properties</p>\n+<pre class=\"toml=\"><code>url = &quot;https://election.methyl.cc&quot;\n+name = &quot;Test Election 2024&quot;\n+start_height = 2397000\n+snapshot_height = 2462430\n+lockdown_height = 2460000\n+closing_height = 2460000\n+msg_size = 64\n+public= &quot;788ff50468e2c095d97f8249e27eef3afe8a07712988a475583eb89494d64abe&quot;\n+cmx_root = &quot;063121a1fed23f154e18d9dfc70a13867f295207fd3c59d0e3f86615b857d332&quot;\n+nf_root = &quot;5341fe36634580cdf8862a106dab2cf7bd39f67d171a2877995babb435a5ee0e&quot;</code></pre>\n+<p>The public key MUST match the EB signing key.</p>\n+<p>The <code>cmx_root</code> and <code>nf_root</code> are OPTIONAL but\n+if present MUST match the hashes calculated by building the reference\n+data.</p>\n+<div class=\"info\">\n+<p>If the snapshot height is not in the past, it is impossible to\n+calculate the tree roots.</p>\n+</div>\n+<p>The EB publishes the <code>election.toml</code> file so that voters\n+can download it.</p>\n+<h2 id=\"voter-send-their-ballots\">Voter Send their Ballots</h2>\n+<p>Once the snapshot height is reached and before the lockdown height,\n+voters can submit their vote.</p>\n+<p>They have to follow the steps: - generate an openssh ed25519 key; -\n+download block data for the range from start height to snapshot height;\n+- build reference data, i.e. cmx and nf Merkle trees; - copy their\n+wallet file <code>zec.db</code> from Ywallet. The vote does not have to\n+use this wallet app as long as it has the note data: - d, pkd, - v, -\n+rseed, - <span class=\"math inline\"><em>ρ</em></span>, <span\n+class=\"math inline\"><em>ψ</em></span> - note position - create the proof\n+with a ballot message; - submit the ballot to the EB; - wait until the\n+election reaches the closing height.</p>\n+<pre><code>&gt;&gt; load-identity voter.pem\n+c76a89174ed64479c5f567b955ce27ce998568e72a044de80b0ebe748d826b3c\n+&gt;&gt; download https://lwd1.zcash-infra.com:9067\n+&gt;&gt; build-reference-data\n+Root cmxs: 5de18172c1c9d2a526a9c73067e75dfbe1ac4e774e1bfec9a31ee178db98a635\n+Root nfs: 12d4776996da7929255bccfcb21c99853d840c4008da547b113c4c857eec3d29\n+&gt;&gt; create-proof pob.bin 1 &quot;Vote for BOB!&quot;\n+5c70068567d0b8d52e4dcfc395a67dc83fd6bdf9c65a835b41d2bfeb776480fb\n+cmx anchor: 5de18172c1c9d2a526a9c73067e75dfbe1ac4e774e1bfec9a31ee178db98a635\n+nf  anchor: 12d4776996da7929255bccfcb21c99853d840c4008da547b113c4c857eec3d29</code></pre>\n+<h2 id=\"eb-collects-votes\">EB Collects Votes</h2>\n+<p>The EB collects votes and verifies the validity of the ZKP, spending\n+signatures and binding signature. It also checks that the root hashes\n+match and that the nullifiers are not used.</p>\n+<p>If everything is fine, the EB returns a receipt that is a signature\n+on the sighash of the ballot.</p>\n+<pre><code>&gt;&gt; load-identity poller.pem\n+3f8a41af400aa0197594a7b5c2e21bb6307753840a8513b28b816aad43fb30c0\n+&gt;&gt; init-db\n+&gt;&gt; verify-proof pob.bin\n+ZK Proof verified!\n+Spending Signature verified!\n+Binding Signature verified!\n+Verified 0.05000000 ZEC\n+Receipt: pob-receipt-1djsvezm3wep2ganwd4upzawxsgz60hq2e86cqkv4djmusut4fjzzvzu2ut5z3uda4eedvff6anlmzz3k48d7ugxsrs0gxfn36nscvqqzn4js8</code></pre>\n+<p>At this point, the ballot is verified and its amount can be accounted\n+for but the vote payload is not revealed.</p>\n+<div class=\"info\">\n+<p>The EB publishes the ballot hashes and receipts on their website.</p>\n+</div>\n+<h2 id=\"opening-votes\">Opening Votes</h2>\n+<p>Voters uploads their signing key to the EB.</p>\n+<pre><code>&gt;&gt; load-identity voter.pem\n+c76a89174ed64479c5f567b955ce27ce998568e72a044de80b0ebe748d826b3c\n+&gt;&gt; open-proof pob.bin\n+5c70068567d0b8d52e4dcfc395a67dc83fd6bdf9c65a835b41d2bfeb776480fb\n+Decrypted message: Vote for BOB!</code></pre>\n+<div class=\"info\">\n+<p>The EB counts the votes and publishes the results</p>\n+</div>\n+<h2 id=\"audit\">Audit</h2>\n+<div class=\"info\">\n+<p>The EB publishes all ballots and their opening keys for verification\n+by auditors.</p>\n+</div>\n+</body>\n+</html>"},{"filename":"zip-0340.md","status":"added","additions":537,"deletions":0,"patch":"@@ -0,0 +1,537 @@\n+---\n+title: Coin Voting\n+breaks: false\n+tags: coin_voting\n+---\n+\n+# Coin Voting\n+\n+This ZIP describes a mechanism for the organization of\n+coin-weighted election. \n+\n+## Use Case\n+\n+A person or organization, referred as the Election Board (EB)\n+decides to request the opinion of the zcash holders (ZH)\n+on a particular topic. The EB defines the parameters of the\n+referendum:\n+- when it starts (snapshot height) and stops (closing height)\n+- An optional lockdown period before it closes\n+- the question asked\n+- the criteria for aggregating the votes\n+\n+They publically ask the ZH to participate by sending them ballots.\n+They verify and tally the votes following the aggregation method (typicall linear, but it could be quadratic or something else). Then they publish the outcome.\n+\n+The final result of the election is a list of valid ballots that have:\n+- a value in ZEC\n+- a message\n+\n+The content of the message depends on the type of referendum.\n+The acceptable options should be defined by EB. For example, for a \n+yes/no question, it could simply be a value of 0 or 1.\n+\n+The EB could decide to accept write-in votes too. The format of the \n+message is not part of this ZIP.\n+\n+The value of the ballot MUST be an amount of ZEC that the ZH\n+can spend at the snapshot height. \n+\n+The ZH is not obligated to vote with all the ZEC that they have.\n+But it MUST be impossible to use the same coins to vote\n+twice on the same election.\n+\n+It MUST be possible to use the same ZEC on DIFFERENT elections.\n+\n+**See Privacy section on important caveats regarding concurrent\n+voting.**\n+\n+## Other Uses Cases\n+\n+### Airdrop\n+This ZIP can be used to perform air-drops. An organization\n+that wants to give their tokens/coins based on an amount\n+of ZEC can organize an election. The ballots contain\n+the ZH's address where they want to receive the airdrop.\n+\n+### Proof of Balance\n+In this case, the ZH sends an empty message because the\n+ballot value is all that matters.\n+\n+# Requirements\n+\n+## Participants / Actors\n+\n+### Election Board\n+\n+The Election Board (EB) is the entity in charge of organizing\n+the election (E). \n+- The EB MUST have a website secured by https that clearly\n+reflects their identity. \n+- They MUST make their announcements \n+through their website. \n+- The EB SHOULD have a forum for election related discussions\n+- The EB MUST set the election parameters described in a later\n+section\n+- The EB MUST publish the election parameters on their website\n+- The election parameters are immutable. If the EB\n+wants to make a modification, another election MUST be created\n+\n+### Voter (ZH)\n+\n+Anyone who has *spending authority* of ZEC \n+(either transparent or shielded) can \n+participate.\n+\n+- Ballots MUST have a ZEC value\n+- Ballots MUST have a message in the format defined by the Election\n+- Ballots MUST include a proof of spending authority for the\n+amount specified, valid at the snapshot height\n+- Ballots MUST not prevent the ZH from spending the coins in a transaction\n+- Coins spent before the snapshot height MUST not be eligible for voting\n+- Voting MUST not disclose information that would allow someone \n+to use the coins themselves (in a transaction or an election)\n+- Ballots MUST follow the format specified in the section Ballot\n+- ZEC held in transparent t3 addresses CANNOT be used. They introduce\n+most complexity and are not used much\n+\n+Notes:\n+- Having coins on an exchange such as Binance, Bitfinex, etc.\n+does not allow you to vote because the coins are under their custody\n+- The EB MAY set special rules for the election. For example,\n+there MAY be a fee for casting a vote or a minimum ZEC requirement\n+\n+## Privacy\n+:::danger\n+If the coins used for voting for a given election are not moved\n+before voting for another election, it is possible to link\n+the votes.\n+:::\n+\n+1. Transparent inputs are in clear\n+1. Shielded inputs reveal their nullifier\n+\n+The total value is also revealed since it is the weight\n+of the vote.\n+\n+To mitigate this issue, the user SHOULD be offered to move\n+their coins after a vote. However, if multiple elections\n+are carried out concurrently with similar snapshot\n+heights, it will be impossible for a Voter to participate\n+in both without linking their votes.\n+\n+A possible solution is suggested in \n+[Air drops, Proof-of-Balance, and Stake-weighted Polling](https://hackmd.io/ujald5ElTMumppM7juk2WA?both)\n+\n+Alternatively, homomorphic encryption could be used in\n+order to tally the votes without decrypting them individually.\n+However, this would be less flexible than the above.\n+\n+## Workflow\n+\n+The election E is carried in several sequential phases.\n+\n+1. The EB publishes the E parameters and advertise the E\n+1. Each V prepares his/her ballot and send it to the EB\n+1. The EB returns a receipt to the V\n+1. The E enters a lockdown period\n+1. The E closes and the results are published\n+\n+```mermaid\n+sequenceDiagram\n+    EB->>Public: Publish Election\n+    Note over V,EB: When snapshot height reached\n+    loop every Voter, concurrently\n+    V->>EB: Ballot\n+    EB-->>V: Receipt\n+    end\n+\n+    Note over V,EB: When lockdown height reached\n+    EB->>Public: List of Ballot ID\n+    Note over V,EB: When expiration height reached\n+    loop every Voter, concurrently\n+    V->>EB: Ballot Decryption Key\n+    end\n+    EB->>Public: Publish Decrypted Ballots, E results\n+```\n+\n+## Ballots\n+\n+The message part of a Ballot is encrypted with the V key.\n+The decryption key is sent to the EB after the lockdown period.\n+The EB validates the ballot coin value and returns a receipt.\n+The Receipt contains a signature from the EB.\n+\n+## Lockdown Period / Ballot List commitment\n+The lockdown period is optional but RECOMMENDED. \n+\n+It gives time for the Voters to check that their ballot is \n+recorded by the EB publicly and that no additional ballots are\n+added after their opening.\n+\n+If a V does not see their ballot on the list, they MUST\n+contact the EB and resolve the dispute. Their receipt\n+has the EB signature and proves the integrity of the ballot.\n+Failure to recognize a valid ballot is a serious breach\n+of trust.\n+\n+## Counting\n+\n+After the lockdown period, V MUST send their\n+decryption key to the EB. \n+\n+EB decrypts the vote and updates the tally.\n+\n+Finally EB publishes the results for everyone to see.\n+\n+The encrypted ballots and their decryption keys are published\n+for verification.\n+\n+## Verification\n+\n+- Check that the ballot hashes correspond to the ballot published\n+- For each ballot,\n+1. Check the validity of the signatures and the zkps\n+1. Compute the coin value\n+1. Decrypt the vote\n+1. Tally the vote weighted by the coin value\n+\n+# Specifications\n+\n+The communication uses binary messages in protobuf format.\n+\n+## EB\n+\n+- URL: \n+    - MUST be https\n+    - MUST identify the organization\n+- Name\n+    - MUST be unique to organization\n+- start_height\n+- snapshot height\n+- lockdown height\n+- closing height\n+- the EB public key\n+\n+Example:\n+```toml=\n+url = \"https://election.methyl.cc\"\n+name = \"Test Election 2024\"\n+start_height = 2397000\n+snapshot_height = 2462430\n+lockdown_height = 2460000\n+closing_height = 2460000\n+msg_size = 64\n+public= \"788ff50468e2c095d97f8249e27eef3afe8a07712988a475583eb89494d64abe\"\n+cmx_root = \"063121a1fed23f154e18d9dfc70a13867f295207fd3c59d0e3f86615b857d332\"\n+nf_root = \"5341fe36634580cdf8862a106dab2cf7bd39f67d171a2877995babb435a5ee0e\"\n+```\n+\n+Only notes received and unspent between the start_height and the snapshot height\n+are eligible.\n+\n+The start_height defaults to the Sapling activation height.\n+\n+In addition, the EB MUST define a message in protobuf format.\n+The message MUST have a fixed size.\n+\n+`public` is defined in the section Message Encryption\n+\n+Example:\n+```proto\n+message BallotContent {\n+    uint8 candidate = 1;\n+}\n+```\n+\n+From the E definition, the E domain is calculated as\n+\n+`domain = Blake2b-256(b\"Zcash_WCV_domain\", E)`\n+\n+The E domain is included in the ballot and ensures that \n+ballots cannot be replayed.\n+\n+## Ballot\n+\n+```protobuf=\n+syntax = \"proto3\";\n+package proof_balance;\n+// Protobuf does not support fixed size arrays\n+// This type MUST be 32 byte-long\n+message b256 {\n+    bytes b = 1;\n+}\n+\n+// A ballot is made of 2 parts, \n+// the data part is non malleable and is used to calculate the sighash\n+// the witness part has the signatures and the zkp\n+message Ballot {\n+    BallotData data = 1;\n+    Witness witness = 2;\n+}\n+\n+message Header {\n+    uint32 version = 1; // version = 1\n+    b256 domain = 2; // Election Domain\n+}\n+\n+message BallotData {\n+    Header header = 1;\n+    TransparentData tdata = 2; // transparent inputs\n+    ShieldedData zdata = 3; // sapling inputs\n+    ShieldedData odata = 4; // orchard inputs\n+    EncMessage enc_message = 5; // encrypted vote\n+}\n+\n+message EncMessage {\n+    bytes epk = 1;\n+    bytes data = 2;\n+    bytes tag = 3;\n+}\n+\n+message Witness {\n+    TransparentWitness twitness = 1; // transparent sigs\n+    SaplingWitness zwitness = 2; // sapling sigs & zkp\n+    OrchardWitness owitness = 3; // orchard sigs & zkp\n+}\n+\n+// Transparent Input, i.e UTXO\n+message TxIn {\n+    b256 txid = 1;    // prev txout\n+    uint32 vout = 2;  // index of the output\n+}\n+\n+message TransparentData {\n+    repeated TxIn txin = 1;\n+}\n+\n+// See section 7.3 and 7.5 of [protocol doc]\n+message Spend {\n+    b256 commitment_value = 1;\n+    b256 nullifier = 2;\n+    b256 rk = 3;\n+}\n+\n+message ShieldedData {\n+    // b256 anchor = 1; // MUST be the anchor at snapshot height\n+    repeated Spend spends = 2;\n+    uint64 valuebalance = 3;\n+}\n+\n+message Signature {\n+    bytes b = 1; // 64 bytes, r, s\n+}\n+\n+message TransparentProof {\n+    b256 pk = 1;\n+    Signature signature = 2;\n+}\n+\n+message TransparentWitness {\n+    repeated TransparentProof proofs = 1;\n+}\n+\n+message SaplingSpendProof {\n+    bytes zkproof = 1; // 192 bytes\n+    Signature signature = 2;\n+}\n+\n+message SaplingWitness {\n+    repeated SaplingSpendProof proofs = 1;\n+    Signature binding_signature = 2;\n+}\n+\n+message OrchardWitness {\n+    repeated Signature signatures = 1;\n+    bytes zkproof = 2; // 2720 + 2272 * nOrchard\n+    Signature binding_signature = 3;\n+}\n+\n+message ElectionId {\n+    string name = 1;\n+}\n+\n+message ElectionInfo {\n+    string url = 1;\n+    string name = 2;\n+    uint32 start_height = 3;\n+    uint32 snapshot_height = 4;\n+    uint32 lockdown_height = 5;\n+    uint32 closing_height = 6;\n+    string pk = 7;\n+    uint32 msg_size = 8;\n+    bytes cmx_root = 9;\n+    bytes nf_root = 10;\n+}\n+\n+message ProofDomain {\n+    bytes domain = 1;\n+}\n+\n+message Proof {\n+    bytes cv = 1;\n+    bytes nullifier = 2;\n+    bytes rk = 3;\n+    bytes proof = 4;\n+}\n+\n+```\n+\n+### Ballot Hash / Sighash\n+\n+For the signatures, the sighash is the blake2b-256\n+hash of the serialization of the ballot data (in protobuf 3)\n+with personalization string `Zcash_WCV___Hash`.\n+\n+`hash = Blake2b-256(\"Zcash_WCVSigHash\", BallotData)`.\n+\n+## Message Encryption\n+\n+The EB MUST sign the ballot sighash with a ed25519 signing key and return\n+a receipt as a bech32m encoded signature with hrp `pob-receipt-`\n+\n+The Voter MUST add a message in the format specified by the EB.\n+The message is encrypted into EncMessage.\n+- epk is a 32 byte public key on x25519\n+- the Voter reveals his own ed25519 signing key after the vote closes\n+- the EB converts the key to a x25519 secret key\n+- the EB uses DH to calculate a shared key KA\n+- SymKey = Blake2b-256(KA, perso=\"Zcash_ProofBlnce\")\n+- SymKey is used with Chacha20-Poly1305 to decrypt the message\n+- The tag MUST match the decrypted tag\n+\n+Rationale: This design allows the use of ed25519 SSH keys for both Voter and EB.\n+\n+:::warning\n+The Voter SHOULD not reuse the signing key!\n+:::\n+\n+\n+# POC\n+\n+The POC is for a modified version specific to Orchard that uses\n+nullifiers scoped by elections.\n+\n+## Poll/Election Setup\n+\n+The EB (Election Board) generates an openssh ed25519 signing key\n+\n+```bash=\n+openssl genpkey -algorithm ed25519 -out poller.pem\n+```\n+\n+:::info\n+The key MAY be reused. It is used for signing ballot receipts.\n+It is RECOMMENDED to use a different key per election.\n+:::\n+\n+The EB chooses a event properties\n+\n+```toml=\n+url = \"https://election.methyl.cc\"\n+name = \"Test Election 2024\"\n+start_height = 2397000\n+snapshot_height = 2462430\n+lockdown_height = 2460000\n+closing_height = 2460000\n+msg_size = 64\n+public= \"788ff50468e2c095d97f8249e27eef3afe8a07712988a475583eb89494d64abe\"\n+cmx_root = \"063121a1fed23f154e18d9dfc70a13867f295207fd3c59d0e3f86615b857d332\"\n+nf_root = \"5341fe36634580cdf8862a106dab2cf7bd39f67d171a2877995babb435a5ee0e\"\n+```\n+\n+The public key MUST match the EB signing key.\n+\n+The `cmx_root` and `nf_root` are OPTIONAL but if present\n+MUST match the hashes calculated by building the reference data.\n+\n+:::info\n+If the snapshot height is not in the past, it is impossible\n+to calculate the tree roots.\n+:::\n+\n+The EB publishes the `election.toml` file so that voters can download it.\n+\n+## Voter Send their Ballots\n+\n+Once the snapshot height is reached and before the lockdown height,\n+voters can submit their vote.\n+\n+They have to follow the steps:\n+- generate an openssh ed25519 key;\n+- download block data for the range from start height to snapshot height;\n+- build reference data, i.e. cmx and nf Merkle trees;\n+- copy their wallet file `zec.db` from Ywallet. The vote does not\n+have to use this wallet app as long as it has the note data:\n+    - d, pkd,\n+    - v,\n+    - rseed,\n+    - $\\rho$, $\\psi$\n+    - note position\n+- create the proof with a ballot message;\n+- submit the ballot to the EB;\n+- wait until the election reaches the closing height.\n+\n+```\n+>> load-identity voter.pem\n+c76a89174ed64479c5f567b955ce27ce998568e72a044de80b0ebe748d826b3c\n+>> download https://lwd1.zcash-infra.com:9067\n+>> build-reference-data\n+Root cmxs: 5de18172c1c9d2a526a9c73067e75dfbe1ac4e774e1bfec9a31ee178db98a635\n+Root nfs: 12d4776996da7929255bccfcb21c99853d840c4008da547b113c4c857eec3d29\n+>> create-proof pob.bin 1 \"Vote for BOB!\"\n+5c70068567d0b8d52e4dcfc395a67dc83fd6bdf9c65a835b41d2bfeb776480fb\n+cmx anchor: 5de18172c1c9d2a526a9c73067e75dfbe1ac4e774e1bfec9a31ee178db98a635\n+nf  anchor: 12d4776996da7929255bccfcb21c99853d840c4008da547b113c4c857eec3d29\n+```\n+\n+## EB Collects Votes\n+\n+The EB collects votes and verifies the validity of the ZKP, spending\n+signatures and binding signature. It also checks that the root hashes\n+match and that the nullifiers are not used.\n+\n+If everything is fine, the EB returns a receipt that is a \n+signature on the sighash of the ballot.\n+\n+```\n+>> load-identity poller.pem\n+3f8a41af400aa0197594a7b5c2e21bb6307753840a8513b28b816aad43fb30c0\n+>> init-db\n+>> verify-proof pob.bin\n+ZK Proof verified!\n+Spending Signature verified!\n+Binding Signature verified!\n+Verified 0.05000000 ZEC\n+Receipt: pob-receipt-1djsvezm3wep2ganwd4upzawxsgz60hq2e86cqkv4djmusut4fjzzvzu2ut5z3uda4eedvff6anlmzz3k48d7ugxsrs0gxfn36nscvqqzn4js8\n+```\n+\n+At this point, the ballot is verified and its amount can be accounted\n+for but the vote payload is not revealed.\n+\n+:::info\n+The EB publishes the ballot hashes and receipts on their website.\n+:::\n+\n+## Opening Votes\n+\n+Voters uploads their signing key to the EB.\n+\n+```\n+>> load-identity voter.pem\n+c76a89174ed64479c5f567b955ce27ce998568e72a044de80b0ebe748d826b3c\n+>> open-proof pob.bin\n+5c70068567d0b8d52e4dcfc395a67dc83fd6bdf9c65a835b41d2bfeb776480fb\n+Decrypted message: Vote for BOB!\n+```\n+\n+:::info\n+The EB counts the votes and publishes the results\n+:::\n+\n+## Audit\n+\n+:::info\n+The EB publishes all ballots and their opening keys for verification\n+by auditors.\n+:::"}],"filesTruncated":false,"preview":{"filename":"zip-0340.md","title":"Coin Voting ZIP","status":"Draft","headings":[{"level":2,"id":"title-coin-votingbreaks-falsetags-coin_voting","text":"title: Coin Voting breaks: false tags: coin_voting"},{"level":1,"id":"coin-voting","text":"Coin Voting"},{"level":2,"id":"use-case","text":"Use Case"},{"level":2,"id":"other-uses-cases","text":"Other Uses Cases"},{"level":3,"id":"airdrop","text":"Airdrop"},{"level":3,"id":"proof-of-balance","text":"Proof of Balance"},{"level":1,"id":"requirements","text":"Requirements"},{"level":2,"id":"participants--actors","text":"Participants / Actors"},{"level":3,"id":"election-board","text":"Election Board"},{"level":3,"id":"voter-zh","text":"Voter (ZH)"},{"level":2,"id":"privacy","text":"Privacy"},{"level":2,"id":"workflow","text":"Workflow"},{"level":2,"id":"ballots","text":"Ballots"},{"level":2,"id":"lockdown-period--ballot-list-commitment","text":"Lockdown Period / Ballot List commitment"},{"level":2,"id":"counting","text":"Counting"},{"level":2,"id":"verification","text":"Verification"},{"level":1,"id":"specifications","text":"Specifications"},{"level":2,"id":"eb","text":"EB"},{"level":2,"id":"ballot","text":"Ballot"},{"level":3,"id":"ballot-hash--sighash","text":"Ballot Hash / Sighash"},{"level":2,"id":"message-encryption","text":"Message Encryption"},{"level":1,"id":"poc","text":"POC"},{"level":2,"id":"pollelection-setup","text":"Poll/Election Setup"},{"level":2,"id":"voter-send-their-ballots","text":"Voter Send their Ballots"},{"level":2,"id":"eb-collects-votes","text":"EB Collects Votes"},{"level":2,"id":"opening-votes","text":"Opening Votes"},{"level":2,"id":"audit","text":"Audit"}],"format":"md"}},{"number":1244,"title":"[ZIP TBD] Shielded Voting Wallet API","author":"czarcas7ic","createdAt":"2026-03-30T14:13:09Z","updatedAt":"2026-04-22T01:09:36Z","url":"https://github.com/zcash/zips/pull/1244","diffUrl":"https://github.com/zcash/zips/pull/1244.diff","headRef":"adam/shielded-voting-wallet-api","headRepo":"valargroup/zips","headSha":"1fc4e78134f03c4a5e307f3511847cd5807ffef8","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"## Summary - Adds a standalone wallet API ZIP (`draft-valargroup-shielded-voting-wallet-api.md`) specifying the REST endpoints, JSON wire formats, and discovery mechanism for wallet integration with shielded on-chain voting. - Covers vote discovery (per-vote configuration document), chain query endp","zipRefs":[],"draftRefs":["draft-valargroup-shielded-voting-wallet-api"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-valargroup-shielded-voting-wallet-api.md","status":"added","additions":861,"deletions":0,"patch":"@@ -0,0 +1,861 @@\n+```\n+ZIP: Unassigned\n+Title: Shielded Voting Wallet API\n+Owners: Dev Ojha <dojha@berkeley.edu>\n+        Adam Tucker <adamleetucker@outlook.com>\n+        Roman Akhtariev <ackhtariev@gmail.com>\n+        Greg Nagy <greg@dhamma.works>\n+Status: Draft\n+Category: Standards / Wallet\n+Created: 2026-03-24\n+License: MIT\n+Pull-Request: <https://github.com/zcash/zips/pull/1244>\n+```\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document\n+are to be interpreted as described in BCP 14 [^BCP14] when, and only\n+when, they appear in all capitals.\n+\n+The terms below are to be interpreted as follows:\n+\n+Vote round\n+\n+: A time-bounded voting session defining a set of proposals, a Zcash\n+  snapshot height, and a deadline. Wallet clients interact with exactly\n+  one vote round at a time.\n+\n+Delegation\n+\n+: The act of proving ownership of unspent Orchard notes at the snapshot\n+  height and registering a vote authority note on the vote commitment\n+  tree. See [^orchard-balance-proof].\n+\n+Vote authority note (VAN)\n+\n+: A note appended to the vote commitment tree during delegation. The VAN\n+  carries the delegated vote weight and is consumed (nullified) when\n+  the holder casts a vote.\n+\n+Vote commitment tree\n+\n+: An append-only Merkle tree that records vote authority notes and vote\n+  commitments. The tree root at a given block height serves as a public\n+  input to zero-knowledge proof verification.\n+\n+Share\n+\n+: A fragment of the holder's delegated vote weight, encrypted and\n+  submitted to helper servers rather than directly to the chain to\n+  prevent timing-based linkability.\n+\n+# Abstract\n+\n+This ZIP specifies the REST API endpoints, wire formats, and discovery\n+mechanism that wallet clients use to participate in shielded on-chain\n+voting rounds. It covers vote round discovery via a per-vote\n+configuration document, data query endpoints for reading chain state,\n+transaction submission endpoints for delegation and vote casting, and\n+the encoding conventions for all exchanged data.\n+\n+# Motivation\n+\n+The shielded voting protocol involves multiple ZIPs that specify the\n+cryptographic circuits [^voting-protocol], nullifier retrieval\n+[^nullifier-pir], proof-of-balance [^orchard-balance-proof], share\n+submission [^submission-server], and election authority key ceremony\n+[^ea-ceremony]. A wallet integrator currently must read several of these\n+specifications to understand which endpoints to call, what wire formats\n+to use, and how to discover an active vote.\n+\n+This ZIP consolidates the wallet-facing API surface into a single\n+document, specifying the REST endpoints, JSON wire formats, encoding\n+conventions, and discovery mechanism needed to participate in a vote.\n+\n+Versioning fields in the vote configuration allow the protocol to evolve\n+(new PIR schemes, circuit versions, tally methods) while maintaining\n+backwards compatibility with deployed wallets.\n+\n+# Requirements\n+\n+- A wallet can discover and join an active voting round using a published\n+configuration document.\n+- A wallet can submit delegation and vote commitment transactions\n+using the wire formats in this specification. Proof construction is\n+specified in companion ZIPs.\n+- A wallet can submit encrypted vote shares to helper servers and\n+confirm their on-chain inclusion.\n+- Each protocol component (vote server, vote protocol, tally method,\n+PIR) can be versioned and upgraded independently. A change to one\n+component has no impact on other components or the configuration schema.\n+\n+# Non-requirements\n+\n+- Validator onboarding, key registration, and EA key ceremony.\n+- Chain consensus rules and block production.\n+- Round creation and governance authority operations.\n+- The internal implementation of helper servers (specified in\n+[^submission-server]).\n+\n+# High level summary\n+\n+This section is non-normative.\n+\n+This section provides an informational overview of the end-to-end\n+sequence a wallet follows to participate in a shielded voting round.\n+Each step references the normative section that specifies its details.\n+All requirements use the language defined in those sections.\n+\n+The vote configuration itself is versioned by `config_version`, which\n+tracks the schema of the configuration document. It also carries four\n+independently versioned protocol components:\n+\n+- **`vote_protocol`** — the ZKP circuits (ZKP1, ZKP2, ZKP3) and\n+  commitment tree structure. The circuits are designed to be\n+  upgradeable: a new circuit version bumps `vote_protocol` without\n+  affecting the other components.\n+- **`tally`** — threshold decryption and result aggregation.\n+- **`pir`** — the nullifier PIR retrieval scheme.\n+- **`vote_server`** — the helper server API that vote shares are submitted to.\n+\n+See [Version Handling] for the normative rules.\n+\n+## Discovery and Validation\n+\n+1. **Obtain vote configuration.** Fetch or receive the vote\n+   configuration JSON document for the round.\n+   See [Vote Configuration Format].\n+\n+2. **Validate configuration.** Check all fields against the rules in\n+   [Validation Rules] and verify version compatibility per\n+   [Compatibility Rules]. Reject the configuration and stop if any\n+   check fails.\n+\n+3. **Fetch active round from chain.** Query `GET /shielded-vote/v1/rounds/active`\n+   to confirm the round is ACTIVE and retrieve on-chain parameters:\n+   `ea_pk`, `nullifier_imt_root`, `nc_root`, and `proposals_hash`.\n+   See [Active Round].\n+\n+4. **Verify proposals hash.** Compute the proposals hash from the\n+   configuration's `proposals` array and compare it to `proposals_hash`\n+   from the chain response. See [Proposals Hash].\n+\n+## Delegation\n+\n+5. **Retrieve nullifier exclusion proofs.** Connect to a\n+   `pir_endpoints` server and retrieve Merkle non-membership proofs\n+   for the wallet's Orchard note nullifiers at `snapshot_height`.\n+   See [Nullifier Retrieval] and [^nullifier-pir].\n+\n+6. **Construct and submit delegation transaction.** Build the ZKP1\n+   proof (proving Orchard note ownership at the snapshot height) and\n+   submit via `POST /shielded-vote/v1/delegate-vote`.\n+   See [Delegation Transaction] and [^orchard-balance-proof].\n+\n+7. **Poll for delegation confirmation.** Poll\n+   `GET /shielded-vote/v1/tx/{hash}` using the `tx_hash` from the\n+   submission response until the response includes a non-empty `height`\n+   and `code` = 0. See [Confirmation Polling].\n+\n+8. **Sync commitment tree and locate VAN.** Query the\n+   [Commitment Tree Leaves] endpoint to incrementally sync the local\n+   tree. Identify the wallet's vote authority note by its commitment\n+   `van_cmx` computed during step 6.\n+\n+## Voting (repeat for each proposal)\n+\n+9. **Construct and submit vote commitment.** Build the ZKP2 proof\n+   (consuming the current VAN and producing a new VAN) and submit\n+   via `POST /shielded-vote/v1/cast-vote`. The tree root at the\n+   anchor height is a public input to this proof.\n+   See [Vote Commitment Transaction] and [^voting-protocol].\n+\n+10. **Poll for vote commitment confirmation.** Poll\n+    `GET /shielded-vote/v1/tx/{hash}` until confirmed.\n+\n+11. **Sync commitment tree.** Query [Commitment Tree Leaves] again\n+    to locate the new VAN (needed as input for the next proposal)\n+    and the vote commitment leaf (needed for share construction).\n+\n+12. **Construct and submit shares.** Build 16 encrypted share\n+    payloads and submit each to a helper server via\n+    `POST /shielded-vote/v1/shares`. Each share references the\n+    `tree_position` of the vote commitment leaf from step 11.\n+    See [Share Delegation] and [^submission-server].\n+\n+13. **Poll share statuses.** For each submitted share, poll\n+    `GET /shielded-vote/v1/share-status/{roundId}/{nullifier}` until\n+    all return `\"confirmed\"`. See [Share Status].\n+\n+Steps 9 through 13 are repeated sequentially for each proposal in\n+the round. Each iteration consumes the current VAN and produces a\n+new one, so proposals cannot be voted on in parallel.\n+\n+## Results (optional)\n+\n+14. **View tally results.** After the round reaches FINALIZED status,\n+    query `GET /shielded-vote/v1/tally-results/{round_id}` for\n+    decrypted per-proposal tallies. See [Tally Results].\n+\n+# Specification\n+\n+## Vote Discovery\n+\n+A vote configuration is a JSON document published for each vote round.\n+It contains all parameters a wallet needs to locate services and\n+participate in the round.\n+\n+### Vote Configuration Format\n+\n+```json\n+{\n+  \"config_version\": 1,\n+  \"vote_round_id\": \"<hex, 64 characters>\",\n+  \"vote_servers\": [\n+    {\"url\": \"https://vote1.example.com\", \"label\": \"validator-1\"}\n+  ],\n+  \"pir_endpoints\": [\n+    {\"url\": \"https://pir1.example.com\", \"label\": \"pir-1\"}\n+  ],\n+  \"snapshot_height\": 2800000,\n+  \"vote_end_time\": 1735689600,\n+  \"proposals\": [\n+    {\n+      \"id\": 1,\n+      \"title\": \"Approve protocol upgrade\",\n+      \"description\": \"Approve or oppose the proposed protocol upgrade.\",\n+      \"options\": [\n+        {\"index\": 0, \"label\": \"Support\"},\n+        {\"index\": 1, \"label\": \"Oppose\"}\n+      ]\n+    }\n+  ],\n+  \"supported_versions\": {\n+    \"pir\": [\"v0\", \"v1\"],\n+    \"vote_protocol\": \"v0\",\n+    \"tally\": \"v0\",\n+    \"vote_server\": \"v1\"\n+  }\n+}\n+```\n+\n+### Field Definitions\n+\n+\n+| Field                              | Type             | Description                                                                                                                    |\n+| ---------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n+| `config_version`                   | integer          | Schema version of this configuration document. Currently 1.                                                                    |\n+| `vote_round_id`                    | string           | Hex-encoded 32-byte vote round identifier (64 characters, lowercase).                                                          |\n+| `vote_servers`                     | array            | One or more vote server base URLs serving both chain and helper endpoints. Each entry has `url` (string) and `label` (string). |\n+| `pir_endpoints`                    | array            | One or more nullifier PIR server base URLs. Each entry has `url` and `label`.                                                  |\n+| `snapshot_height`                  | integer          | Zcash block height at which the Orchard pool snapshot was taken.                                                               |\n+| `vote_end_time`                    | integer          | Unix timestamp (seconds) after which votes are no longer accepted.                                                             |\n+| `proposals`                        | array            | Ordered list of proposals. Each has `id` (integer, 1-indexed), `title` (string), `description` (string), and `options` (array of `{index, label}`). |\n+| `supported_versions.pir`           | array of strings | PIR retrieval scheme versions supported by the servers (e.g., `[\"v0\", \"v1\"]`).                                                 |\n+| `supported_versions.vote_protocol` | string           | Vote protocol version covering the ZKP circuits and commitment tree structure (e.g., `\"v0\"`).                                  |\n+| `supported_versions.tally`         | string           | Tally method version covering threshold decryption and result aggregation (e.g., `\"v0\"`).                                      |\n+| `supported_versions.vote_server`   | string           | Vote server version covering the REST API (e.g., `\"v1\"`).                                                                      |\n+\n+\n+### Validation Rules\n+\n+A wallet MUST validate the configuration before use:\n+\n+- `config_version` MUST be a version the wallet recognizes. This\n+specification defines version 1.\n+- `vote_round_id` MUST be exactly 64 lowercase hexadecimal characters.\n+- `vote_servers` MUST contain at least one entry.\n+- `pir_endpoints` MUST contain at least one entry.\n+- `snapshot_height` MUST be greater than 0.\n+- `proposals` MUST contain between 1 and 15 entries.\n+- Each proposal MUST have between 2 and 8 options.\n+- Proposal `id` values MUST be unique and in the range 1 to 15.\n+- Option `index` values within a proposal MUST be unique and 0-indexed.\n+- The wallet MUST check version compatibility as specified in\n+[Version Handling]. In summary: `supported_versions.vote_server`,\n+`supported_versions.vote_protocol`, and `supported_versions.tally`\n+MUST be recognized versions; `supported_versions.pir` MUST contain\n+at least one version the wallet supports.\n+\n+### Distribution\n+\n+The vote configuration is published out-of-band for each vote round.\n+Distribution mechanisms include:\n+\n+- A developer-merged pull request to a well-known repository linking\n+to the configuration file.\n+- A CDN or API endpoint serving the configuration.\n+- Bundling the configuration within a wallet release.\n+\n+The choice of distribution mechanism is outside the scope of this\n+specification. Regardless of the mechanism, the wallet MUST validate the\n+configuration as described above before using it.\n+\n+## Data Query Endpoints\n+\n+All query endpoints are served relative to a `vote_servers` base URL\n+from the vote configuration. Responses are JSON-serialized protobuf\n+messages; byte fields are base64-encoded.\n+\n+### Active Round\n+\n+```\n+GET /shielded-vote/v1/rounds/active\n+```\n+\n+Returns the active voting round, if any.\n+\n+**Response body:** A JSON object containing a `round` field with the\n+`VoteRound` structure:\n+\n+\n+| Field                | Type              | Description                                                          |\n+| -------------------- | ----------------- | -------------------------------------------------------------------- |\n+| `vote_round_id`      | base64 (32 bytes) | Round identifier.                                                    |\n+| `snapshot_height`    | uint64            | Zcash snapshot block height.                                         |\n+| `snapshot_blockhash` | base64 (32 bytes) | Zcash block hash at snapshot.                                        |\n+| `proposals_hash`     | base64 (32 bytes) | SHA-256 hash of the proposals array (see [Proposals Hash]).          |\n+| `vote_end_time`      | uint64            | Unix timestamp (seconds).                                            |\n+| `nullifier_imt_root` | base64 (32 bytes) | Nullifier non-membership tree root.                                  |\n+| `nc_root`            | base64 (32 bytes) | Orchard note commitment tree root.                                   |\n+| `status`             | uint32            | Session status enum (4=PENDING, 1=ACTIVE, 2=TALLYING, 3=FINALIZED).  |\n+| `ea_pk`              | base64 (32 bytes) | Election authority public key (compressed Pallas point).             |\n+| `proposals`          | array             | Proposals with `id` (uint32), `title`, `description`, and `options`. |\n+| `description`        | string            | Human-readable round description.                                    |\n+| `title`              | string            | Short human-readable round title.                                    |\n+| `creator`            | string            | Address of the account that created the session.                     |\n+| `created_at_height`  | uint64            | Vote chain block height at which the round was created.              |\n+\n+\n+The response may contain additional fields related to the EA key\n+ceremony and threshold decryption (e.g., ceremony status, validator\n+keys, ECIES payloads). These fields exist for validator coordination\n+and have no bearing on wallet operations, so they are not documented\n+here. See [^ea-ceremony] for details.\n+\n+The `proposals` field in the VoteRound response contains the same\n+proposals as the vote configuration document. The `proposals_hash`\n+field can be used to verify consistency between the two sources.\n+\n+### Proposals Hash\n+\n+The `proposals_hash` is SHA-256 over the canonical JSON serialization\n+of the `proposals` array, allowing wallets to verify that the\n+proposals shown to the user match those on the vote chain.\n+\n+To compute: construct a JSON array of proposal objects containing\n+`id`, `title`, `description`, and `options` (each with `index`, `label`),\n+ordered by `id` then `index` ascending, with object keys in the order\n+listed. Serialize with no whitespace and SHA-256 the UTF-8 result. The\n+JSON serialization MUST NOT escape the forward slash character (`/`);\n+non-ASCII characters are emitted as UTF-8 bytes, not as `\\uXXXX`\n+escapes. Control characters (U+0000 through U+001F) are escaped per\n+RFC 8259 (`\\b`, `\\f`, `\\n`, `\\r`, `\\t`, or `\\u00XX`).\n+\n+Example preimage (from [Vote Configuration Format]):\n+\n+```\n+[{\"id\":1,\"title\":\"Approve protocol upgrade\",\"description\":\"Approve or oppose the proposed protocol upgrade.\",\"options\":[{\"index\":0,\"label\":\"Support\"},{\"index\":1,\"label\":\"Oppose\"}]}]\n+```\n+\n+SHA-256 of the above preimage: `3f9a361d43c4ddb77ad138a091374e2e2958718e64937f33df99a09bd567e63d`.\n+\n+Wallets SHOULD verify `proposals_hash` against the vote\n+configuration before proceeding.\n+\n+If no active round exists, the response contains a `round` field with\n+a null or empty value.\n+\n+### Round Details\n+\n+```\n+GET /shielded-vote/v1/round/{round_id}\n+```\n+\n+Returns details for a specific vote round.\n+\n+**Path parameters:**\n+\n+- `round_id`: Hex-encoded 32-byte vote round identifier (64 characters).\n+\n+**Response body:** Same `VoteRound` structure as [Active Round].\n+\n+### List Rounds\n+\n+```\n+GET /shielded-vote/v1/rounds\n+```\n+\n+Returns all stored vote rounds.\n+\n+**Response body:** A JSON object containing a `rounds` array of\n+`VoteRound` structures.\n+\n+### Commitment Tree (Latest)\n+\n+```\n+GET /shielded-vote/v1/commitment-tree/{round_id}/latest\n+```\n+\n+Returns the latest commitment tree state for a round.\n+\n+**Path parameters:**\n+\n+- `round_id`: Hex-encoded 32-byte vote round identifier.\n+\n+**Response body:** A JSON object containing a `tree` field:\n+\n+\n+| Field                | Type              | Description                                   |\n+| -------------------- | ----------------- | --------------------------------------------- |\n+| `next_index`         | uint64            | Next leaf index to be written.                |\n+| `root`               | base64 (32 bytes) | Current Merkle root.                          |\n+| `height`             | uint64            | Block height at which this root was computed. |\n+| `next_index_at_root` | uint64            | `next_index` at the time the root was stored. |\n+\n+\n+### Commitment Tree at Height\n+\n+```\n+GET /shielded-vote/v1/commitment-tree/{round_id}/{height}\n+```\n+\n+Returns the commitment tree state at a specific block height.\n+\n+**Path parameters:**\n+\n+- `round_id`: Hex-encoded 32-byte vote round identifier.\n+- `height`: Block height (decimal integer).\n+\n+**Response body:** Same `CommitmentTreeState` structure as\n+[Commitment Tree (Latest)].\n+\n+### Commitment Tree Leaves\n+\n+```\n+GET /shielded-vote/v1/commitment-tree/{round_id}/leaves?from_height=X&to_height=Y\n+```\n+\n+Returns commitment tree leaves appended in blocks within the specified\n+height range. Used by wallet clients to incrementally sync the local\n+copy of the vote commitment tree.\n+\n+**Path parameters:**\n+\n+- `round_id`: Hex-encoded 32-byte vote round identifier.\n+\n+**Query parameters:**\n+\n+- `from_height`: Start block height (inclusive).\n+- `to_height`: End block height (inclusive).\n+\n+**Response body:** A JSON object containing a `blocks` array. Each\n+entry represents one block:\n+\n+\n+| Field         | Type                       | Description                                                         |\n+| ------------- | -------------------------- | ------------------------------------------------------------------- |\n+| `height`      | uint64                     | Block height.                                                       |\n+| `start_index` | uint64                     | Index of the first leaf appended in this block.                     |\n+| `leaves`      | array of base64 (32 bytes) | Commitment leaves (Pallas base field elements, little-endian each). |\n+\n+\n+### Tally Results\n+\n+```\n+GET /shielded-vote/v1/tally-results/{round_id}\n+```\n+\n+Returns finalized (decrypted) tally results for a vote round. Only\n+available after the round reaches FINALIZED status.\n+\n+**Path parameters:**\n+\n+- `round_id`: Hex-encoded 32-byte vote round identifier.\n+\n+**Response body:** A JSON object containing a `results` array:\n+\n+\n+| Field           | Type              | Description                          |\n+| --------------- | ----------------- | ------------------------------------ |\n+| `vote_round_id` | base64 (32 bytes) | Round identifier.                    |\n+| `proposal_id`   | uint32            | Proposal identifier.                 |\n+| `vote_decision` | uint32            | Vote option index.                   |\n+| `total_value`   | uint64            | Decrypted aggregate value (zatoshi). |\n+\n+\n+### Transaction Status\n+\n+```\n+GET /shielded-vote/v1/tx/{hash}\n+```\n+\n+Returns the confirmation status of a previously submitted transaction.\n+\n+**Path parameters:**\n+\n+- `hash`: Hex-encoded transaction hash.\n+\n+**Response body:**\n+\n+\n+| Field    | Type   | Description                                                            |\n+| -------- | ------ | ---------------------------------------------------------------------- |\n+| `height` | string | Block height at which the transaction was included (empty if pending). |\n+| `code`   | uint32 | Result code (0 = success).                                             |\n+| `log`    | string | Error message if `code` is non-zero.                                   |\n+| `events` | array  | ABCI events emitted by the transaction.                                |\n+\n+\n+## Delegation Transaction\n+\n+The delegation transaction registers a holder's vote weight on the vote\n+commitment tree. It corresponds to ZKP1 (the delegation circuit) as\n+specified in [^orchard-balance-proof] and [^voting-protocol].\n+\n+### Endpoint\n+\n+```\n+POST /shielded-vote/v1/delegate-vote\n+```\n+\n+### Request Body\n+\n+A JSON object with the following fields:\n+\n+\n+| Field                   | Type                            | Description                                                                |\n+| ----------------------- | ------------------------------- | -------------------------------------------------------------------------- |\n+| `rk`                    | base64 (32 bytes)               | Randomized spend authorization verification key (compressed Pallas point). |\n+| `spend_auth_sig`        | base64 (64 bytes)               | RedPallas spend authorization signature.                                   |\n+| `signed_note_nullifier` | base64 (32 bytes)               | Nullifier of the dummy signed note.                                        |\n+| `cmx_new`               | base64 (32 bytes)               | Output note commitment (extracted x-coordinate).                           |\n+| `van_cmx`               | base64 (32 bytes)               | Vote authority note commitment (extracted x-coordinate).                   |\n+| `gov_nullifiers`        | array of base64 (32 bytes each) | Governance nullifiers (up to 5). One per claimed note.                     |\n+| `proof`                 | base64 (variable)               | Halo 2 ZKP1 proof.                                                         |\n+| `vote_round_id`         | base64 (32 bytes)               | Vote round identifier.                                                     |\n+| `sighash`               | base64 (32 bytes)               | Client-computed sighash for signature verification.                        |\n+\n+\n+### Sighash\n+\n+The `sighash` field is the 32-byte ZIP 244 [^zip-244] shielded sighash\n+extracted from the signed PCZT after the hardware wallet signing flow.\n+The chain verifies the `spend_auth_sig` against this client-provided\n+sighash; it does not recompute it. See [^orchard-balance-proof] for the\n+PCZT construction and signing flow that produces the sighash.\n+\n+### Response\n+\n+All transaction submission endpoints return the same response format:\n+\n+```json\n+{\n+  \"tx_hash\": \"<hex-encoded transaction hash>\",\n+  \"code\": 0,\n+  \"log\": \"\"\n+}\n+```\n+\n+\n+| Field     | Type         | Description                                                             |\n+| --------- | ------------ | ----------------------------------------------------------------------- |\n+| `tx_hash` | string (hex) | Transaction hash for status polling.                                    |\n+| `code`    | uint32       | Result code. 0 indicates the transaction was accepted into the mempool. |\n+| `log`     | string       | Error description when `code` is non-zero. Omitted on success.          |\n+\n+\n+## Vote Commitment Transaction\n+\n+The vote commitment transaction casts a vote on a specific proposal. It\n+corresponds to ZKP2 (the vote commitment circuit) as specified in\n+[^voting-protocol].\n+\n+### Endpoint\n+\n+```\n+POST /shielded-vote/v1/cast-vote\n+```\n+\n+### Request Body\n+\n+A JSON object with the following fields:\n+\n+\n+| Field                          | Type              | Description                                                   |\n+| ------------------------------ | ----------------- | ------------------------------------------------------------- |\n+| `van_nullifier`                | base64 (32 bytes) | Nullifier of the vote authority note being consumed.          |\n+| `vote_authority_note_new`      | base64 (32 bytes) | New vote authority note commitment.                           |\n+| `vote_commitment`              | base64 (32 bytes) | Vote commitment (Poseidon hash binding the vote).             |\n+| `proposal_id`                  | uint32            | Proposal identifier (1 to 15).                                |\n+| `proof`                        | base64 (variable) | Halo 2 ZKP2 proof.                                            |\n+| `vote_round_id`                | base64 (32 bytes) | Vote round identifier.                                        |\n+| `vote_comm_tree_anchor_height` | uint64            | Block height of the vote commitment tree root used as anchor. |\n+| `vote_auth_sig`                | base64 (64 bytes) | RedPallas signature under the randomized voting key.          |\n+| `r_vpk`                        | base64 (32 bytes) | Randomized voting public key (compressed Pallas point).       |\n+\n+\n+### Response\n+\n+Same response format as [Delegation Transaction].\n+\n+## Share Delegation\n+\n+After casting a vote commitment, the wallet constructs encrypted share\n+payloads and submits them to helper servers. The helper server queues\n+each share and submits it to the chain at the client-specified time.\n+See [^submission-server] for the server-side processing pipeline and\n+[^voting-protocol] for share construction.\n+\n+The following endpoints are served from the same `vote_servers` base\n+URLs as the chain query endpoints.\n+\n+### Submit Share\n+\n+```\n+POST /shielded-vote/v1/shares\n+```\n+\n+Submits a single encrypted vote share to a helper server.\n+\n+**Request body:** A JSON object with the following fields:\n+\n+\n+| Field           | Type                            | Description                                                                                                                                            |\n+| --------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |\n+| `shares_hash`   | base64 (32 bytes)               | Poseidon hash committing to all shares in this vote.                                                                                                   |\n+| `proposal_id`   | uint32                          | Proposal identifier (1-indexed).                                                                                                                       |\n+| `vote_decision` | uint32                          | Vote option index (0-indexed).                                                                                                                         |\n+| `enc_share`     | object                          | Encrypted share (see below).                                                                                                                           |\n+| `tree_position` | uint64                          | Vote commitment tree leaf index.                                                                                                                       |\n+| `vote_round_id` | string (hex, 64 chars)          | Hex-encoded vote round identifier.                                                                                                                     |\n+| `share_comms`   | array of base64 (32 bytes each) | 16 per-share Poseidon commitments.                                                                                                                     |\n+| `primary_blind` | base64 (32 bytes)               | Blinding factor for the revealed share.                                                                                                                |\n+| `submit_at`     | uint64                          | Unix timestamp (seconds) at which the helper should submit this share to the chain. 0 means submit at the last possible moment before `vote_end_time`. |\n+\n+\n+The `enc_share` object contains:\n+\n+\n+| Field         | Type              | Description                                                  |\n+| ------------- | ----------------- | ------------------------------------------------------------ |\n+| `c1`          | base64 (32 bytes) | ElGamal ciphertext component `C1` (compressed Pallas point). |\n+| `c2`          | base64 (32 bytes) | ElGamal ciphertext component `C2` (compressed Pallas point). |\n+| `share_index` | uint32            | Share index (0 to 15).                                       |\n+\n+\n+**Response body:**\n+\n+```json\n+{\"status\": \"queued\"}\n+```\n+\n+\n+| Field    | Type   | Description                                                |\n+| -------- | ------ | ---------------------------------------------------------- |\n+| `status` | string | `\"queued\"` if accepted, `\"duplicate\"` if already received. |\n+| `error`  | string | Error description (present only on failure).               |\n+\n+\n+### Share Nullifier\n+\n+Each submitted share has a deterministic nullifier derived from the\n+vote commitment fields and the share's blinding factor. The wallet\n+computes this nullifier locally and hex-encodes it (lowercase, 64\n+characters) for use in the [Share Status] endpoint path. The\n+derivation is specified in [^voting-protocol].\n+\n+### Share Status\n+\n+```\n+GET /shielded-vote/v1/share-status/{roundId}/{nullifier}\n+```\n+\n+Polls whether a submitted share has been included on-chain.\n+\n+**Path parameters:**\n+\n+- `roundId`: Hex-encoded 32-byte vote round identifier (64 characters).\n+- `nullifier`: Hex-encoded 32-byte share nullifier (64 characters),\n+  computed as specified in [Share Nullifier].\n+\n+**Response body:**\n+\n+```json\n+{\"status\": \"pending\"}\n+```\n+\n+\n+| Field    | Type   | Description                                                                                       |\n+| -------- | ------ | ------------------------------------------------------------------------------------------------- |\n+| `status` | string | `\"pending\"` if not yet on-chain, `\"confirmed\"` if the share nullifier has been recorded on-chain. |\n+\n+\n+## Vote Commitment Tree\n+\n+The vote commitment tree is an append-only Merkle tree that records\n+vote authority notes and vote commitments.\n+\n+The vote commitment tree structure, hash function, and leaf encoding\n+are specified in [^voting-protocol]. Wallet clients interact with the\n+tree through the query endpoints defined in [Commitment Tree (Latest)],\n+[Commitment Tree at Height], and [Commitment Tree Leaves].\n+\n+## Private Information Retrieval Nullifier Exclusion Proofs\n+\n+Nullifier exclusion proofs are retrieved using the PIR protocol\n+specified in [^nullifier-pir]. The wallet connects to one of the\n+`pir_endpoints` from the vote configuration; version selection\n+follows the rules in [Version Handling].\n+\n+## Version Handling\n+\n+All version strings in `supported_versions` use the form `\"v\" MAJOR`\n+(e.g., `\"v0\"`, `\"v1\"`). A major version bump indicates a\n+breaking change; within the same major version, implementations remain\n+compatible.\n+\n+A wallet MUST reject the configuration if it does not support the\n+advertised `vote_server`, `vote_protocol`, or `tally` version, or if\n+`pir` contains no version it supports. If any check fails, the wallet\n+MUST NOT proceed and SHOULD prompt the user to update.\n+\n+### URL Path Prefix\n+\n+REST endpoint paths include a version prefix (e.g.,\n+`/shielded-vote/v1/`). The path version corresponds to the major\n+version declared in `supported_versions.vote_server`. A `vote_server`\n+value of `\"v1\"` uses the `/shielded-vote/v1/` prefix.\n+\n+### Relationship to `config_version`\n+\n+`config_version` versions the structure of the vote configuration JSON\n+document itself (field names, types, nesting). The component versions\n+(`vote_server`, `vote_protocol`, `tally`, `pir`) version protocol\n+behavior. These can all evolve independently: a structural change to\n+the config schema (e.g., adding a new required top-level field) bumps\n+`config_version`, while a change to endpoint behavior bumps\n+`vote_server`, a change to circuits or tree structure bumps\n+`vote_protocol`, and a change to decryption or aggregation bumps\n+`tally`.\n+\n+## Transaction Lifecycle\n+\n+### Broadcast Semantics\n+\n+Transaction submission endpoints (`/delegate-vote`, `/cast-vote`)\n+return synchronously after initial validation. A successful response\n+(HTTP 200, `code` = 0) indicates that the transaction passed validation\n+and entered the mempool. It does not guarantee inclusion in a block.\n+\n+### Confirmation Polling\n+\n+After receiving a successful broadcast response, the wallet SHOULD poll\n+the [Transaction Status] endpoint using the `tx_hash` from the response.\n+The transaction is confirmed when the response includes a non-empty\n+`height` and `code` = 0.\n+\n+### Timeouts\n+\n+Transaction validation includes zero-knowledge proof verification,\n+which may take 30 to 60 seconds. Wallet HTTP clients SHOULD use a\n+timeout of at least 120 seconds for transaction submission requests.\n+\n+## Encoding Conventions\n+\n+All data exchanged between wallet clients and servers uses the encodings\n+described in this section.\n+\n+### Cryptographic Types\n+\n+\n+| Type                      | Size     | Encoding                                                                                                     |\n+| ------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |\n+| Pallas base field element | 32 bytes | Little-endian canonical representation. Implementations MUST reject values >= the Pallas base field modulus. |\n+| Compressed Pallas point   | 32 bytes | Standard Pallas point compression. [^protocol]                                                               |\n+| ElGamal ciphertext        | 64 bytes | `C1` (32 bytes) followed by `C2` (32 bytes), each a compressed Pallas point.                                 |\n+| Halo 2 proof              | variable | Opaque byte sequence.                                                                                        |\n+| RedPallas signature       | 64 bytes | `R` (32 bytes) followed by `s` (32 bytes).                                                                   |\n+\n+\n+### JSON Encoding\n+\n+All REST endpoints accept and return `application/json`.\n+\n+- **Byte arrays**: Standard base64 encoding with padding (RFC 4648\n+Section 4 [^rfc4648]).\n+- **`vote_round_id`**: The encoding of `vote_round_id` varies by\n+context. The following table lists every occurrence and its encoding:\n+\n+| Context                                         | Encoding                      |\n+| ----------------------------------------------- | ----------------------------- |\n+| Vote configuration JSON (`vote_round_id` field) | Hex (64 lowercase characters) |\n+| URL path parameters (`{round_id}`, `{roundId}`) | Hex (64 lowercase characters) |\n+| Delegation request body (`vote_round_id`)       | Base64 (32 bytes)             |\n+| Vote commitment request body (`vote_round_id`)  | Base64 (32 bytes)             |\n+| Share submission request body (`vote_round_id`) | Hex (64 lowercase characters) |\n+| Chain query response bodies (`vote_round_id`)   | Base64 (32 bytes)             |\n+- **Integers**: JSON numbers. Fields typed `uint32` or `uint64` in the\n+protocol definition are encoded as JSON numbers.\n+- **Enumerations**: JSON numbers corresponding to the protobuf enum\n+value (e.g., `SESSION_STATUS_ACTIVE` = 1).\n+\n+# Rationale\n+\n+## Unified Vote Servers\n+\n+All endpoints — chain queries, transaction submission, and share\n+submission — are served under the `/shielded-vote/v1/` path prefix\n+from the same `vote_servers` base URLs. In the current architecture,\n+a single `svoted` process hosts every endpoint on the same port, so a\n+separate helper URL is unnecessary.\n+\n+## Independent Component Versions\n+\n+Each `supported_versions` field tracks a component that can change on\n+its own schedule: `vote_server` covers the REST API surface,\n+`vote_protocol` covers the ZKP circuits and commitment tree structure,\n+`tally` covers threshold decryption and result aggregation, and `pir`\n+covers nullifier retrieval. Separating these avoids forcing a\n+wallet update when only one component changes — for example, a new\n+tally method does not require wallets to update their proof generation\n+code.\n+\n+## JSON over Protobuf\n+\n+The REST API uses JSON encoding rather than binary protobuf serialization.\n+JSON is broadly supported across wallet development stacks (Swift, Kotlin,\n+TypeScript, Rust) and does not require protobuf code generation. The\n+trade-off in message size is acceptable for the request and response\n+volumes involved.\n+\n+# Open issues\n+\n+# Reference implementation\n+\n+A reference implementation of the vote chain REST API and helper server\n+is available at\n+[valargroup/vote-sdk](https://github.com/valargroup/vote-sdk).\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 -- \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later](protocol/protocol.pdf)\n+\n+[^protocol-poseidon]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.2: Pseudo Random Functions](protocol/protocol.pdf#concreteprfs)\n+\n+[^rfc4648]: [RFC 4648: The Base16, Base32, and Base64 Data Encodings](https://www.rfc-editor.org/rfc/rfc4648)\n+\n+[^voting-protocol]: [Draft ZIP: Shielded Voting Protocol](draft-valargroup-shielded-voting.md)\n+\n+[^nullifier-pir]: [Draft ZIP: Private Information Retrieval for Nullifier Exclusion Proofs](draft-valargroup-nullifier-pir.md)\n+\n+[^submission-server]: [Draft ZIP: Shielded Voting Submission Server](draft-valargroup-submission-server.md)\n+\n+[^orchard-balance-proof]: [Draft ZIP: Orchard Proof-of-Balance](draft-valargroup-orchard-balance-proof.md)\n+\n+[^ea-ceremony]: [Draft ZIP: Election Authority Key Ceremony](draft-valargroup-ea-key-ceremony.md)\n+\n+[^zip-244]: [ZIP 244: Transaction Identifier Non-Malleability](zip-0244.rst)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-valargroup-shielded-voting-wallet-api.md","title":"[ZIP TBD] Shielded Voting Wallet API","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"high-level-summary","text":"High level summary"},{"level":2,"id":"discovery-and-validation","text":"Discovery and Validation"},{"level":2,"id":"delegation","text":"Delegation"},{"level":2,"id":"voting-repeat-for-each-proposal","text":"Voting (repeat for each proposal)"},{"level":2,"id":"results-optional","text":"Results (optional)"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"vote-discovery","text":"Vote Discovery"},{"level":3,"id":"vote-configuration-format","text":"Vote Configuration Format"},{"level":3,"id":"field-definitions","text":"Field Definitions"},{"level":3,"id":"validation-rules","text":"Validation Rules"},{"level":3,"id":"distribution","text":"Distribution"},{"level":2,"id":"data-query-endpoints","text":"Data Query Endpoints"},{"level":3,"id":"active-round","text":"Active Round"},{"level":3,"id":"proposals-hash","text":"Proposals Hash"},{"level":3,"id":"round-details","text":"Round Details"},{"level":3,"id":"list-rounds","text":"List Rounds"},{"level":3,"id":"commitment-tree-latest","text":"Commitment Tree (Latest)"},{"level":3,"id":"commitment-tree-at-height","text":"Commitment Tree at Height"},{"level":3,"id":"commitment-tree-leaves","text":"Commitment Tree Leaves"},{"level":3,"id":"tally-results","text":"Tally Results"},{"level":3,"id":"transaction-status","text":"Transaction Status"},{"level":2,"id":"delegation-transaction","text":"Delegation Transaction"},{"level":3,"id":"endpoint","text":"Endpoint"},{"level":3,"id":"request-body","text":"Request Body"},{"level":3,"id":"sighash","text":"Sighash"},{"level":3,"id":"response","text":"Response"},{"level":2,"id":"vote-commitment-transaction","text":"Vote Commitment Transaction"},{"level":3,"id":"endpoint-1","text":"Endpoint"},{"level":3,"id":"request-body-1","text":"Request Body"},{"level":3,"id":"response-1","text":"Response"},{"level":2,"id":"share-delegation","text":"Share Delegation"},{"level":3,"id":"submit-share","text":"Submit Share"},{"level":3,"id":"share-nullifier","text":"Share Nullifier"},{"level":3,"id":"share-status","text":"Share Status"},{"level":2,"id":"vote-commitment-tree","text":"Vote Commitment Tree"},{"level":2,"id":"private-information-retrieval-nullifier-exclusion-proofs","text":"Private Information Retrieval Nullifier Exclusion Proofs"},{"level":2,"id":"version-handling","text":"Version Handling"},{"level":3,"id":"url-path-prefix","text":"URL Path Prefix"},{"level":3,"id":"relationship-to-config_version","text":"Relationship to config_version"},{"level":2,"id":"transaction-lifecycle","text":"Transaction Lifecycle"},{"level":3,"id":"broadcast-semantics","text":"Broadcast Semantics"},{"level":3,"id":"confirmation-polling","text":"Confirmation Polling"},{"level":3,"id":"timeouts","text":"Timeouts"},{"level":2,"id":"encoding-conventions","text":"Encoding Conventions"},{"level":3,"id":"cryptographic-types","text":"Cryptographic Types"},{"level":3,"id":"json-encoding","text":"JSON Encoding"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"unified-vote-servers","text":"Unified Vote Servers"},{"level":2,"id":"independent-component-versions","text":"Independent Component Versions"},{"level":2,"id":"json-over-protobuf","text":"JSON over Protobuf"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1203,"title":"[ZIP TBD] Zcash Shielded Coinholder Voting","author":"greg0x","createdAt":"2026-03-05T23:00:05Z","updatedAt":"2026-04-10T20:21:09Z","url":"https://github.com/zcash/zips/pull/1203","diffUrl":"https://github.com/zcash/zips/pull/1203.diff","headRef":"greg/shielded-voting-setup","headRepo":"valargroup/zips","headSha":"d3d3c3c0c4110a40eaed10829ccd5d3512affd19","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Introduces the initial draft of the shielded voting setup specification from Valar Group. This ZIP defines the cryptographic setup phase for shielded on-chain voting, covering key generation, ballot structure, and the trust model for election authorities.","zipRefs":[],"draftRefs":["draft-valargroup-shielded-voting-setup"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-valargroup-shielded-voting-setup.md","status":"added","additions":782,"deletions":0,"patch":"@@ -0,0 +1,782 @@\n+    ZIP: Unassigned\n+    Title: Zcash Shielded Coinholder Voting\n+    Owners: Dev Ojha <dojha@berkeley.edu>\n+            Roman Akhtariev <ackhtariev@gmail.com>\n+            Adam Tucker <adamleetucker@outlook.com>\n+            Greg Nagy <greg@dhamma.works>\n+    Status: Draft\n+    Category: Process\n+    Created: 2026-03-04\n+    License: MIT\n+    Pull-Request: <https://github.com/zcash/zips/pull/???>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\" and \"MAY\" in this document are to be\n+interpreted as described in BCP 14 [^BCP14] when, and only when,\n+they appear in all capitals.\n+\n+The terms below are to be interpreted as follows:\n+\n+Vote chain\n+: The blockchain that serves as the single source of truth for voting\n+  operations. See [System Overview] for the state it maintains.\n+\n+Voting round\n+: A complete instance of a coinholder vote, scoped to a single Zcash\n+  mainnet snapshot and a fresh Election Authority key.\n+\n+Vote round ID\n+: A unique identifier for a voting round. See [Poll Creation] for the\n+  computation.\n+\n+Poll runner\n+: The entity responsible for conducting a voting round.\n+\n+Vote manager\n+: The on-chain role authorized to create voting rounds.\n+\n+Bootstrap operator\n+: The entity that provisions the vote chain genesis and initial\n+  validator set.\n+\n+Validator\n+: A vote chain consensus participant. See [Validator] under Roles for\n+  responsibilities and keypair details.\n+\n+Nullifier service operator\n+: The entity that runs the nullifier exclusion PIR server. See\n+  [Nullifier Service Operator] under Roles for responsibilities.\n+\n+Bonded validator\n+: A validator whose stake is active under the standard Cosmos SDK\n+  `x/staking` module [^cosmos-staking]: its delegation is committed,\n+  it participates in consensus, and it is eligible to produce blocks.\n+\n+Submission server\n+: An untrusted service that accepts encrypted vote share payloads\n+  from voters and submits the corresponding share reveal\n+  transactions to the vote chain. Specified in\n+  `draft-valargroup-submission-server` [^draft-submission-server].\n+\n+Election Authority (EA)\n+: A virtual signing key, jointly constructed by validators during a key\n+  ceremony so that no single party holds the private key. Used to encrypt\n+  vote shares and decrypt the final tally. See\n+  `draft-valargroup-ea-key-ceremony` [^draft-ceremony] for the\n+  ceremony protocol.\n+\n+Snapshot height\n+: The Zcash mainnet block height at which eligible Orchard note balances\n+  are captured. See [Snapshot Configuration] for constraints.\n+\n+For definitions of cryptographic terms including *alternate nullifier*,\n+*nullifier non-membership tree*, *nullifier domain*, *pool snapshot*, and\n+*claim*, see the Orchard Proof-of-Balance ZIP [^draft-balance-proof]. For\n+EA key ceremony terms, see `draft-valargroup-ea-key-ceremony`\n+[^draft-ceremony]. For PIR-related terms, see\n+`draft-valargroup-nullifier-pir` [^draft-pir].\n+\n+\n+# Abstract\n+\n+This ZIP specifies how to operate the infrastructure for Zcash shielded\n+coinholder voting. It defines a purpose-built vote chain built on Cosmos\n+SDK, three operator roles (bootstrap operator, vote manager, validator),\n+and the lifecycle of a voting round from snapshot selection through tally\n+verification.\n+\n+The vote chain stores vote commitments in a Poseidon Merkle tree, tracks\n+three nullifier sets to prevent double-voting, and accumulates encrypted\n+vote shares as homomorphic El Gamal ciphertexts. Each transaction\n+(delegation, vote, share reveal) is verified by a zero-knowledge proof\n+on-chain. Validators join through an automated onboarding script that\n+handles binary distribution, key generation, and on-chain registration.\n+A separate nullifier service provides private information retrieval of\n+exclusion proofs so voters can prove note non-spending without revealing\n+which notes they hold.\n+\n+Tally correctness is independently verifiable: validators submit partial\n+decryptions which are Lagrange-combined on-chain. Any party with access\n+to the chain state can re-derive the combination from the stored\n+partials and confirm the decrypted result.\n+\n+\n+# Motivation\n+\n+The Zcash Shielded Voting Protocol [^draft-voting-protocol] defines a\n+cryptographic protocol for private coinholder voting. This ZIP specifies\n+the deployment infrastructure required to run that protocol: the vote\n+chain, operator roles, and voting round lifecycle.\n+\n+\n+# Privacy Implications\n+\n+- Zero-knowledge proofs and encryption hide the contents of\n+  delegations, votes, and share reveals, but not the network-layer\n+  metadata associated with their submission. A vote chain validator\n+  sees the source IP, submission timestamp, connection correlation,\n+  and P2P propagation pattern of every transaction it receives.\n+- PIR queries against the nullifier service are private in content\n+  but not in timing: a nullifier service operator sees the source IP\n+  and time of each query, which reveals that a given client is\n+  participating in the current voting round.\n+- The vote chain is a public ledger. Transaction contents are\n+  encrypted or zero-knowledge-proven, but their existence, ordering,\n+  and block-inclusion timing are a permanent public record.\n+- Bootstrap operators learn the network identities of validators\n+  during onboarding (see [Onboarding Validators]).\n+- Validator power distribution affects the trust model for the EA\n+  key ceremony. See `draft-valargroup-ea-key-ceremony`\n+  [^draft-ceremony] for EA-specific privacy implications.\n+\n+\n+# Requirements\n+\n+- A new poll runner can set up infrastructure and conduct a voting round\n+  by following this specification and the referenced companion ZIPs.\n+- A Zcash coinholder with eligible Orchard funds at the round's\n+  snapshot height can participate in the voting round using a\n+  conforming wallet client.\n+- The vote chain operates as a public, verifiable ledger — anyone can run\n+  a monitoring node to audit.\n+- The system operates with partial validator availability.\n+\n+\n+# Non-requirements\n+\n+- Governance policy decisions such as proposal eligibility, quorum\n+  requirements, and fund disbursement rules (see ZIP 1016 [^zip-1016]).\n+\n+\n+# Specification\n+\n+## Proposals and Decisions\n+\n+The voting process specified by this ZIP allows a poll runner to put\n+one or more questions — *proposals* — to eligible voters. For each\n+proposal, voters choose exactly one of a predefined set of labeled\n+*options*; the chosen option is the voter's *decision* for that\n+proposal.\n+\n+### Structure of a proposal\n+\n+Each proposal has:\n+\n+- A **title**, short and human-readable.\n+- An optional **description** providing additional context.\n+- Between 2 and 8 **options**, each carrying a human-readable label.\n+  Option labels MUST be non-empty ASCII strings.\n+\n+Proposals in a voting round are assigned 1-indexed sequential\n+identifiers; options within a proposal are assigned 0-indexed\n+sequential indices.\n+\n+### Decisions\n+\n+A **decision** is a voter's chosen option for a specific proposal,\n+represented as the option's 0-indexed position within that proposal's\n+option list. Decisions are recorded in the encrypted share\n+accumulator, keyed by `(proposal_id, vote_decision)`; see\n+`draft-valargroup-voting-protocol` [^draft-voting-protocol] for the\n+cryptographic construction.\n+\n+### Kinds of polls that can be expressed\n+\n+A voting round can carry **1 to 15 independent proposals**, and each\n+proposal can offer **2 to 8 labeled options**. This is sufficient for:\n+\n+- Yes/no questions (\"Approve proposal X?\" with options Yes / No).\n+- Multiple-choice preference questions (for example, choosing among\n+  named candidates or funding tiers).\n+- Rating-style questions using a fixed option ladder.\n+\n+The following ballot shapes are out of scope for this specification:\n+\n+- Free-form write-in answers.\n+- Ranked-choice or weighted-ranking ballots.\n+- More than 15 proposals in a single voting round, or more than 8\n+  options in a single proposal.\n+\n+The 15-proposal upper bound is imposed by the zero-knowledge vote\n+authority bitmask (1 bit is reserved as a sentinel). Polls requiring\n+more proposals or richer ballot structures are split across multiple\n+rounds or expressed through an external layer.\n+\n+## System Overview\n+\n+The coinholder voting system operates on a purpose-built Cosmos SDK vote\n+chain. Zcash mainnet snapshots provide the set of eligible Orchard note\n+balances.\n+\n+The vote chain stores:\n+\n+- A **Vote Commitment Tree** (VCT): a Poseidon Merkle tree of vote\n+  commitments.\n+- Three **nullifier sets**: governance nullifiers (alternate nullifiers\n+  from note claims), VAN nullifiers (from delegation consumption), and\n+  share nullifiers (from share reveals).\n+- An **encrypted share accumulator** per (proposal, decision): the\n+  homomorphic sum of El Gamal ciphertexts for each vote option.\n+\n+The vote chain verifies a zero-knowledge proof for each transaction type:\n+delegation, vote, and share reveal. The proof circuits are specified in\n+`draft-valargroup-voting-protocol` [^draft-voting-protocol].\n+\n+## Deployment Architecture\n+\n+A complete deployment consists of:\n+\n+- **Vote chain nodes** — one or more `svoted` instances running CometBFT\n+  consensus. Each `svoted` binary additionally includes the\n+  **submission server** (an untrusted service that accepts vote\n+  share payloads from clients and submits the corresponding share\n+  reveal transactions at client-specified times). The submission\n+  server shares the node's process but is functionally decoupled\n+  from chain consensus; see `draft-valargroup-submission-server`\n+  [^draft-submission-server].\n+- **Nullifier service** — a PIR server that provides private nullifier\n+  exclusion proofs to voters (see [Nullifier Service]).\n+- **Vote configuration document** — a per-round document published\n+  by the vote manager that lists the network endpoints of the vote\n+  chain nodes and nullifier service operators participating in the\n+  round. The document format and distribution rules are specified\n+  in `draft-valargroup-shielded-voting-wallet-api`\n+  [^draft-wallet-api]; see also [Vote Configuration Publication].\n+\n+## Roles\n+\n+### Bootstrap Operator\n+\n+The bootstrap operator generates the vote chain genesis block and\n+provisions the initial validator set. At genesis, a single vote\n+manager account is created with a balance of the chain's native token\n+(denom `usvote`) sized to fund all planned validators. From that\n+account the bootstrap operator funds each validator via\n+`MsgAuthorizedSend`, a transfer message gated by the vote chain's\n+ante handler: the vote manager MAY send to any address, and bonded\n+validators MAY send to the vote manager or to other bonded\n+validators; all other transfers — including the standard Cosmos bank\n+`MsgSend` and `MsgMultiSend` messages — are rejected.\n+\n+The amount transferred to each validator at bonding time determines\n+their consensus voting power. An even distribution across validators\n+reduces the risk of consensus capture. The bootstrap operator's\n+activities are confined to genesis. The keypair that controls the\n+genesis `vote_manager` address continues afterwards as the initial\n+vote manager (see [Vote Manager]), and MAY transfer that role via\n+`MsgSetVoteManager`.\n+\n+### Vote Manager\n+\n+The vote manager is the only account authorized to initialize the\n+chain's voting round (see [Poll Creation]).\n+\n+The vote manager address is set in the genesis block (see\n+[Genesis Validator Setup]). The current vote manager MAY transfer\n+the role to another address via `MsgSetVoteManager`; the transfer\n+is atomic and moves the full account balance to the new address.\n+No other account can claim or reassign the role.\n+\n+### Validator\n+\n+Validators participate in consensus, the EA key ceremony (see\n+`draft-valargroup-ea-key-ceremony` [^draft-ceremony]), and automatic\n+tally computation. Each validator maintains three keypairs:\n+\n+- **Consensus keypair**: used for CometBFT consensus.\n+- **Account keypair**: used for submitting chain transactions.\n+- **Pallas keypair**: used for ECIES key exchange during the EA ceremony.\n+\n+Validators join the network by following the flow described in\n+[Onboarding Validators].\n+\n+Each validator additionally runs the submission server bundled\n+into the `svoted` binary, which receives encrypted vote share\n+payloads from voters and submits the corresponding share reveal\n+transactions on their behalf. See `draft-valargroup-submission-server`\n+[^draft-submission-server].\n+\n+### Nullifier Service Operator\n+\n+A nullifier service operator runs the nullifier exclusion PIR\n+server that wallet clients query to obtain Merkle non-membership\n+proofs for the Zcash mainnet nullifier set at the snapshot\n+height. The PIR server is a separate binary, distributed\n+independently of the vote chain node, with its own ingest pipeline\n+and HTTP query endpoint. Specified in\n+`draft-valargroup-nullifier-pir` [^draft-pir].\n+\n+## Vote Chain Infrastructure\n+\n+### Genesis Validator Setup\n+\n+The bootstrap operator obtains the vote chain binary `svoted`\n+either from a published release of the reference implementation\n+(see [Reference implementation]) or by building from source, and\n+initializes a new single-validator chain for the voting round.\n+\n+Initialization proceeds as follows:\n+\n+1. **Choose a chain identifier.** Each voting round uses its own\n+   chain identifier so that transactions signed for one round\n+   cannot be misinterpreted by software configured for another\n+   round.\n+\n+2. **Generate the bootstrap operator's keypairs.** Generate a\n+   CometBFT consensus keypair, a Cosmos account keypair, and a\n+   Pallas keypair (see [Validator] for the role of each).\n+\n+3. **Construct the genesis block.** Populate the genesis state\n+   with:\n+   - The chain identifier from step 1.\n+   - The vote manager singleton, set to an address controlled by\n+     the bootstrap operator (see [Bootstrap Operator]).\n+   - An initial balance for the vote manager account, in the\n+     chain's native token (`usvote`), sized to fund the planned\n+     validator set via subsequent authorized transfers.\n+   - A minimum ceremony validator count required before the EA\n+     key ceremony can proceed.\n+   - The bootstrap operator's own validator entry, bonded with\n+     consensus voting power and with its Pallas public key\n+     registered.\n+   - The standard Cosmos SDK module states (auth, bank, staking)\n+     as required by the Cosmos SDK runtime.\n+\n+4. **Start the node.** Starting `svoted` begins block production\n+   and the chain transitions out of the genesis state.\n+\n+The genesis state does not pre-populate voting rounds, nullifier\n+sets, tally results, or PIR data; these are all populated through\n+subsequent on-chain transactions during the voting round.\n+\n+After the chain is producing blocks, the bootstrap operator\n+publishes the initial vote configuration document listing the\n+genesis node's public URL, so that joining validators and wallet\n+clients can find the network. See [Vote Configuration Publication].\n+\n+### Onboarding Validators\n+\n+A new validator joins the vote chain by following these steps:\n+\n+1. **Acquire the vote chain binary.** Obtain `svoted` either from\n+   a published release of the reference implementation (see\n+   [Reference implementation]) or by building from source. Binary\n+   releases MUST be verified against a published checksum before\n+   use.\n+\n+2. **Discover the network and initialize.** Read the vote\n+   configuration document for the target poll (see\n+   [Vote Configuration Publication]) to find at least one active\n+   validator. Fetch the chain's genesis block from that validator\n+   and initialize a local node directory.\n+\n+3. **Generate keypairs.** Generate the validator's consensus\n+   keypair, account keypair, and Pallas keypair (see [Validator]).\n+\n+4. **Sync the chain.** Start the node, connect to the active\n+   validators listed in the vote configuration document via\n+   CometBFT peer-exchange, and sync to the current height.\n+\n+5. **Register in the vote configuration document.** Propose an\n+   update to the document adding the new validator's public URL.\n+\n+6. **Wait for funding.** The vote manager reviews the proposed\n+   update, applies it, and funds the new validator's account via\n+   an authorized transfer (see [Bootstrap Operator] for the\n+   funding mechanism).\n+\n+7. **Register on-chain.** Once funds are received, submit the\n+   validator registration transaction that wraps a standard\n+   Cosmos staking validator creation message together with the\n+   validator's Pallas public key, atomically binding the key to\n+   the new validator.\n+\n+The amount transferred at step 6 determines the new validator's\n+consensus voting power. The vote chain rejects raw Cosmos staking\n+validator creation messages: every validator MUST be registered\n+through the wrapped form so that a Pallas public key is bound at\n+creation time. A validator without a registered Pallas key is\n+bonded for consensus but cannot participate in the EA ceremony\n+(see [Validator]).\n+\n+The reference implementation (see [Reference implementation])\n+includes an automated `join.sh` script that performs the above\n+steps. The script is parameterized by the vote configuration\n+document URL; the same script is used for any poll and is not\n+specialized per poll.\n+\n+### Nullifier Service\n+\n+The nullifier service is an external service that lets voters\n+privately verify that their Orchard nullifiers are absent from\n+the Zcash mainnet nullifier set at the snapshot height, using\n+private information retrieval. A voter's PIR query reveals\n+neither which nullifier is being checked nor the answer to any\n+observer of the network.\n+\n+The service is run by a nullifier service operator (see\n+[Nullifier Service Operator]) using the implementation referenced\n+in [Reference implementation]. The PIR construction and database\n+layout are specified in `draft-valargroup-nullifier-pir`\n+[^draft-pir].\n+\n+The service operates as a three-stage pipeline:\n+\n+1. **Ingest**: fetch the Zcash mainnet nullifier set up to the\n+   chosen snapshot height from a Zcash node and persist it to\n+   local storage.\n+2. **Export**: build the nullifier non-membership tree (an Indexed\n+   Merkle Tree as specified in\n+   `draft-valargroup-orchard-balance-proof` [^draft-balance-proof])\n+   and export the PIR database tiers as specified in\n+   `draft-valargroup-nullifier-pir` [^draft-pir]. The exported\n+   files allow the server to restart without rebuilding the tree\n+   from raw nullifiers.\n+3. **Serve**: accept PIR queries from voters and return encrypted\n+   responses over HTTP. The operator publishes the service URL by\n+   adding it to the vote configuration document (see\n+   [Vote Configuration Publication]).\n+\n+The PIR client-server query and response wire format is not yet\n+specified in any normative document. See [Open Issues].\n+\n+### Vote Configuration Publication\n+\n+The vote manager publishes a vote configuration document for the\n+chain's voting round, in the format and via the publication\n+channel described in `draft-valargroup-shielded-voting-wallet-api`\n+[^draft-wallet-api]. Wallets and joining validators fetch the\n+document from that channel; the vote chain itself does not\n+provide a service-discovery endpoint.\n+\n+The vote manager publishes the initial document once the genesis\n+validator is producing blocks. New validators and nullifier service\n+operators are added by proposing updates to the document, which the\n+vote manager reviews and applies; for validators this proposal is\n+automated by the join.sh flow described in [Onboarding Validators],\n+while nullifier service operators propose their entries directly.\n+The document is incremental — new entries are added without\n+removing earlier ones — and any active entry can serve as an entry\n+point for new joiners; there is no distinguished seed node.\n+\n+Each voting round runs on its own vote chain with its own\n+publication channel. The vote manager announces the channel URL\n+out-of-band before the round opens, so that wallet implementers\n+can configure their wallets to fetch from it — either as a\n+bundled default or as a user-supplied setting. There is no\n+automated cross-poll discovery; a new poll requires the vote\n+manager to communicate the new channel to wallets and end users\n+through whatever distribution path they choose.\n+\n+## Conducting a Voting Round\n+\n+### Snapshot Configuration\n+\n+A voting round is anchored to a Zcash mainnet snapshot: a specific\n+mainnet block height at which the eligible Orchard pool is\n+captured. The poll runner chooses this snapshot height subject\n+to the following constraint:\n+\n+- The height MUST be at or after NU5 activation, since the\n+  protocol requires Orchard.\n+\n+Choosing the snapshot height is the start of round setup, not a\n+single automatic action. The poll runner is responsible for the\n+following coordinated activities:\n+\n+1. **Determine the snapshot's commitment values.** Compute the\n+   Orchard note commitment tree root ($\\mathsf{nc}\\_\\mathsf{root}$)\n+   and the nullifier non-membership Indexed Merkle Tree root\n+   ($\\mathsf{nullifier}\\_\\mathsf{imt}\\_\\mathsf{root}$) at the\n+   chosen height. These values are deterministic given the\n+   height; they are derived from Zcash mainnet state.\n+\n+2. **Ensure the nullifier service has the snapshot's PIR\n+   database.** The poll runner coordinates with each nullifier\n+   service operator (see [Nullifier Service Operator]) so that\n+   their ingest and export pipelines (see [Nullifier Service])\n+   have run to the chosen height before the round opens, so\n+   wallets can query exclusion proofs against that snapshot.\n+\n+3. **Use the values during chain bootstrap.**\n+   $\\mathsf{nc}\\_\\mathsf{root}$ and\n+   $\\mathsf{nullifier}\\_\\mathsf{imt}\\_\\mathsf{root}$ are passed\n+   into the genesis state (see [Genesis Validator Setup]) and\n+   into the voting round initialization transaction (see\n+   [Poll Creation]) so that on-chain verifiers and wallet\n+   clients use them as ZKP public inputs.\n+\n+### Poll Creation\n+\n+The vote manager initializes the chain's voting round by submitting\n+a transaction carrying the client-supplied subset of the `VoteRound`\n+structure specified in `draft-valargroup-shielded-voting-wallet-api`\n+[^draft-wallet-api]. The vote manager supplies `snapshot_height`,\n+`snapshot_blockhash`, `proposals_hash`, `vote_end_time`,\n+`nullifier_imt_root`, `nc_root`, `proposals`, `title`, and\n+`description`; the transaction's signer becomes the `creator` field\n+of the resulting `VoteRound`. The chain derives the remaining\n+fields (`vote_round_id`, `status`, `ea_pk`, `created_at_height`)\n+at inclusion or during the round lifecycle.\n+\n+The chain rejects the transaction if the signer is not the current\n+vote manager, or if the `proposals` field violates the constraints\n+in [Proposals and Decisions].\n+\n+The vote chain derives the 32-byte `vote_round_id` from the\n+transaction fields after inclusion, using the Poseidon construction\n+specified in the \"Voting Round Identifier\" section of\n+`draft-valargroup-shielded-voting`\n+[^draft-voting-protocol-vri]. The result is a Pallas field element\n+so that the round ID can enter ZKP circuits as a public input.\n+\n+The round enters the **PENDING** state. The EA key ceremony (see\n+`draft-valargroup-ea-key-ceremony` [^draft-ceremony]) runs\n+automatically. On successful completion, the\n+round transitions to **ACTIVE**, the voting window opens, and the\n+transition timestamp is recorded as `ceremony_phase_start`. Clients use\n+`ceremony_phase_start` together with `vote_end_time` to compute the\n+last-moment buffer for submission timing, as specified in the\n+\"Last-Moment Buffer\" section of `draft-valargroup-submission-server`\n+[^draft-submission-server-lmb].\n+\n+### Round Lifecycle\n+\n+1. **PENDING**: round created, awaiting EA key ceremony.\n+2. **ACTIVE**: ceremony complete, voting window open. Voters may delegate,\n+   vote, and submit shares (see `draft-valargroup-voting-protocol`\n+   [^draft-voting-protocol]).\n+3. **TALLYING**: `vote_end_time` has passed. Validators submit\n+   partial decryptions, the chain combines them, and tally\n+   decryption runs automatically (see\n+   `draft-valargroup-ea-key-ceremony` [^draft-ceremony]). The\n+   chain enforces a bounded timeout on the TALLYING state: if a\n+   tally is not submitted within this timeout, the round\n+   auto-finalizes with no tally, preserving liveness.\n+4. **FINALIZED**: tally published and verifiable. A round that\n+   auto-finalized due to a TALLYING timeout publishes no tally.\n+\n+## Coinholder Participation\n+\n+For a Zcash coinholder to participate in a voting round, they\n+must satisfy an eligibility precondition and follow a wallet-\n+driven participation flow. The protocol details for each step\n+are specified in companion ZIPs; this section ties them together\n+from the coinholder's perspective.\n+\n+### Eligibility\n+\n+Voting weight derives from the value held in a coinholder's\n+Orchard notes at the round's snapshot height (see\n+[Snapshot Configuration]). Funds held in Sapling or transparent\n+pools at the snapshot height carry no voting weight; coinholders\n+who wish to participate with such funds MUST migrate them to\n+Orchard before the snapshot height.\n+\n+### Wallet Setup\n+\n+A wallet client obtains the vote configuration document for the\n+round (see [Vote Configuration Publication]), which lists the\n+vote chain endpoints, the nullifier service endpoints, and the\n+protocol versions in use. The configuration document schema and\n+the wallet-side validation rules are specified in\n+`draft-valargroup-shielded-voting-wallet-api`\n+[^draft-wallet-api].\n+\n+### Participation Flow\n+\n+For each Orchard note the coinholder uses as voting weight, the\n+wallet performs:\n+\n+1. **Retrieve a non-membership proof.** Query a nullifier service\n+   endpoint to retrieve a Merkle non-membership proof for the\n+   note's alternate nullifier against the snapshot's\n+   `nullifier_imt_root`, as specified in\n+   `draft-valargroup-nullifier-pir` [^draft-pir].\n+\n+2. **Submit a delegation transaction.** Construct a Delegation\n+   Proof asserting ownership of the eligible note without\n+   revealing which note it is, and submit the transaction to a\n+   vote chain endpoint listed in the vote configuration document.\n+   This produces a Vote Authority Note (VAN) on the vote\n+   commitment tree. The proof construction and the on-chain\n+   handling are specified in the Delegation Phase of\n+   `draft-valargroup-shielded-voting` [^draft-voting-protocol].\n+\n+For each proposal the coinholder votes on, the wallet performs:\n+\n+3. **Submit a vote transaction.** Construct a Vote Proof\n+   consuming the current VAN and producing a new VAN with the\n+   relevant proposal authority bit cleared, plus a Vote\n+   Commitment binding the chosen option, as specified in the\n+   Vote Phase of `draft-valargroup-shielded-voting`\n+   [^draft-voting-protocol].\n+\n+4. **Submit encrypted vote shares.** Send the share payloads to\n+   submission server endpoints, which queue them and submit\n+   share reveal transactions on the coinholder's behalf at\n+   client-specified times. Share decomposition, server selection,\n+   and the last-moment buffer rules are specified in\n+   `draft-valargroup-submission-server`\n+   [^draft-submission-server].\n+\n+After `vote_end_time`, the coinholder may verify the final tally\n+following [Verification and Auditing].\n+\n+## Verification and Auditing\n+\n+The vote chain is publicly readable. Any party running a full\n+node of the chain — a validator, the vote manager, or an\n+independent observer — can verify all aspects of a voting round\n+by replaying chain state and applying the verification\n+procedures defined in companion ZIPs.\n+\n+This section uses the following procedures imported by\n+reference:\n+\n+- **Out-of-Circuit Verification of the Claim proof** in\n+  `draft-valargroup-orchard-balance-proof`\n+  [^draft-balance-proof] — the abstract balance-proof primitive\n+  on which the Delegation Proof is built.\n+- **Out-of-Circuit Verification of the Delegation Proof, the\n+  Vote Proof, and the Vote Reveal Proof** in\n+  `draft-valargroup-shielded-voting` [^draft-voting-protocol].\n+- **Authentication Path** verification and the exclusion-range\n+  check for the nullifier non-membership tree, in\n+  `draft-valargroup-nullifier-pir` [^draft-pir].\n+- **Proof Verification** (DLEQ verification of partial\n+  decryptions) and the **Tally** procedure (Lagrange combination\n+  and plaintext recovery), in `draft-valargroup-ea-key-ceremony`\n+  [^draft-ceremony].\n+\n+A full-node operator combines these procedures to verify a\n+voting round across three layers:\n+\n+- **Per-transaction zero-knowledge proof verification.** Each\n+  delegation, vote, and share reveal transaction carries a Halo\n+  2 proof that is checked at inclusion against the\n+  Out-of-Circuit Verification rules in\n+  `draft-valargroup-shielded-voting`. Those rules apply the\n+  Claim proof verification from the balance proof ZIP and the\n+  Authentication Path / exclusion-range checks from the PIR\n+  ZIP transitively, so a single proof verification enforces the\n+  structural correctness of all three layers at once.\n+\n+- **Nullifier set integrity.** The chain maintains three\n+  nullifier sets — governance nullifiers (from delegation), VAN\n+  nullifiers (from voting), and share nullifiers (from share\n+  reveals) — and rejects any transaction that would re-use a\n+  previously published nullifier. A full-node operator confirms\n+  set integrity by replaying every accepted transaction and\n+  checking that no nullifier appears twice.\n+\n+- **Tally correctness.** A full-node operator re-aggregates the\n+  encrypted share ciphertexts per (`proposal_id`, `vote_decision`)\n+  from the on-chain share reveal transactions, applies the DLEQ\n+  Proof Verification to each stored partial decryption,\n+  re-derives the Lagrange combination, and confirms the\n+  decrypted aggregate following the Tally procedure in\n+  `draft-valargroup-ea-key-ceremony`.\n+\n+Because every input to verification — proofs, nullifiers,\n+ciphertexts, and partial decryptions — is stored on the public\n+vote chain, no full-node operator needs to trust any other\n+participant. A round that auto-finalized due to a TALLYING\n+timeout (see [Round Lifecycle]) verifies as having no tally;\n+this is itself a verifiable property of the chain state.\n+\n+\n+# Rationale\n+\n+**Separate vote chain (not Zcash mainnet)**: the vote chain is purpose-built\n+for governance with ZKP-optimized state transitions (Poseidon hashing, custom\n+transaction types). Zcash mainnet's transaction throughput and scripting model\n+are not designed for interactive multi-phase voting protocols.\n+\n+**Orchard-only snapshots**: the voting protocol is built on Orchard's\n+circuit-friendly primitives (Poseidon hashing, Pallas curve). Sapling\n+and transparent pools use incompatible cryptographic constructions.\n+The corresponding requirement on coinholders is stated in\n+[Eligibility].\n+\n+**Cosmos SDK**: provides a mature BFT consensus engine (CometBFT),\n+validator lifecycle management (bonding, jailing for missed blocks or\n+missed ceremony acknowledgements, consensus power distribution), and a\n+transaction pipeline that can be extended with custom message types and\n+ante handlers for ZKP verification. The alternative — building a chain\n+from scratch — would duplicate well-tested consensus infrastructure.\n+\n+**Funding equals voting power**: bonding serves three purposes: it\n+determines which validators participate in consensus and the EA key\n+ceremony (and thus can decrypt the tally), it enables jailing of\n+inactive validators who miss blocks or ceremony acknowledgements, and\n+an even funding split gives each validator a roughly equal probability\n+of becoming the block proposer — not important for correctness, but\n+important for liveness.\n+\n+**Automated validator onboarding**: `join.sh` eliminates manual\n+coordination between the bootstrap operator and joining validators. The\n+self-registration, admin-approval, and auto-bonding flow allows the\n+network to grow without requiring validators to build from source or\n+understand Cosmos SDK tooling.\n+\n+**Vote manager reassignment**: only the current vote manager can\n+transfer the role (via `MsgSetVoteManager`). This is a deliberate\n+single-party control: the vote manager can create rounds but cannot\n+forge votes, and the worst-case mitigation for a compromised vote\n+manager is to spin up a new chain.\n+\n+\n+# Reference implementation\n+\n+- [^ref-vote-sdk] — Cosmos SDK vote chain (`svoted`) implementing\n+  the chain-side state, ceremony, tally, and submission server.\n+- [^ref-nullifier-pir] — PIR server and client for privately\n+  retrieving nullifier non-membership proofs.\n+\n+\n+# Open Issues\n+\n+- **Role consolidation**: evaluate whether the bootstrap operator and\n+  vote manager concepts (already the same keypair at genesis; see\n+  [Bootstrap Operator]) merit separate treatment in the specification.\n+- **PIR client-server wire format**: the query and response wire\n+  format used between wallet clients and the nullifier service\n+  (see [Nullifier Service]) is not currently specified in any\n+  ZIP. The PIR draft scopes its outer transport out of remit, and\n+  the wallet API ZIP describes only the endpoint URLs. A normative\n+  spec home is needed before wallet clients and nullifier service\n+  servers from independent implementations can interoperate.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](zip-1016.md)\n+\n+[^draft-balance-proof]: [Draft ZIP: Orchard Proof-of-Balance](draft-valargroup-orchard-balance-proof.md)\n+\n+[^draft-voting-protocol]: [Draft ZIP: Zcash Shielded Voting Protocol](draft-valargroup-shielded-voting.md)\n+\n+[^draft-voting-protocol-vri]: [Draft ZIP: Zcash Shielded Voting Protocol, Section: Voting Round Identifier](draft-valargroup-shielded-voting.md#voting-round-identifier)\n+\n+[^draft-ceremony]: [Draft ZIP: Election Authority Key Ceremony](draft-valargroup-ea-key-ceremony.md)\n+\n+[^draft-pir]: [Draft ZIP: Private Information Retrieval for Nullifier Exclusion Proofs](draft-valargroup-nullifier-pir.md)\n+\n+[^draft-submission-server]: [Draft ZIP: Vote Share Submission Server](draft-valargroup-submission-server.md)\n+\n+[^draft-wallet-api]: [Draft ZIP: Shielded Voting Wallet API](draft-valargroup-shielded-voting-wallet-api.md)\n+\n+[^draft-submission-server-lmb]: [Draft ZIP: Vote Share Submission Server, Section: Last-Moment Buffer](draft-valargroup-submission-server.md#last-moment-buffer)\n+\n+[^draft-onchain-voting]: [Draft ZIP: On-chain Accountable Voting](draft-ecc-onchain-accountable-voting.md)\n+\n+[^cosmos-staking]: [Cosmos SDK `x/staking` module documentation](https://docs.cosmos.network/main/build/modules/staking)\n+\n+[^ref-vote-sdk]: [valargroup/vote-sdk: Cosmos SDK vote chain for shielded voting](https://github.com/valargroup/vote-sdk)\n+\n+[^ref-nullifier-pir]: [valargroup/vote-nullifier-pir: PIR system for nullifier non-membership proofs](https://github.com/valargroup/vote-nullifier-pir)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-valargroup-shielded-voting-setup.md","title":"Zcash Shielded Coinholder Voting","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"proposals-and-decisions","text":"Proposals and Decisions"},{"level":3,"id":"structure-of-a-proposal","text":"Structure of a proposal"},{"level":3,"id":"decisions","text":"Decisions"},{"level":3,"id":"kinds-of-polls-that-can-be-expressed","text":"Kinds of polls that can be expressed"},{"level":2,"id":"system-overview","text":"System Overview"},{"level":2,"id":"deployment-architecture","text":"Deployment Architecture"},{"level":2,"id":"roles","text":"Roles"},{"level":3,"id":"bootstrap-operator","text":"Bootstrap Operator"},{"level":3,"id":"vote-manager","text":"Vote Manager"},{"level":3,"id":"validator","text":"Validator"},{"level":3,"id":"nullifier-service-operator","text":"Nullifier Service Operator"},{"level":2,"id":"vote-chain-infrastructure","text":"Vote Chain Infrastructure"},{"level":3,"id":"genesis-validator-setup","text":"Genesis Validator Setup"},{"level":3,"id":"onboarding-validators","text":"Onboarding Validators"},{"level":3,"id":"nullifier-service","text":"Nullifier Service"},{"level":3,"id":"vote-configuration-publication","text":"Vote Configuration Publication"},{"level":2,"id":"conducting-a-voting-round","text":"Conducting a Voting Round"},{"level":3,"id":"snapshot-configuration","text":"Snapshot Configuration"},{"level":3,"id":"poll-creation","text":"Poll Creation"},{"level":3,"id":"round-lifecycle","text":"Round Lifecycle"},{"level":2,"id":"coinholder-participation","text":"Coinholder Participation"},{"level":3,"id":"eligibility","text":"Eligibility"},{"level":3,"id":"wallet-setup","text":"Wallet Setup"},{"level":3,"id":"participation-flow","text":"Participation Flow"},{"level":2,"id":"verification-and-auditing","text":"Verification and Auditing"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"open-issues","text":"Open Issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1202,"title":"[ZIP 307] Update out-of-date light client protocol definition.","author":"nuttycom","createdAt":"2026-03-05T22:54:36Z","updatedAt":"2026-04-07T19:25:18Z","url":"https://github.com/zcash/zips/pull/1202","diffUrl":"https://github.com/zcash/zips/pull/1202.diff","headRef":"zip-307-update","headRepo":"nuttycom/zips","headSha":"75f1a84fc480887961659a246ecf2176f1da6923","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Once this cleanup is complete, we should migrate ZIP 307 to Active status, as it has been deployed for quite some time.","zipRefs":[307],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0307.rst","status":"modified","additions":292,"deletions":449,"patch":"@@ -15,43 +15,54 @@\n Terminology\n ===========\n \n-The key words \"MUST\", \"SHOULD\", and \"MAY\" in this document are to be interpreted as\n-described in BCP 14 [#BCP14]_ when, and only when, they appear in all capitals.\n+The key words \"MUST\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this document are to be\n+interpreted as described in BCP 14 [#BCP14]_ when, and only when, they appear in all\n+capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol Specification.\n+[^protocol]\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in § 3.12\n+‘Mainnet and Testnet’. [^protocol-networks]\n \n The terms below are to be interpreted as follows:\n \n Light client\n-  A client that is not a full participant in the network of Zcash peers. It can send and\n-  receive payments, but does not store or validate a copy of the block chain.\n+  A client that is not a full participant in the network of Zcash peers. It can receive\n+  and (if it holds the required spending keys) send payments, but does not store or\n+  validate a full copy of the block chain.\n \n Abstract\n ========\n \n-This proposal defines a protocol for a Zcash light client supporting Sapling shielded\n-transactions.\n+This ZIP defines a protocol for Zcash light clients that enables detection of incoming\n+payments, identification of spent notes, and maintenance of note commitment tree witnesses\n+and nullifier sets for Sapling [#zip-0205]_ and Orchard [#zip-0224]_ shielded transactions,\n+as well as transparent input and output tracing.\n \n Motivation\n ==========\n \n-Currently a client that wishes to send or receive shielded payments must be a full node\n-participating in the Zcash network. This requires an amount of available bandwidth,\n-space, and processing power that may be unsuitable for some classes of user. This light\n-client protocol addresses that need, and is appropriate for low-power,\n-bandwidth-conscious, or otherwise limited machines (such as mobile phones).\n+Prior to the introduction of this protocol, a client that wished to send or\n+receive shielded payments had to be a full node participating in the Zcash\n+network. This required an amount of available bandwidth, space, and processing\n+power that was unsuitable for some use cases. This light client protocol\n+addresses that need, and is appropriate for low-power, bandwidth-conscious, or\n+otherwise limited machines (such as mobile phones).\n \n High-Level Design\n =================\n \n There are three logical components to a Zcash light client system:\n \n-- **Zcash node** that provides chain state and serves as a root of trust for the system.\n+- **Zcash full node** that provides chain state and serves as a root of trust for the system.\n \n-- **Proxy server** that extracts block chain data from zcashd to store and serve it in a\n-  lower-bandwidth format.\n+- **Proxy server** that extracts block chain data from a Zcash full node to store and serve it\n+  in a lower-bandwidth format.\n \n - **Light client** that subscribes to the stream from a proxy server and uses that data to\n   update its own view of the chain state. The light client MAY be attached to a wallet\n-  backend that will track particular Sapling notes.\n+  backend that will track particular shielded notes.\n \n .. figure:: ../rendered/assets/images/zip-0307-arch.png\n     :align: center\n@@ -66,7 +77,7 @@ In this model, we propose **payment detection privacy** as our main security goa\n is, the proxy should not learn which transactions (received from the block chain) are\n addressed to a given light wallet. If we further assume network privacy (via Tor or\n similar), the proxy should not be able to link different connections or queries as\n-deriving from the the same wallet.\n+deriving from the same wallet.\n \n In particular, the underlying Zcash node / proxy combination is assumed to be \"honest but\n curious\" and is trusted to provide a correct view of the current best chain state and to\n@@ -77,122 +88,92 @@ This ZIP does not address how to spend notes privately.\n Compact Stream Format\n =====================\n \n-A key observation in this protocol is that the current zcashd encrypted field is several\n-hundred bytes long, due to the inclusion of a transaction “memo”. The need to download\n-this entire field imposes a substantial bandwidth cost on each light wallet, which may be\n-a limited mobile device on a restricted-bandwidth plan. While more efficient techniques\n-can be developed in the future, for the moment we propose ignoring the memo field during\n-payment detection. Futhermore, we can also ignore any information that is not directly\n-relevant to a Sapling shielded transaction.\n+A key observation in this protocol is that the ciphertext in each shielded output\n+is several hundred bytes long, due to the inclusion of a transaction memo. The need to\n+download the entire ciphertext imposes a substantial bandwidth cost on each light wallet,\n+which may be a limited mobile device on a restricted-bandwidth plan. The memo field can be\n+ignored during payment detection, as can any information that is not directly relevant to\n+shielded transaction processing.\n \n A **compact block** is a packaging of ONLY the data from a block needed to:\n \n-1. Detect a payment to your shielded Sapling address\n-2. Detect a spend of your shielded Sapling notes\n-3. Update your witnesses to generate new Sapling spend proofs.\n-\n-A compact block and its component compact transactions are encoded on the wire using the\n-following Protocol Buffers [#protocolbuffers]_ format:\n+1. Detect a payment to your shielded address (Sapling or Orchard).\n+2. Detect a spend of your shielded notes.\n+3. Update note commitment tree witnesses.\n \n-.. code:: proto\n+Compact blocks and their component compact transactions are encoded on the wire using\n+Protocol Buffers [#protocolbuffers]_. The normative definitions of the compact block format\n+are specified in the ``compact_formats.proto`` file of the Zcash Light Client Protocol\n+[#lightwallet-protocol]_.\n \n-    message BlockID {\n-         uint64 blockHeight = 1;\n-         bytes blockHash = 2;\n-    }\n+The protocol has evolved through several major versions:\n \n-    message CompactBlock {\n-        BlockID id = 1;\n-        repeated CompactTx vtx = 3;\n-    }\n+- **v0.3.x** (2021–2023): Added Orchard shielded protocol support via\n+  ``CompactOrchardAction`` and note commitment tree state queries\n+  (``GetTreeState``, ``GetLatestTreeState``) and subtree root retrieval\n+  (``GetSubtreeRoots``) to support efficient witness construction.\n \n-    message CompactTx {\n-        uint64 txIndex = 1;\n-        bytes txHash = 2;\n+- **v0.4.x** (2025): Added transparent transaction data (``CompactTxIn``, ``TxOut``) to\n+  compact blocks, enabling transparent outputs to be discovered without revealing\n+  the client's addresses to the light wallet server. The update also added\n+  ``PoolType``-based filtering for selective retrieval by shielded protocol.\n \n-        repeated CompactSpend spends = 3;\n-        repeated CompactOutput outputs = 4;\n-    }\n-\n-    message CompactSpend {\n-        bytes nf = 1;\n-    }\n-\n-    message CompactOutput {\n-        bytes cmu = 1;\n-        bytes epk = 2;\n-        bytes ciphertext = 3;\n-    }\n-\n-Encoding Details\n-----------------\n-\n-``blockHash``, ``txHash``, ``nf``, ``cmu``, and ``epk`` are encoded as\n-specified in the Zcash Protocol Spec.\n-\n-The output and spend descriptions are handled differently, as described in the following\n-sections.\n+The following sections describe the rationale for the compact format design.\n \n Output Compression\n ------------------\n \n-In the normal Zcash protocol, the output ciphertext consists of the AEAD-encrypted form of\n-a *note plaintext* [#protocol-notept]_:\n-\n-+------------+----------+----------+---------------+-----------------------------------+\n-| 8-bit 0x01 | 88-bit d | 64-bit v | 256-bit rseed | memo (512 bytes) + tag (16 bytes) |\n-+------------+----------+----------+---------------+-----------------------------------+\n-\n-A recipient detects their transactions by trial-decrypting this ciphertext. On a full node\n-that has the entire block chain, the primary cost is computational. For light clients\n-however, there is an additional bandwidth cost: every ciphertext on the block chain must\n-be received from the server (or network node) the light client is connected to. This\n-results in a total of 580 bytes per output that must be streamed to the client (in addition\n-to the 32-byte ephemeral public key).\n-\n-However, we don't need all of that just to detect payments. The first 52 bytes of the\n-ciphertext contain the contents and opening of the note commitment, which is all of the\n-data needed to spend the note and to verify that the note is spendable. If we ignore the\n-memo and the authentication tag, we're left with a 32-byte ephemeral public key, the 32-byte\n-note commitment, and only the first 52 bytes of the ciphertext for each output needed to\n-decrypt, verify, and spend a note. This totals to 116 bytes per output, for an 80%\n-reduction in bandwidth use.\n-\n-However, skipping the full ciphertext means that we can no longer calculate the\n-authentication tag for the entire ciphertext and will need to do something else to\n-validate the integrity of the decrypted note plaintext.\n-\n-Since the note commitment is sent outside the ciphertext and is authenticated by the\n-binding signature over the entire transaction, it serves as an adequate check on the\n-validity of the decrypted plaintext (assuming you trust the entity assembling\n-transactions). We therefore recalculate the note commitment from the decrypted plaintext.\n-If the recalculated commitment matches the one in the output, we accept the note as valid\n-and spendable.\n+In the Zcash protocol, each shielded output contains an AEAD-encrypted *note\n+plaintext* [#protocol-notept]_. For both Sapling and Orchard, this ciphertext\n+(``encCiphertext``) is 580 bytes long and includes the memo field and an\n+authentication tag.\n+\n+A recipient detects their transactions by trial-decrypting this ciphertext. On\n+a full node that has the entire block chain, the primary cost is computational.\n+For light clients however, there is an additional bandwidth cost: every\n+ciphertext on the block chain must be received from the server (or network\n+node) the light client is connected to.\n+\n+However, payment detection does not require the full ciphertext. The first 52\n+bytes of ``encCiphertext`` contain the contents and opening of the note\n+commitment, which is sufficient to detect, verify, and spend the note. The\n+compact representation therefore includes only these 52 bytes along with the\n+extracted note commitment and ephemeral public key.\n+\n+For **Sapling**, a ``CompactSaplingOutput`` contains the 32-byte extracted note\n+commitment ($\\mathtt{cmu}$), the 32-byte ephemeral public key, and the first 52\n+bytes of the ciphertext, for a total of 116 bytes per output. A full Sapling\n+Output description [#protocol-outputencodingandconsensus]_ is 948 bytes in v4\n+transactions (756 bytes in v5), so the compact representation provides an 85–88%\n+reduction in bandwidth.\n+\n+For **Orchard**, a ``CompactOrchardAction`` contains the 32-byte nullifier, the\n+32-byte extracted note commitment ($\\mathtt{cmx}$), the 32-byte ephemeral public\n+key, and the first 52 bytes of the ciphertext, for a total of 148 bytes per\n+action. A full Orchard Action description [#protocol-actionencodingandconsensus]_\n+is 820 bytes, so the compact representation provides an 82% reduction in\n+bandwidth. Note that the Orchard compact representation includes the nullifier\n+because the Orchard Action structure combines a spend and an output into a\n+single description.\n+\n+Since the compact representation omits the authentication tag, the integrity of\n+the decrypted note plaintext is instead validated by recalculating the note\n+commitment from the decrypted plaintext and checking it against the note\n+commitment sent outside the ciphertext, which is authenticated by the binding\n+signature over the entire transaction.\n \n Spend Compression\n -----------------\n \n-Recall that a full Sapling Spend description is 384 bytes long [#protocol-spendencodingandconsensus]_:\n-\n-+-------+--------------+-----------+\n-| Bytes | Name         | Type      |\n-+=======+==============+===========+\n-| 32    | cv           | char[32]  |\n-+-------+--------------+-----------+\n-| 32    | anchor       | char[32]  |\n-+-------+--------------+-----------+\n-| 32    | nullifier    | char[32]  |\n-+-------+--------------+-----------+\n-| 32    | rk           | char[32]  |\n-+-------+--------------+-----------+\n-| 192   | zkproof      | char[192] |\n-+-------+--------------+-----------+\n-| 64    | spendAuthSig | char[64]  |\n-+-------+--------------+-----------+\n-\n-The only part necessary for detection is the nullifier, which allows a light client to\n-detect when one of its own notes has been spent. This means we only need to take 32 bytes\n-of each Spend, for a 90% improvement in bandwidth use.\n+For **Sapling**, a full Spend description is 384 bytes long\n+[#protocol-spendencodingandconsensus]_. The only part necessary for spend\n+detection is the 32-byte nullifier, which allows a light client to detect when\n+one of its own notes has been spent. A ``CompactSaplingSpend`` therefore\n+contains only the nullifier, for a 92% reduction in bandwidth.\n+\n+For **Orchard**, spend detection is handled by checking the nullifier included\n+in each ``CompactOrchardAction`` (as described in `Output Compression`_ above),\n+since the Orchard Action structure combines a spend and an output.\n \n Proxy operation\n ===============\n@@ -202,46 +183,10 @@ Zcash node and any number of light clients. It accomplishes this by parsing a bl\n stream of transactions from the node and converting them into the compact format described\n above.\n \n-*The details of the API described below may differ from the implementation.*\n-\n-The proxy offers the following API to clients:\n-\n-.. code:: proto\n-\n-    service CompactTxStreamer {\n-        rpc GetLatestBlock(ChainSpec) returns (BlockID) {}\n-        rpc GetBlock(BlockID) returns (CompactBlock) {}\n-        rpc GetBlockRange(RangeFilter) returns (stream CompactBlock) {}\n-        rpc GetTransaction(TxFilter) returns (FullTransaction) {}\n-    }\n-\n-    // Remember that proto3 fields are all optional.\n-\n-    // Someday we may want to specify e.g. a particular chain fork.\n-    message ChainSpec {}\n-\n-\n-    // A BlockID message contains identifiers to select a block: either a\n-    // height or a hash.\n-    message BlockID {\n-        uint64 blockHeight = 1;\n-        bytes blockHash = 2;\n-    }\n-\n-\n-    message RangeFilter {\n-        BlockID start = 1;\n-        BlockID end = 2;\n-    }\n-\n-    // A TxFilter contains the information needed to identify a particular\n-    // transaction: either a block and an index, or a direct transaction hash.\n-    message TxFilter {\n-        BlockID blockID = 1;\n-        uint64 txIndex = 2;\n-        bytes txHash = 3;\n-    }\n-\n+The proxy offers a gRPC API to clients, defined by the ``service.proto`` file of the\n+Zcash Light Client Protocol [#lightwallet-protocol]_. The ``CompactTxStreamer`` service\n+provides methods for retrieving compact blocks, submitting transactions, querying note\n+commitment tree state, and querying for transparent outputs by address.\n \n Client operation\n ================\n@@ -253,77 +198,95 @@ server here without loss of generality.\n Local processing\n ----------------\n \n-Given a ``CompactBlock`` at block height $\\mathsf{height}$ received in height-sequential\n-order from a proxy server, a light client can process it in four ways:\n+Given a ``CompactBlock`` at block height $\\mathsf{height}$ received from a proxy server, a\n+light client processes it in the following ways:\n \n Scanning for relevant transactions\n ``````````````````````````````````\n-For every ``CompactOutput`` in the ``CompactBlock``, the light client can trial-decrypt it\n-against a set of Sapling incoming viewing keys. The procedure for trial-decrypting a\n-``CompactOutput`` $(\\mathtt{cmu}, \\mathtt{ephemeralKey}, \\mathsf{ciphertext})$ with an incoming\n-viewing key $\\mathsf{ivk}$ is a slight deviation from the standard decryption process\n-[#protocol-saplingdecryptivk]_ (all constants and algorithms are as defined there):\n-\n-- let $\\mathsf{epk} = \\mathsf{abst}_{\\mathbb{J}}(\\mathtt{ephemeralKey})$\n-- if $\\mathsf{epk} = \\bot$, return $\\bot$\n-- let $\\mathsf{sharedSecret} = \\mathsf{KA^{Sapling}.Agree}(\\mathsf{ivk}, \\mathsf{epk})$\n-- let $K^{\\mathsf{enc}} = \\mathsf{KDF^{Sapling}}(\\mathsf{sharedSecret}, \\mathtt{ephemeralKey})$\n-- let $P^{\\mathsf{enc}} = \\mathsf{ChaCha20.Decrypt}_{K^{\\mathsf{enc}}}(\\mathsf{ciphertext})$\n-- extract $\\mathbf{np} = (\\mathsf{leadByte}, \\mathsf{d}, \\mathsf{v}, \\mathsf{rseed})$ from $P^{\\mathsf{enc}}$\n-- [Pre-Canopy] if $\\mathsf{leadByte} \\neq 0x01$, return $\\bot$\n-- [Pre-Canopy] let $\\mathsf{\\underline{rcm}} = \\mathsf{rseed}$\n-- [Canopy onward] if $\\mathsf{height} < \\mathsf{CanopyActivationHeight} + \\mathsf{ZIP212GracePeriod}$ and $\\mathsf{leadByte} \\not\\in \\{ \\mathtt{0x01}, \\mathtt{0x02} \\}$, return $\\bot$\n-- [Canopy onward] if $\\mathsf{height} < \\mathsf{CanopyActivationHeight} + \\mathsf{ZIP212GracePeriod}$ and $\\mathsf{leadByte} \\neq \\mathtt{0x02}$, return $\\bot$\n-- [Canopy onward] let $\\mathsf{\\underline{rcm}} = \\begin{cases}\\mathsf{rseed}, &\\text{if } \\mathsf{leadByte} = \\mathtt{0x01} \\\\ \\mathsf{ToScalar}(\\mathsf{PRF^{expand}_{rseed}}([5])), &\\text{otherwise}\\end{cases}$\n-- let $\\mathsf{rcm} = \\mathsf{LEOS2IP}_{256}(\\mathsf{\\underline{rcm}})$ and $\\mathsf{g_d} = \\mathsf{DiversifyHash}(\\mathsf{d})$\n-- if $\\mathsf{rcm} \\geq r_{\\mathbb{J}}$ or $\\mathsf{g_d} = \\bot$, return $\\bot$\n-- [Canopy onward] if $\\mathsf{leadByte} \\neq \\mathtt{0x01}$:\n-\n-  * $\\mathsf{esk} = \\mathsf{ToScalar}(\\mathsf{PRF^{expand}_{rseed}}([4]))$\n-  * if $\\mathsf{repr}_{\\mathbb{J}}(\\mathsf{KA^{Sapling}.DerivePublic}(\\mathsf{esk}, \\mathsf{g_d})) \\neq \\mathtt{ephemeralKey}$, return $\\bot$\n-\n-- let $\\mathsf{pk_d} = \\mathsf{KA^{Sapling}.DerivePublic}(\\mathsf{ivk}, \\mathsf{g_d})$\n-- let $\\mathsf{cm}_u' = \\mathsf{Extract}_{\\mathbb{J}^{(r)}}(\\mathsf{NoteCommit^{Sapling}_{rcm}}(\\mathsf{repr}_{\\mathbb{J}}(\\mathsf{g_d}), \\mathsf{repr}_{\\mathbb{J}}(\\mathsf{pk_d}), \\mathsf{v}))$.\n-- if $\\mathsf{LEBS2OSP}_{256}(\\mathsf{cm}_u') \\neq \\mathtt{cmu}$, return $\\bot$\n-- return $\\mathbf{np}$.\n-\n-Creating and updating note witnesses\n-````````````````````````````````````\n-As ``CompactBlocks`` are received in height order, and the transactions within them have\n-their order preserved, the *cmu* values in each ``CompactOutput`` can be sequentially\n-appended to an incremental Merkle tree of depth 32 in order to maintain a local copy of\n-the Sapling note commitment tree. [#protocol-merkletree]_ This can then be used to\n-create incremental witnesses for each unspent note the light client is tracking.\n-[#incremental-witness]_ An incremental witness updated to height ``X`` corresponds to a\n-Merkle path from the note to the Sapling commitment tree anchor for block ``X``.\n-[#protocol-merklepath]_\n-\n-Let ``tree`` be the Sapling note commitment tree at height ``X-1``, and ``note_witnesses``\n-be the incremental witnesses for unspent notes detected up to height ``X-1``. When the\n-``CompactBlock`` at height ``X`` is received:\n-\n-- For each ``CompactTx`` in ``CompactBlock``:\n-\n-  - For each ``CompactOutput`` (*cmu*, *epk*, *ciphertext*) in ``CompactBlock``:\n-\n-    - Append ``cmu`` to ``tree``.\n-    - For ``witness`` in ``note_witnesses``:\n-\n-      - Append ``cmu`` to ``witness``.\n-\n-    - If ``ciphertext`` contains a relevant note, create an incremental witness from\n-      ``tree`` and append it to ``note_witnesses``.\n-\n-Incremental Merkle trees cannot be rewound, so the light client should cache both the\n-Sapling note commitment tree and per-note incremental witnesses for recent block heights.\n-Cache management is implementation-dependent, but a cache size of 100 is reasonable, as no\n-full Zcash node will roll back the chain by more than 100 blocks.\n+\n+For every ``CompactSaplingOutput`` and ``CompactOrchardAction`` in the ``CompactBlock``,\n+the light client attempts trial decryption against its set of incoming viewing keys. The\n+following subsection describes the compact trial decryption procedure for Sapling and\n+Orchard.\n+\n+Sapling and Orchard compact trial decryption\n+''''''''''''''''''''''''''''''''''''''''''''\n+\n+For each Sapling or Orchard compact output, the light client trial-decrypts with each\n+incoming viewing key $\\mathsf{ivk}$ for that output's shielded protocol.\n+\n+Let $\\ell_{\\mathsf{d}}$ be as defined in § 5.3 ‘Constants’.\n+\n+Let $\\mathsf{ChaCha20.Decrypt_K}(\\mathsf{C})$ be the unauthenticated decryption of\n+ciphertext $\\mathsf{C} \\;{\\small ⦂}\\; \\mathbb{B}^{{\\kern-0.05em\\tiny\\mathbb{Y}}[\\mathbb{N}]}$\n+with 256-bit key $\\mathsf{K}$, using the stream cipher ChaCha20 as defined in\n+RFC 7539 [#RFC7539-ChaCha20]_ with the initial counter set to 1 (as defined for\n+the encryption component of AEAD_CHACHA20_POLY1305, but without the authentication\n+component). It returns a byte sequence of the same length as $\\mathsf{C}$ and is\n+infallible.\n+\n+The procedure to trial-decrypt a compact output takes as input $\\mathtt{cm*}$ (the\n+$\\mathtt{cmu}$ or $\\mathtt{cmx}$ field of the Output description or Action description\n+respectively), $\\mathtt{ephemeralKey}$, the truncated ``encCiphertext``\n+$\\mathsf{C^{compact}} \\;{\\small ⦂}\\; \\mathbb{B}^{{\\kern-0.05em\\tiny\\mathbb{Y}}[52]}$,\n+the shielded protocol ($\\mathsf{Sapling}$ or $\\mathsf{Orchard}$), block height, and\n+transaction version number, and an incoming viewing key $\\mathsf{ivk}$, and outputs a\n+note for that shielded protocol.\n+\n+This procedure deviates from the standard note decryption using an Incoming Viewing Key, as specified in\n+§ 4.20.2 ‘Decryption using an Incoming Viewing Key (Sapling and Orchard)’ [#protocol-decryptivk]_,\n+only in how $\\mathbf{np}$ is obtained:\n+\n+- let $\\mathsf{P^{compact}} = \\mathsf{ChaCha20.Decrypt_{K^{enc}}}(\\mathsf{C^{compact}})$\n+- extract $\\mathbf{np} = (\\mathsf{leadByte} \\;{\\small ⦂}\\; \\mathbb{B}^{\\kern-0.05em\\tiny\\mathbb{Y}}, \\mathsf{d} \\;{\\small ⦂}\\; \\mathbb{B}^{[\\ell_{\\mathsf{d}}]}, \\mathsf{v} \\;{\\small ⦂}\\; \\{ 0 .. 2^{\\ell_{\\mathsf{value}}}\\!-\\!1 \\}, \\mathsf{rseed} \\;{\\small ⦂}\\; \\mathbb{B}^{{\\kern-0.05em\\tiny\\mathbb{Y}}[32]})$\n+  from $\\mathsf{P^{compact}}$\n+\n+and in the fact that the last step returns only $\\mathbf{n}$, rather than\n+$(\\mathbf{n}, \\mathsf{memo})$.\n+\n+Non-normative note: Previous versions of this ZIP reiterated the compact output\n+decryption procedure (for Sapling) rather than specifying the differences from\n+§ 4.20.2. This led to the following unintended differences:\n+\n+- The procedure described in this ZIP did not support pre-ZIP-212 Sapling outputs.\n+- The swapping of domain separators $[4]$ and $[5]$ in the $\\mathsf{PRF^{expand}}$\n+  inputs used to derive $\\mathsf{rcm}$ and $\\mathsf{esk}$ for Sapling that was fixed in\n+  `version 2023.4.0 of the protocol specification <https://zips.z.cash/protocol/protocol.pdf#2023.4.0>`_,\n+  had not been fixed in this ZIP.\n+- The type error that was fixed in\n+  `version 2025.6.2 of the protocol specification <https://zips.z.cash/protocol/protocol.pdf#2025.6.2>`_,\n+  had not been fixed in this ZIP.\n+- The refactoring to use $\\mathsf{allowedLeadBytes}$ also made in version 2025.6.2\n+  had not been applied.\n+- The last step incorrectly returned $\\mathbf{np}$ rather than the note.\n+\n+Maintaining note commitment tree witnesses\n+``````````````````````````````````````````\n+In order to spend a shielded note, the spender must produce a Merkle path\n+(witness) from the note's commitment to an anchor (a root of the note commitment \n+tree at some block height). [#protocol-merkletree]_ [#protocol-merklepath]_ \n+A light client MUST therefore maintain sufficient note commitment tree state to\n+produce witnesses for its unspent notes.\n+\n+The note commitments in each ``CompactBlock`` (the $\\mathsf{cmu}$ values from\n+``CompactSaplingOutput`` entries and the $\\mathsf{cmx}$ values from\n+``CompactOrchardAction`` entries) are inserted into the respective Sapling or\n+Orchard note commitment trees in the order they appear in the block.\n+\n+A light client that has scanned a contiguous range of blocks can construct\n+witnesses from the note commitments it has observed. However, a light client\n+need not have scanned the entire chain in order to construct witnesses: the\n+``GetSubtreeRoots`` RPC method [#lightwallet-protocol]_ provides roots of\n+completed subtrees of the note commitment trees, which can be combined with\n+locally-scanned commitments to produce valid witnesses even when portions of\n+the chain have not been scanned.\n \n Detecting spends\n ````````````````\n \n-The ``CompactSpend`` entries can be checked against known local nullifiers, to for example\n-ensure that a transaction has been received by the network and mined.\n+The ``CompactSaplingSpend`` nullifier entries and the nullifier fields of\n+``CompactOrchardAction`` entries can be checked against the nullifiers\n+of notes known to the client to determine when those notes have been spent.\n \n Block header validation\n ```````````````````````\n@@ -350,272 +313,152 @@ A ``CompactBlock`` that fails any of these checks MUST be discarded. If it was r\n part of a ``GetBlockRange`` call, the call MUST be aborted.\n \n Block header validation provides light clients with some assurance that the\n-``CompactOutputs`` being sent to them are indeed from valid blocks that have been mined.\n+compact outputs being sent to them are indeed from valid blocks that have been mined.\n The strongest-possible assurance is achieved when all block headers are synchronised; this\n comes at the cost of bandwidth and storage.\n \n By default, ``CompactBlocks`` only contain ``CompactTxs`` for transactions that contain\n-Sapling spends or outputs. Thus they do not contain sufficient information to validate\n+shielded spends or outputs. Thus they do not contain sufficient information to validate\n that the received transaction IDs correspond to the transaction tree root in the block\n header. This does not have a significant effect on light client security: light clients\n-only directly depend on ``CompactOutputs``, which can be authenticated via block header\n+only directly depend on compact outputs, which can be authenticated via block header\n validation. If a txid is used in a ``GetTransaction`` call, the returned transaction\n-SHOULD be checked against the corresponding ``CompactOutputs``, in addition to verifying\n+SHOULD be checked against the corresponding compact outputs, in addition to verifying\n the transaction signatures.\n \n-Potential extensions\n-````````````````````\n-\n-A trivial extension (with corresponding bandwidth cost) would be to transmit empty\n-``CompactTxs`` corresponding to transactions that do not contain Sapling spends or\n-outputs. A more complex extension would send the inner nodes within the transaction\n-trees corresponding to non-Sapling-relevant subtrees; this would require strictly less\n-bandwidth that the trivial extension. These extensions are not currently defined.\n-\n-\n Client-server interaction\n -------------------------\n \n-We can divide the typical client-server interaction into four distinct phases:\n-\n-.. code:: text\n-\n-    Phase   Client                Server\n-    =====   ============================\n-      A     GetLatestBlock ------------>\n-\n-            <---------------- BlockID(X)\n-\n-            GetBlock(X) --------------->\n-\n-            <----------- CompactBlock(X)\n-\n-                ===\n-\n-      B     GetLatestBlock ------------>\n-\n-            <---------------- BlockID(Y)\n-\n-            GetBlockRange(X, Y) ------->\n-\n-            <--------- CompactBlock(X)\n-            <--------- CompactBlock(X+1)\n-            <--------- CompactBlock(X+2)\n-                            ...\n-            <--------- CompactBlock(Y-1)\n-            <--------- CompactBlock(Y)\n-\n-                ===\n-\n-      C     GetTransaction(X+4, 7) ---->\n-\n-            <--- FullTransaction(X+4, 7)\n-\n-            GetTransaction(X+9, 2) ---->\n-\n-            <--- FullTransaction(X+9, 2)\n-\n-                ===\n-\n-      D     GetLatestBlock ------------>\n-\n-            <---------------- BlockID(Z)\n-\n-            GetBlockRange(Y, Z) ------->\n-\n-            <--------- CompactBlock(Y)\n-            <--------- CompactBlock(Y+1)\n-            <--------- CompactBlock(Y+2)\n-                            ...\n-            <--------- CompactBlock(Z-1)\n-            <--------- CompactBlock(Z)\n-\n-**Phase A:** The light client starts up for the first time.\n-\n-- The light client queries the server to fetch the most recent block ``X``.\n-- The light client queries the commitment tree state for block ``X``.\n-\n-  - Or, it has to set ``X`` to the block height at which Sapling activated, so as to be\n-    sent the entire commitment tree. [TODO: Decide which to specify.]\n-\n-- Shielded addresses created by the light client will not have any relevant transactions\n-  in this or any prior block.\n-\n-**Phase B:** The light client updates its local chain view for the first time.\n-\n-- The light client queries the server to fetch the most recent block ``Y``.\n-- It then executes a block range query to fetch every block between ``X`` (inclusive) and\n-  ``Y`` (inclusive).\n-- The block at height ``X`` is checked to ensure the received ``blockHash`` matches the\n-  light client's cached copy, and then discards it without further processing.\n-\n-  - An inconsistency would imply that block ``X`` was orphaned during a chain reorg.\n-\n-- As each subsequent ``CompactBlock`` arrives, the light client:\n-\n-  - Validates the block header if it is present.\n-  - Scans the ``CompactBlock`` to find any relevant transactions for addresses generated\n-    since ``X`` was fetched (likely the first transactions involving those addresses). If\n-    notes are detected, it:\n-\n-    - Generates incremental witnesses for the notes, and updates them going forward.\n-    - Scans for their nullifiers from that block onwards.\n-\n-**Phase C:** The light client has detected some notes and displayed them. User interaction\n-has indicated that the corresponding full transactions should be fetched.\n-\n-- The light client queries the server for each transaction it wishes to fetch.\n-\n-**Phase D:** The user has spent some notes. The light client updates its local chain view\n-some time later.\n-\n-- The light client queries the server to fetch the most recent block ``Z``.\n-- It then executes a block range query to fetch every block between ``Y`` (inclusive) and\n-  ``Z`` (inclusive).\n-- The block at height ``Y`` is checked to ensure the received ``blockHash`` matches the\n-  light client's cached copy, and then discards it without further processing.\n-\n-  - An inconsistency would imply that block ``Y`` was orphaned during a chain reorg.\n-\n-- As each subsequent ``CompactBlock`` arrives, the light client:\n-\n-  - Validates the block header if it is present.\n-  - Updates the incremental witnesses for known notes.\n-  - Scans for any known nullifiers. The corresponding notes are marked as spent at that\n-    height, and excluded from further witness updates.\n-  - Scans for any relevant transactions for addresses generated since ``Y`` was fetched.\n-    These are handled as in phase B.\n-\n-Importing a pre-existing seed\n+A light client MUST perform the following operations as part of synchronisation:\n+\n+- **Trial decryption**: For each ``CompactSaplingOutput`` and ``CompactOrchardAction`` in\n+  the scanned ``CompactBlocks``, the light client attempts trial decryption against its\n+  set of incoming viewing keys. Successfully decrypted outputs represent received notes.\n+  See `Scanning for relevant transactions`_ for details.\n+\n+- **Nullifier matching**: For each ``CompactSaplingSpend`` nullifier and\n+  ``CompactOrchardAction`` nullifier in the scanned ``CompactBlocks``, the light client\n+  checks against its set of known nullifiers for unspent notes. A match indicates that the\n+  corresponding note has been spent.\n+\n+- **Note commitment tree maintenance**: The light client updates its local note commitment\n+  tree state as described in `Maintaining note commitment tree witnesses`_.\n+\n+Retrieving full transactions\n+````````````````````````````\n+\n+The compact block stream provides sufficient information for payment detection\n+and balance tracking, but does not include memo fields or other transaction\n+details. When a light client needs to access the memo field of a received note,\n+the outgoing ciphertexts for spend recovery, or other transaction components,\n+it retrieves the full transaction via the ``GetTransaction`` RPC method.\n+\n+Since a ``GetTransaction`` request reveals to the proxy server that the light\n+client is interested in a specific transaction, full transaction retrieval\n+SHOULD be performed over a privacy-preserving network connection such as Tor\n+[#tor]_ or the Nym mixnet [#nym]_.\n+\n+In addition, even when using a privacy-preserving network connection for these\n+operations, a client SHOULD NOT make transaction data requests to the proxy\n+server in a fashion that links the client's interest in that transaction to\n+other operations that the client may be performing in the clear. For example,\n+if a client uses an unprotected connection for compact block download, that\n+client SHOULD NOT retrieve full transaction data immediately upon transaction\n+discovery, but SHOULD defer it until a later time when the download will \n+not be identifiably related to the download of the compact block range in \n+which the transaction was discovered.\n+\n+Importing a pre-existing key\n `````````````````````````````\n-Phase A of the interaction assumes that shielded addresses created by the light client\n-will have never been used before. This is not a valid assumption if the light client is\n-being initialised with a seed that it did not generate (e.g. a previously backed-up seed).\n-In this case, phase A is modified as follows:\n-\n-**Phase A:** The light client starts up for the first time.\n-\n-- The light client sets ``X`` to the block height at which Sapling activated.\n-\n-  - Shielded addresses created by any light client cannot have any relevant transactions\n-    prior to Sapling activation.\n+The light client's scanning start height (often called the \"wallet birthday\")\n+determines how far back the light client must scan to detect all relevant\n+transactions. When a light client is initialized with a newly generated key, it\n+can use the current chain tip as its wallet birthday, since no prior\n+transactions can be relevant.\n+\n+When initialized with a pre-existing key —whether a seed, a full viewing key,\n+or an incoming viewing key— the light client MUST scan from the key's\n+birthday height, which is the earliest block height at which the key could have\n+been used. If the birthday height is unknown and cannot be estimated by the\n+user, the light client SHOULD scan from the activation height of the earliest\n+shielded protocol supported by the key (Sapling activation at block 419200 on\n+Mainnet, or Orchard activation at block 1687104 on Mainnet).\n \n Block privacy via bucketing\n ---------------------------\n \n *This section describes a proposed enhancement that has not been implemented.*\n \n-The above interaction reveals to the server at the start of each synchronisation phase (B\n-and D) the block height which the light client had previously synchronised to. This is an\n-information leak under our security model (assuming network privacy). We can reduce the\n-information leakage by \"bucketing\" the start point of each synchronisation. Doing so also\n-enables us to handle most chain reorgs simultaneously.\n+When a light client requests a range of compact blocks via ``GetBlockRange``, the start\n+height of the request reveals to the proxy server the block height to which the light\n+client had previously synchronised. This is an information leak under our security model\n+(assuming network privacy). The information leakage can be reduced by \"bucketing\" the\n+start point of each synchronisation request.\n \n Let ``⌊X⌋ = X - (X % N)`` be the value of ``X`` rounded down to some multiple of the\n-bucket size ``N``. The synchronisation phases from the above interaction are modified as\n-follows:\n-\n-.. code:: text\n-\n-    Phase   Client                Server\n-    =====   ============================\n-      B     GetLatestBlock ------------>\n-\n-            <---------------- BlockID(Y)\n-\n-            GetBlockRange(⌊X⌋, Y) ----->\n-\n-            <-------- CompactBlock(⌊X⌋)\n-            <-------- CompactBlock(⌊X⌋+1)\n-            <-------- CompactBlock(⌊X⌋+2)\n-                            ...\n-            <-------- CompactBlock(Y-1)\n-            <-------- CompactBlock(Y)\n-\n-                ===\n-\n-      D     GetLatestBlock ------------>\n-\n-            <---------------- BlockID(Z)\n-\n-            GetBlockRange(⌊Y⌋, Z) ----->\n-\n-            <-------- CompactBlock(⌊Y⌋)\n-            <-------- CompactBlock(⌊Y⌋+1)\n-                            ...\n-            <-------- CompactBlock(Z-1)\n-            <-------- CompactBlock(Z)\n-\n-**Phase B:** The light client updates its local chain view for the first time.\n-\n-- The light client queries the server to fetch the most recent block ``Y``.\n-- It then executes a block range query to fetch every block between ``⌊X⌋`` (inclusive)\n-  and ``Y`` (inclusive).\n-- Blocks between ``⌊X⌋`` and ``X`` are checked to ensure that the received ``blockHash``\n-  matches the light client's chain view for each height, and are then discarded without\n-  further processing.\n-\n-  - If an inconsistency is detected at height ``Q``, the light client sets ``X = Q-1``,\n-    discards all local blocks with height ``>= Q``, and rolls back the state of all local\n-    transactions to height ``Q-1`` (un-mining them as necessary).\n-\n-- Blocks between ``X+1`` and ``Y`` are processed as before.\n-\n-**Phase D:** The user has spent some notes. The light client updates its local chain view\n-some time later.\n-\n-- The light client queries the server to fetch the most recent block ``Z``.\n-- It then executes a block range query to fetch every block between ``⌊Y⌋`` (inclusive)\n-  and ``Z`` (inclusive).\n-- Blocks between ``⌊Y⌋`` and ``Y`` are checked to ensure that the received ``blockHash``\n-  matches the light client's chain view for each height, and are then discarded without\n-  further processing.\n-\n-  - If an inconsistency is detected at height ``R``, the light client sets ``Y = R-1``,\n-    discards all local blocks with height ``>= R``, and rolls back the following local\n-    state to height ``R-1``:\n-\n-    - All local transactions (un-mining them as necessary).\n-    - All tracked nullifiers (unspending or discarding as necessary).\n-    - All incremental witnesses (caching strategies are not covered in this ZIP).\n-\n-- Blocks between ``Y+1`` and ``Z`` are processed as before.\n+bucket size ``N``. Instead of requesting blocks starting from the light client's last\n+synchronised height ``X``, the light client requests blocks starting from ``⌊X⌋``. Blocks\n+between ``⌊X⌋`` and ``X`` are checked against the light client's cached chain view for\n+consistency and then discarded without further processing. This also helps detect chain\n+reorganisations that occurred within the bucket window.\n \n Transaction privacy\n -------------------\n \n-The synchronisation phases give the light client sufficient information to determine\n+The compact block stream gives the light client sufficient information to determine\n accurate address balances, show when funds were received or spent, and spend any unspent\n-notes. As synchronisation happens via a broadcast medium, it leaks no information about\n-which transactions the light client is interested in.\n-\n-If, however, the light client needs access to other components of a transaction (such as\n-the memo fields for received notes, or the outgoing ciphertexts in order to recover spend\n-information when importing a wallet seed), it will need to download the full transaction.\n-The light client SHOULD obscure the exact transactions of interest by downloading numerous\n-uninteresting transactions as well, and SHOULD download all transactions in any block from\n-which a single full transaction is fetched (interesting or otherwise). It MUST convey to\n-the user that fetching full transactions will reduce their privacy.\n+notes. Since synchronisation happens via a broadcast medium (the compact block stream), it\n+leaks no information about which transactions the light client is interested in.\n+\n+However, other API methods reveal more specific interests to the proxy server:\n+\n+- ``GetTransaction`` reveals interest in a specific transaction. See\n+  `Retrieving full transactions`_ for mitigation recommendations.\n+- ``SendTransaction`` reveals that the light client is the sender of a transaction.\n+  Transaction submission SHOULD be performed over a privacy-preserving network connection\n+  such as Tor [#tor]_ or the Nym mixnet [#nym]_.\n+- Transparent address queries (``GetTaddressTransactions``, ``GetTaddressBalance``,\n+  ``GetAddressUtxos``) reveal the transparent addresses held by the light client. These\n+  queries SHOULD be performed over a privacy-preserving network connection such as\n+  Tor [#tor]_ or the Nym mixnet [#nym]_.\n+- If a client queries for transactions related to multiple transparent addresses in\n+  a single request, this will leak the fact that those addresses are controlled by\n+  the same client. If the ``GetTaddressTransactions``, ``GetTaddressBalance`` \n+  or ``GetAddressUtxos`` methods are used, even over a privacy-preserving network\n+  connect, the client SHOULD NOT request data for more than one address at a time\n+  and SHOULD make such requests in a fashion that is not temporally linkable to\n+  other operation of the wallet.\n+- ``GetMempoolTx`` and ``GetMempoolStream`` reveal the light client's interest in\n+  unconfirmed transactions.\n \n \n Reference Implementation\n ========================\n \n-This proposal is supported by a set of libraries and reference code made available by the\n-Electric Coin Company.\n+- `lightwalletd <https://github.com/zcash/lightwalletd>`_ (proxy server)\n+- `zcash_client_backend <https://github.com/zcash/librustzcash/tree/main/zcash_client_backend>`_ (light client scanning library)\n \n \n References\n ==========\n \n .. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n-.. [#protocol-merkletree] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 3.8: Note Commitment Trees <protocol/protocol.pdf#merkletree>`_\n-.. [#protocol-merklepath] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.9: Merkle Path Validity <protocol/protocol.pdf#merklepath>`_\n-.. [#protocol-saplingdecryptivk] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.2: Decryption using an Incoming Viewing Key (Sapling) <protocol/protocol.pdf#saplingdecryptivk>`_\n-.. [#protocol-notept] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.5: Encodings of Note Plaintexts and Memo Fields <protocol/protocol.pdf#notept>`_\n-.. [#protocol-spendencodingandconsensus] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 7.3: Spend Description Encoding and Consensus <protocol/protocol.pdf#spendencodingandconsensus>`_\n-.. [#protocol-outputencodingandconsensus] `Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 7.4: Output Description Encoding and Consensus <protocol/protocol.pdf#outputencodingandconsensus>`_\n+.. [#RFC7539-ChaCha20] `RFC 7539: ChaCha20 and Poly1305 for IETF Protocols. Sections 2.1 to 2.4 inclusive. <https://www.rfc-editor.org/rfc/rfc7539.html#section-2.1>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later <protocol/protocol.pdf>`_\n+.. [#protocol-merkletree] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.8: Note Commitment Trees <protocol/protocol.pdf#merkletree>`_\n+.. [#protocol-networks] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#protocol-merklepath] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.9: Merkle Path Validity <protocol/protocol.pdf#merklepath>`_\n+.. [#protocol-decryptivk] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.20.2: Decryption using an Incoming Viewing Key (Sapling and Orchard) <protocol/protocol.pdf#decryptivk>`_\n+.. [#protocol-concreteorchardkeyagreement] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.6.4: Orchard Key Agreement <protocol/protocol.pdf#concreteorchardkeyagreement>`_\n+.. [#protocol-notept] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.5: Encodings of Note Plaintexts and Memo Fields <protocol/protocol.pdf#notept>`_\n+.. [#protocol-spendencodingandconsensus] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.3: Spend Description Encoding and Consensus <protocol/protocol.pdf#spendencodingandconsensus>`_\n+.. [#protocol-outputencodingandconsensus] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.4: Output Description Encoding and Consensus <protocol/protocol.pdf#outputencodingandconsensus>`_\n+.. [#protocol-actionencodingandconsensus] `Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.5: Action Description Encoding and Consensus <protocol/protocol.pdf#actionencodingandconsensus>`_\n+.. [#zip-0205] `ZIP 205: Deployment of the Sapling Network Upgrade <zip-0205.rst>`_\n .. [#zip-0212] `ZIP 212: Allow Recipient to Derive Sapling Ephemeral Secret from Note Plaintext <zip-0212.rst>`_\n+.. [#zip-0224] `ZIP 224: Orchard Shielded Protocol <zip-0224.rst>`_\n+.. [#lightwallet-protocol] `Zcash Light Client Protocol <https://github.com/zcash/lightwallet-protocol>`_\n .. [#protocolbuffers] `Protocol Buffers documentation <https://developers.google.com/protocol-buffers/>`_\n-.. [#incremental-witness] `zcash_primitives Rust crate — merkle_tree.rs <https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/merkle_tree.rs>`_\n .. [#spv-clients] `Bitcoin Wiki: Scalability — Simplified payment verification <https://en.bitcoin.it/wiki/Scalability#Simplified_payment_verification>`_\n+.. [#tor] `Tor Project <https://www.torproject.org/>`_\n+.. [#nym] `Nym mixnet <https://nymtech.net/>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0307.rst","title":"Light Client Protocol for Payment Detection","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"high-level-design","text":"High-Level Design"},{"level":1,"id":"security-model","text":"Security Model"},{"level":1,"id":"compact-stream-format","text":"Compact Stream Format"},{"level":2,"id":"output-compression","text":"Output Compression"},{"level":2,"id":"spend-compression","text":"Spend Compression"},{"level":1,"id":"proxy-operation","text":"Proxy operation"},{"level":1,"id":"client-operation","text":"Client operation"},{"level":2,"id":"local-processing","text":"Local processing"},{"level":3,"id":"scanning-for-relevant-transactions","text":"Scanning for relevant transactions"},{"level":4,"id":"sapling-and-orchard-compact-trial-decryption","text":"Sapling and Orchard compact trial decryption"},{"level":3,"id":"maintaining-note-commitment-tree-witnesses","text":"Maintaining note commitment tree witnesses"},{"level":3,"id":"detecting-spends","text":"Detecting spends"},{"level":3,"id":"block-header-validation","text":"Block header validation"},{"level":2,"id":"client-server-interaction","text":"Client-server interaction"},{"level":3,"id":"retrieving-full-transactions","text":"Retrieving full transactions"},{"level":3,"id":"importing-a-pre-existing-key","text":"Importing a pre-existing key"},{"level":2,"id":"block-privacy-via-bucketing","text":"Block privacy via bucketing"},{"level":2,"id":"transaction-privacy","text":"Transaction privacy"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1218,"title":"[ZIP TBD] Shielded Voting Protocol: Submission Server","author":"p0mvn","createdAt":"2026-03-18T02:22:19Z","updatedAt":"2026-04-02T16:04:22Z","url":"https://github.com/zcash/zips/pull/1218","diffUrl":"https://github.com/zcash/zips/pull/1218.diff","headRef":"roman/submission-server","headRepo":"valargroup/zips","headSha":"8c280af89797764e1324d6c9d865fe12f2f1fbba","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Introduces a new draft ZIP specifying the submission server for the Shielded Voting Protocol https://github.com/zcash/zips/pull/1200. The submission server is responsible for temporal mixing in the Zcash shielded voting system. The server receives encrypted vote shares from voters and submits them t","zipRefs":[],"draftRefs":["draft-valargroup-submission-server"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-valargroup-submission-server.md","status":"added","additions":581,"deletions":0,"patch":"@@ -0,0 +1,581 @@\n+    ZIP: Unassigned\n+    Title: Vote Share Submission Server\n+    Owners: Dev Ojha <dojha@berkeley.edu>\n+            Roman Akhtariev <ackhtariev@gmail.com>\n+            Adam Tucker <adamleetucker@outlook.com>\n+            Greg Nagy <greg@dhamma.works>\n+    Status: Draft\n+    Category: Standards\n+    Created: 2026-03-06\n+    License: MIT\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"REQUIRED\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and\n+only when, they appear in all capitals.\n+\n+The terms below are to be interpreted as follows:\n+\n+Submission server\n+\n+: An untrusted server that receives encrypted vote shares from voters and\n+  submits the corresponding share reveal transactions to the vote chain.\n+\n+Temporal unlinkability\n+\n+: The property that an observer watching the vote chain cannot attribute\n+  multiple share reveal transactions to a single voter based on\n+  submission timing.\n+\n+Share payload\n+\n+: The off-chain data a voter sends to a submission server for a single\n+  share, containing the information necessary to construct a Vote Reveal\n+  Proof. Defined in [^voting-protocol].\n+\n+Voter\n+\n+: A holder of voting authority (a VAN) who has produced a Vote\n+  Commitment containing encrypted shares and wishes to have those\n+  shares revealed on-chain.\n+\n+Election authority (EA)\n+\n+: The set of validators that collectively hold Shamir shares of the\n+  El Gamal secret key for a voting round.\n+\n+For definitions of *Vote Commitment*, *Vote Commitment Tree*, *vote\n+share*, *Vote Reveal Proof*, *share nullifier*, and *blinded share\n+commitment*, see [^voting-protocol]. For *voting round*, *validator*,\n+and *vote chain*, see [^coinholder-voting].\n+\n+\n+# Abstract\n+\n+This ZIP specifies the submission server, a protocol participant in\n+the Zcash shielded voting system that receives encrypted vote shares\n+from voters and submits them to the vote chain at client-specified\n+times. The server constructs a Vote Reveal Proof for each share and\n+submits the corresponding transaction at the time designated by the\n+voter, interleaving submissions from many voters into a stream that\n+destroys timing correlations.\n+\n+Without temporal mixing, a voter who submits $N_s$ shares for $P$\n+proposals produces $N_s \\times P$ transactions in a short burst. An\n+observer can group these by timing, and after the tally is published,\n+cross-reference the group against low-participation proposal options to\n+narrow the voter's balance to a small range. Client-controlled\n+submission timing eliminates this attack surface: each share carries a\n+$\\mathsf{submit\\_at}$ timestamp sampled uniformly from the remaining\n+voting window, spreading shares from a single voter across time and\n+interleaving them with shares from other voters.\n+\n+Each share is sent to half of the available servers, balancing\n+censorship resistance (redundancy ensures an honest server can relay)\n+against amount privacy (limiting the servers that see each share's\n+ciphertext).\n+\n+Voters who cast their vote near the end of the voting window use a\n+reduced *last-moment mode*: a single share carrying the full ballot\n+count is submitted immediately, sacrificing temporal mixing in exchange\n+for guaranteed inclusion before the deadline.\n+\n+\n+# Motivation\n+\n+The Shielded Voting Protocol [^voting-protocol] encrypts vote amounts\n+under the election authority's public key and splits each vote into\n+$N_s$ shares. Encryption alone does not provide effective privacy when\n+combined with observable metadata.\n+\n+Consider a voter with a large balance voting on $P$ proposals. If the\n+voter submits all $N_s \\times P$ shares directly to the vote chain, an\n+observer sees a burst of transactions arriving together. After the\n+round, the published tally reveals the aggregate total and voter count\n+per (proposal, decision) pair. For proposal options with few voters, the\n+observer can narrow each voter's balance to a tight range. When the\n+timing burst is correlated across proposals, the observer can link all\n+of the voter's choices, and social context (forum posts, public\n+advocacy) can close the remaining gap.\n+\n+The submission server solves this by introducing an intermediary that\n+holds shares and submits them at client-specified times spread across\n+the voting window. The $N_s \\times P$ transactions from a single voter\n+are interleaved into a stream of thousands of similar transactions from\n+hundreds of voters. The timing fingerprint is destroyed.\n+\n+The client samples a $\\mathsf{submit\\_at}$ timestamp for each share,\n+drawn uniformly from the remaining voting window. The server holds the\n+share until that time, then constructs the Vote Reveal Proof and\n+submits the transaction. This design gives the client control over its\n+own temporal mixing distribution while delegating proof construction\n+and reliable delivery to the always-on server. A mobile client may be\n+killed or lose connectivity at the scheduled submission time, but the\n+server guarantees delivery.\n+\n+\n+# Privacy Implications\n+\n+**Temporal mixing.** The client's primary privacy mechanism is\n+spreading share submissions across the voting window by sampling\n+independent $\\mathsf{submit\\_at}$ timestamps. The submission server\n+executes these schedules reliably, interleaving shares from many\n+voters into a uniform stream. Without this mixing, an observer can\n+group shares by submission time and, after tally publication, use\n+low-participation proposal options to reconstruct individual balances.\n+\n+**Per-server information.** Each submission server learns the encrypted\n+share ciphertext and blind factor for the single share it reveals, the\n+blinded share commitments for all $N_s$ shares, the proposal\n+identifier, and the vote decision. It cannot learn the plaintext share\n+amount (encrypted under $\\mathsf{ea}\\_\\mathsf{pk}$), the ciphertexts\n+or blind factors of shares assigned to other servers, the voter's\n+identity, or which VCT leaf the vote commitment corresponds to.\n+\n+**Redundancy vs. restriction tension.** Censorship resistance requires\n+sending each share to multiple servers (so an honest server can relay\n+even if others censor). Amount privacy requires restricting the number\n+of servers that see each share (to limit the set that could collude\n+with the EA to gain an information advantage). These requirements pull\n+in opposite directions; the resolution is specified in [Server\n+Selection].\n+\n+**Decision and share count visibility.** Vote decisions are public\n+inputs to the Vote Reveal Proof and appear in every share reveal\n+transaction on-chain.\n+\n+Because share counts are visible, an observer who also sees the\n+aggregate weight for each decision after tally publication can, in\n+low-participation options, narrow individual voter balances to a\n+range. The protocol limits this inference in two ways: encrypted amounts keep exact balances hidden, and non-uniform share\n+decomposition strategies break the direct relationship between share\n+count and voter balance (see [Open Design Questions]). Encrypting\n+decisions entirely would close this channel but at substantial circuit\n+cost (see [Why Not Encrypt Decisions]).\n+\n+Decision visibility also means a malicious submission server could\n+selectively delay or drop shares for a particular decision; the\n+redundancy mechanisms in [Decision Censorship Resistance] address this.\n+\n+**Voter identity isolation.** Even a fully compromised election\n+authority (one that reconstructs $\\mathsf{ea}\\_\\mathsf{sk}$ and\n+decrypts every share) cannot determine which voter cast a given vote.\n+The delegation phase of the Shielded Voting Protocol [^voting-protocol]\n+moves voting authority from the holder's Orchard spending key to a\n+locally-generated, unlinkable governance hotkey. All vote commitments\n+and share reveals reference only this hotkey. The link between on-chain\n+identity and governance hotkey is broken by the zero-knowledge\n+delegation proof and the alternate nullifier unlinkability property\n+established in [^balance-proof]; the EA never sees anything that\n+connects the two.\n+\n+**Timing metadata.** Submission servers see $\\mathsf{submit\\_at}$\n+timestamps but these are independently sampled per share and do not\n+reveal the voter's identity or total ballot count. A server that logs\n+receipt timestamps could correlate shares received in the same session,\n+but because each share is sent to independently selected server\n+subsets, no single server sees all of a voter's shares. Voters who\n+distribute shares across multiple servers limit any single server's\n+view of their submission pattern.\n+\n+**Last-moment privacy trade-off.** Voters who cast within the\n+last-moment buffer (see [Last-Moment Voting]) submit a single share\n+carrying their full ballot count with immediate delivery. This\n+sacrifices temporal mixing entirely: the share's on-chain arrival time\n+directly reflects when the voter cast. The trade-off is accepted\n+because each share requires the server to construct a Vote Reveal\n+Proof (a computationally expensive ZKP), and with little time\n+remaining the server may not complete all 16 proofs before the\n+deadline — resulting in a partially counted or entirely lost vote.\n+A single share requires only one proof, prioritizing inclusion over\n+mixing.\n+\n+\n+# Requirements\n+\n+- An observer watching the vote chain cannot attribute multiple share\n+  reveal transactions to a single voter based on submission timing.\n+- A single malicious submission server cannot prevent a voter's shares\n+  from reaching the chain, provided at least one server handling those\n+  shares is honest.\n+- No single submission server learns enough information to reconstruct\n+  a voter's total ballot count or link shares across proposals.\n+- The submission server can construct Vote Reveal Proofs without\n+  access to the voter's private key material.\n+- The protocol tolerates submission server unavailability: if a server\n+  goes offline, shares assigned to other servers are unaffected.\n+\n+\n+# Non-requirements\n+\n+- The Vote Reveal Proof circuit and its public/auxiliary inputs are\n+  specified in [^voting-protocol]; this ZIP does not re-specify them.\n+- The share submission payload format is defined in [^voting-protocol].\n+- The vote chain's verification of share reveal transactions (nullifier\n+  checks, ciphertext accumulation) is specified in [^voting-protocol].\n+- Client-side share decomposition (splitting $\\mathsf{num}\\_\\mathsf{ballots}$\n+  into $N_s$ shares) is specified in [^voting-protocol].\n+- The consensus mechanism of the vote chain is out of scope.\n+\n+\n+# Specification\n+\n+## Server Role\n+\n+A submission server is an always-on service that accepts share payloads\n+from voters over an authenticated channel and submits the corresponding\n+share reveal transactions to the vote chain. Each validator on the vote\n+chain embeds a submission server as a helper process.\n+\n+For each share payload received, the server MUST:\n+\n+1. Validate the payload: verify that the vote commitment exists in the\n+   VCT at the claimed position, that the blinded share commitment at\n+   the claimed index is consistent with the provided ciphertext and\n+   blind factor, that the share nullifier has not already been\n+   published, and that $\\mathsf{submit\\_at} \\leq \\mathsf{vote\\_end\\_time}$.\n+2. Enqueue the share for submission at the client-specified\n+   $\\mathsf{submit\\_at}$ time (or immediately if\n+   $\\mathsf{submit\\_at} = 0$).\n+3. At the scheduled time, obtain the current VCT Merkle path for the\n+   vote commitment.\n+4. Derive the share nullifier.\n+5. Construct the Vote Reveal Proof.\n+6. Submit the share reveal transaction to the vote chain.\n+\n+If the share reveal transaction is rejected (e.g., because another\n+server already revealed the same share), the server SHOULD discard the\n+payload without retry.\n+\n+## Submission Timing\n+\n+Each share payload includes a $\\mathsf{submit\\_at}$ field: a Unix\n+timestamp (seconds) specifying when the server SHOULD submit the\n+corresponding share reveal transaction. The server MUST NOT submit the\n+share before this time.\n+\n+- When $\\mathsf{submit\\_at} > 0$, the server enqueues the share and\n+  submits it at or shortly after the specified time. Small inter-share\n+  jitter (on the order of seconds) SHOULD be applied to avoid\n+  simultaneous transaction bursts when multiple shares target nearby\n+  timestamps.\n+- When $\\mathsf{submit\\_at} = 0$, the server MUST submit the share as\n+  soon as possible (last-moment mode; see [Last-Moment Voting]).\n+\n+The server MUST reject any payload where\n+$\\mathsf{submit\\_at} > \\mathsf{vote\\_end\\_time}$ with an error.\n+\n+### Client-Side Delay Sampling\n+\n+The client is responsible for choosing $\\mathsf{submit\\_at}$ for each\n+share. For normal votes (cast outside the last-moment buffer), the\n+client MUST sample each share's submission time independently from a\n+uniform distribution over the remaining voting window:\n+\n+$$\\mathsf{submit\\_at} \\leftarrow \\mathsf{Uniform}(\\mathsf{now}, \\; \\mathsf{vote\\_end\\_time} - \\mathsf{last\\_moment\\_buffer})$$\n+\n+The upper bound excludes the last-moment buffer to ensure all shares\n+are submitted before the window in which last-moment voters begin\n+casting. See [Last-Moment Voting] for the buffer definition.\n+\n+The client MUST NOT batch shares or assign identical timestamps.\n+Independent sampling ensures that shares from a single voter are spread\n+across time and interleaved with shares from other voters.\n+\n+## Last-Moment Voting\n+\n+A voter who casts within the *last-moment buffer* (the final portion\n+of the voting window) uses a reduced submission mode:\n+\n+### Last-Moment Buffer\n+\n+The last-moment buffer is defined as:\n+\n+$$\\mathsf{last\\_moment\\_buffer} = \\min\\!\\left(\\left\\lfloor 0.1 \\times (\\mathsf{vote\\_end\\_time} - \\mathsf{ceremony\\_start}) \\right\\rfloor, \\; 3600\\right)$$\n+\n+That is, 10% of the total round duration (from ceremony completion to\n+vote end), capped at 1 hour. A voter is in last-moment mode when:\n+\n+$$\\mathsf{now} \\geq \\mathsf{vote\\_end\\_time} - \\mathsf{last\\_moment\\_buffer}$$\n+\n+### Single-Share Mode\n+\n+In last-moment mode, the client MUST:\n+\n+1. Place the entire ballot count into a single share (share index 0).\n+   The remaining $N_s - 1$ share slots carry zero value.\n+2. Build only one share payload (for share index 0).\n+3. Set $\\mathsf{submit\\_at} = 0$ (immediate submission).\n+\n+The server processes immediate shares without inter-share jitter delay.\n+\n+### Privacy Rationale\n+\n+Last-moment mode sacrifices temporal mixing for guaranteed inclusion.\n+Each share requires the server to construct a Vote Reveal Proof — a\n+computationally expensive ZKP — before submitting the transaction.\n+With 16 shares and little time remaining, the server may not complete\n+all 16 proofs before the voting window closes, resulting in lost votes.\n+A single share requires only one proof, maximizing the likelihood that\n+the vote is included before the deadline. Getting the vote counted is\n+more important than preserving temporal mixing when the window is\n+nearly closed.\n+\n+The on-chain footprint of a last-moment vote is indistinguishable from\n+a single normal share reveal (same transaction format, same proof\n+type). An observer cannot determine whether a share reveal near the\n+deadline is a last-moment vote or a normally-timed share that happened\n+to be scheduled late.\n+\n+## Server Selection\n+\n+Each share MUST be sent to exactly $\\lceil s/2 \\rceil$ of the $s$\n+available submission servers, where $s$ is the number of active\n+validators (since each validator embeds a submission server). See\n+[Why Half the Servers] for the design rationale.\n+\n+The client MUST select the $\\lceil s/2 \\rceil$ servers uniformly at\n+random for each share independently. Shares from the same vote\n+commitment SHOULD be distributed across different server subsets to\n+minimize any single server's view of the voter's activity.\n+\n+## Per-Server Share Isolation\n+\n+A submission server receives only the ciphertext and blind factor for\n+the single share it is responsible for revealing. The remaining\n+$N_s - 1$ shares are sent to other servers, each of which receives\n+only its own share's raw data.\n+\n+To construct the Vote Reveal Proof, the server needs all $N_s$ blinded\n+share commitments (to recompute $\\mathsf{shares}\\_\\mathsf{hash}$), but\n+these commitments are blinded with independent random factors and do\n+not expose the ciphertexts or blind factors of other shares.\n+\n+This isolation ensures that a compromised server learns at most one\n+share's encrypted amount per vote commitment. Even if the server could\n+decrypt that share (by colluding with $t$ validators to reconstruct\n+$\\mathsf{ea}\\_\\mathsf{sk}$), it would learn only a fraction of the\n+voter's total ballot count.\n+\n+## Decision Censorship Resistance\n+\n+Submission servers see the vote decision for each share they handle,\n+because the decision is a public input to the Vote Reveal Proof and\n+appears in the share reveal transaction. A malicious server could\n+selectively drop shares for decisions it opposes.\n+\n+Because each share is sent to $\\lceil s/2 \\rceil$ servers, a censoring\n+server is effective only if all servers receiving that share collude.\n+Under the assumption that fewer than half the servers are malicious, at\n+least one honest server will relay each share. Alternative mitigation\n+strategies are discussed in [Why Not Encrypt Decisions] and [Why Not\n+TEE-Based Proof Construction].\n+\n+## Duplicate Share Handling\n+\n+Because each share is sent to $\\lceil s/2 \\rceil$ servers, multiple\n+servers will attempt to reveal the same share. The vote chain's share\n+nullifier set prevents double-counting: the first valid share reveal\n+transaction is accepted, and subsequent attempts for the same share\n+nullifier are rejected.\n+\n+Servers MUST treat a share-nullifier-already-exists rejection as a\n+success (the share was revealed by another server) and discard the\n+payload. Servers MUST NOT retry or escalate rejected submissions.\n+\n+## Availability and Fault Tolerance\n+\n+If a submission server goes offline after receiving shares but before\n+submitting them, those shares are lost on that server. Because each\n+share is sent to $\\lceil s/2 \\rceil$ servers, the share will still be\n+revealed by another server unless all $\\lceil s/2 \\rceil$ recipients\n+fail simultaneously.\n+\n+Servers SHOULD persist enqueued shares (including their\n+$\\mathsf{submit\\_at}$ timestamps) to durable storage so that shares\n+survive process restarts. A server that restarts during the voting\n+window SHOULD resume submission of any persisted shares whose\n+$\\mathsf{submit\\_at}$ times have not yet passed. For shares whose\n+$\\mathsf{submit\\_at}$ has already elapsed, the server SHOULD submit\n+them as soon as possible after restart.\n+\n+Servers SHOULD NOT persist share data beyond the end of the voting\n+round. After the round transitions to TALLYING, all share payloads,\n+ciphertexts, blind factors, and blinded share commitments SHOULD be\n+securely erased.\n+\n+\n+# Rationale\n+\n+## Why a Dedicated Submission Server\n+\n+Naively submitting votes reveals balance through metadata. Even with\n+encrypted amounts, an observer can group transactions by timing, and\n+after tally publication, use low-participation proposal options to\n+narrow individual balances. The submission server solves this by\n+providing reliable, always-on execution of client-specified submission\n+schedules and by constructing Vote Reveal Proofs on the voter's behalf.\n+\n+Fully client-side temporal mixing (where the wallet itself staggers\n+submissions over the voting window) was considered but rejected for\n+reliability: mobile clients may be killed, lose connectivity, or enter\n+low-power states during the multi-hour voting window. The hybrid\n+design — client chooses when, server guarantees delivery — preserves\n+client autonomy over timing while ensuring reliable execution.\n+\n+## Why Half the Servers\n+\n+The $\\lceil s/2 \\rceil$ parameter balances two competing concerns:\n+\n+- **Censorship resistance** requires redundancy: if a share is sent to\n+  only one server and that server censors it, the share is lost.\n+  Sending to multiple servers ensures that an honest server can relay.\n+- **Amount privacy** requires restriction: validators who run\n+  submission servers also hold Shamir shares of\n+  $\\mathsf{ea}\\_\\mathsf{sk}$. A validator that sees a share's\n+  ciphertext and colludes with others could gain\n+  an information advantage. Limiting the number of servers that see\n+  each share limits the collusion surface.\n+\n+Sending to half the servers ensures that even if up to\n+$\\lfloor s/2 \\rfloor - 1$ servers are malicious, at least one honest\n+server receives each share. At the same time, no more than half the\n+validator set sees any individual share's ciphertext.\n+\n+For censorship resistance, the adversary model assumes fewer than half\n+the servers are malicious (consistent with the CometBFT assumption of\n+fewer than one-third Byzantine validators, which is strictly stronger).\n+Under this assumption, any subset of $\\lceil s/2 \\rceil$ randomly\n+chosen servers contains at least one honest server.\n+\n+For information restriction, limiting each share to half the servers\n+means that no more than half the validator set (who also hold Shamir\n+shares of $\\mathsf{ea}\\_\\mathsf{sk}$) sees any individual share's\n+ciphertext. Combined with the threshold secret sharing requirement\n+($t = \\lceil n/2 \\rceil + 1$ validators needed to decrypt), a\n+single server that sees a share cannot decrypt it alone.\n+\n+Alternative parameters were considered: sending to all servers\n+maximizes censorship resistance but gives every validator access to\n+every share's ciphertext, undermining information restriction. Sending\n+to a single server minimizes information exposure but provides no\n+censorship resistance.\n+\n+## Why Servers Are Co-located with Validators\n+\n+Submission servers are embedded in validator nodes rather than operated\n+as independent infrastructure. This follows from the protocol's\n+existing trust model: validators already participate in the EA key\n+ceremony and hold Shamir shares. Adding a separate server operator\n+class would introduce a new trust assumption without clear benefit.\n+Co-location also simplifies deployment (a single binary) and ensures\n+that the submission server has direct access to the vote chain for\n+transaction submission.\n+\n+The dual role creates a tension: validators need decryption capability\n+for the tally, but the protocol does not want any single validator to\n+decrypt shares early. Threshold secret sharing\n+(see [^ea-ceremony]) addresses this: $t = \\lceil n/2 \\rceil + 1$\n+validators must collude to reconstruct $\\mathsf{ea}\\_\\mathsf{sk}$,\n+and the server selection rule ensures each share is visible to at most\n+half the validators.\n+\n+## Why Client-Controlled Timing\n+\n+The client samples from a uniform distribution over the remaining\n+window (excluding the last-moment buffer) rather than, for example,\n+exponential or Gaussian. Because each share's $\\mathsf{submit\\_at}$ is\n+sampled independently, a single voter's shares may cluster by chance.\n+However, uniform sampling ensures that the *aggregate* stream of share\n+reveals across all voters is roughly uniform over time. An individual\n+voter's cluster is indistinguishable from coincidental proximity of\n+shares belonging to different voters, provided the total volume of\n+share reveals is sufficiently large.\n+\n+## Why Not Encrypt Decisions\n+\n+Because vote decisions are public, an observer who also sees the\n+published aggregate weight can, for low-participation options, narrow\n+the range of individual voter balances — even though exact amounts\n+remain encrypted. Encrypting decisions would close this channel but at\n+significant cost: a per-option ciphertext approach requires each share\n+to carry $k$ ciphertexts ($\\mathrm{Enc}(v)$ for the chosen option,\n+$\\mathrm{Enc}(0)$ for the remaining $k - 1$), multiplying per-share\n+circuit cost by $k$. A validator-decrypted variant hides per-option\n+counts from the public but not from validators, and making the\n+bucketing publicly verifiable requires an additional proof system that\n+scales with total share count.\n+\n+The protocol instead limits share-count informativeness through\n+non-uniform share decomposition (see [Open Design Questions]), which\n+breaks the direct relationship between share count and individual\n+voter balances without adding circuit complexity.\n+\n+## Why Not TEE-Based Proof Construction\n+\n+Running Vote Reveal Proof construction inside a Trusted Execution\n+Environment would allow the server to handle decrypted decisions\n+without observing them, providing censorship resistance without the\n+circuit cost of decision encryption. However, TEEs introduce\n+infrastructure complexity (all validators would need compatible\n+hardware), rely on vendor-specific trust assumptions, and are subject\n+to side-channel attacks that have been demonstrated against SGX and\n+similar platforms. The multi-server redundancy approach achieves\n+adequate censorship resistance under the existing honest-majority\n+assumption without these additional dependencies.\n+\n+## Open Design Questions\n+\n+Several design questions affect the submission server's effectiveness\n+but are not yet resolved:\n+\n+- **Share decomposition strategy.** For normal votes, the ballot count\n+  is split roughly evenly across $N_s = 16$ shares (floor division,\n+  remainder added to the last share). For last-moment votes, the full\n+  count goes into a single share (see [Last-Moment Voting]). Alternative\n+  decomposition strategies (such as powers-of-two denominations that\n+  make shares from different voters indistinguishable by amount, or\n+  base-10 decomposition with PRF-randomized remainders) could\n+  strengthen the mixing guarantee but are not yet adopted.\n+- **Client confirmation via PIR.** Voters currently have no\n+  privacy-preserving way to confirm that their shares were submitted.\n+  A PIR-based confirmation mechanism (querying the vote chain for\n+  share nullifiers without revealing which nullifiers are being\n+  checked) would close this gap. The Nullifier PIR\n+  ZIP [^pir-governance] provides a foundation, but adapting it for\n+  share nullifier queries requires additional specification.\n+- **Balance amendment.** A voter who is the sole participant on an\n+  unpopular proposal option may have their exact balance revealed by\n+  the tally (the aggregate equals their individual contribution).\n+  An opt-in mechanism to amend the voting ballot — lowering, rounding,\n+  or padding the declared balance and proving the amendment in ZK —\n+  would mitigate this. This is orthogonal to the submission server but\n+  affects the end-to-end privacy guarantee.\n+- **TEE-based proof construction.** Running Vote Reveal Proof\n+  construction inside a Trusted Execution Environment would allow the\n+  server to handle decrypted decisions without observing them,\n+  providing censorship resistance without decision encryption's circuit\n+  cost. Side-channel risks and infrastructure barriers make this a\n+  longer-term option.\n+\n+\n+# Reference Implementation\n+\n+Reference implementation will be provided before this ZIP advances beyond Draft status.\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^voting-protocol]: [Draft ZIP: Shielded Voting Protocol](draft-valargroup-shielded-voting)\n+\n+[^ea-ceremony]: [Draft ZIP: Election Authority Key Ceremony](draft-valargroup-ea-key-ceremony)\n+\n+[^coinholder-voting]: [Draft ZIP: Zcash Shielded Coinholder Voting](draft-valargroup-shielded-voting-setup)\n+\n+[^balance-proof]: [Draft ZIP: Orchard Proof-of-Balance](draft-valargroup-orchard-balance-proof)\n+\n+[^pir-governance]: [Draft ZIP: Private Information Retrieval for Nullifier Exclusion Proofs](draft-valargroup-nullifier-pir)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-valargroup-submission-server.md","title":"Vote Share Submission Server","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"server-role","text":"Server Role"},{"level":2,"id":"submission-timing","text":"Submission Timing"},{"level":3,"id":"client-side-delay-sampling","text":"Client-Side Delay Sampling"},{"level":2,"id":"last-moment-voting","text":"Last-Moment Voting"},{"level":3,"id":"last-moment-buffer","text":"Last-Moment Buffer"},{"level":3,"id":"single-share-mode","text":"Single-Share Mode"},{"level":3,"id":"privacy-rationale","text":"Privacy Rationale"},{"level":2,"id":"server-selection","text":"Server Selection"},{"level":2,"id":"per-server-share-isolation","text":"Per-Server Share Isolation"},{"level":2,"id":"decision-censorship-resistance","text":"Decision Censorship Resistance"},{"level":2,"id":"duplicate-share-handling","text":"Duplicate Share Handling"},{"level":2,"id":"availability-and-fault-tolerance","text":"Availability and Fault Tolerance"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"why-a-dedicated-submission-server","text":"Why a Dedicated Submission Server"},{"level":2,"id":"why-half-the-servers","text":"Why Half the Servers"},{"level":2,"id":"why-servers-are-co-located-with-validators","text":"Why Servers Are Co-located with Validators"},{"level":2,"id":"why-client-controlled-timing","text":"Why Client-Controlled Timing"},{"level":2,"id":"why-not-encrypt-decisions","text":"Why Not Encrypt Decisions"},{"level":2,"id":"why-not-tee-based-proof-construction","text":"Why Not TEE-Based Proof Construction"},{"level":2,"id":"open-design-questions","text":"Open Design Questions"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1199,"title":"[ZIP TBD] Orchard Proof-of-Balance","author":"czarcas7ic","createdAt":"2026-03-04T18:15:11Z","updatedAt":"2026-03-31T21:49:46Z","url":"https://github.com/zcash/zips/pull/1199","diffUrl":"https://github.com/zcash/zips/pull/1199.diff","headRef":"draft-valargroup-orchard-balance-proof","headRepo":"valargroup/zips","headSha":"6d7d72bf80e8eb6e5260923df43a6ca4c3e86c64","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"## Summary - Adds a new informational draft ZIP (`draft-valargroup-orchard-balance-proof.md`) specifying the Orchard Proof-of-Balance protocol, which is intended to be used in a future Zcash voting application. - Enables users to prove the total value of their Orchard notes without revealing individ","zipRefs":[],"draftRefs":["draft-valargroup-orchard-balance-proof","draft-str4d-orchard-balance-proof"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-valargroup-orchard-balance-proof.md","status":"added","additions":1137,"deletions":0,"patch":"@@ -0,0 +1,1137 @@\n+    ZIP: Unassigned\n+    Title: Orchard Proof-of-Balance\n+    Owners: Dev Ojha <dojha@berkeley.edu>\n+            Adam Tucker <adamleetucker@outlook.com>\n+            Roman Akhtariev <ackhtariev@gmail.com>\n+            Greg Nagy <greg@dhamma.works>\n+    Credits: Daira-Emma Hopwood\n+             Jack Grigg\n+    Status: Draft\n+    Category: Informational\n+    Created: 2026-03-03\n+    License: MIT\n+    Pull-Request: <https://github.com/zcash/zips/pull/???>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document\n+are to be interpreted as described in BCP 14 [^BCP14] when, and only\n+when, they appear in all capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms below are to be interpreted as follows:\n+\n+Pool snapshot\n+\n+: A pair $(\\mathsf{rt^{cm}}, \\mathsf{rt^{excl}})$ consisting of the\n+  Orchard note commitment tree root and the nullifier non-membership tree\n+  root, both computed as of the end of a specified block height. The pool\n+  snapshot defines the set of notes eligible for claims.\n+\n+Nullifier non-membership tree\n+\n+: An Indexed Merkle Tree (IMT) whose leaves represent contiguous intervals\n+  between consecutive revealed nullifiers. A Merkle path to a leaf, together\n+  with a range check, proves that a given nullifier does not appear in the\n+  set of revealed nullifiers at the snapshot height. See [Nullifier\n+  Non-Membership Tree].\n+\n+Alternate nullifier\n+\n+: A value derived from an Orchard note and a nullifier domain, with similar\n+  cryptographic properties to the note's standard nullifier but unlinkable\n+  to it. A note has exactly one alternate nullifier for each nullifier\n+  domain. See [Alternate Nullifier Derivation].\n+\n+Nullifier domain\n+\n+: A unique identifier, encoded as an element of $\\mathbb{F}_ {q_ {\\mathbb{P}}}$,\n+  that scopes alternate nullifiers to a particular application instance. Two\n+  claims in the same domain for the same note produce the same alternate\n+  nullifier; claims in different domains are unlinkable.\n+\n+Claim\n+\n+: A zero-knowledge proof, together with a spend authorization signature,\n+  demonstrating that the prover holds an unspent Orchard note with a\n+  committed value at a given pool snapshot, and revealing the note's\n+  alternate nullifier in a specified domain.\n+\n+Dummy signed note\n+\n+: A synthetic Orchard note with value 1 zatoshi that does not exist in\n+  any on-chain note commitment tree. It is constructed solely for\n+  obtaining a spend authorization signature from the holder's key. Its\n+  value does not contribute to the claimed balance. See [Wallet Signing]\n+  and [Dummy Signed Note].\n+\n+\n+# Abstract\n+\n+This ZIP specifies a mechanism for proving ownership of unspent Orchard\n+notes at a snapshot of the shielded pool, without revealing the notes'\n+standard nullifiers or linking claims to past or future on-chain spending.\n+\n+The mechanism introduces two primitives: a *nullifier non-membership tree*\n+(an Indexed Merkle Tree of intervals between consecutive revealed\n+nullifiers) that enables efficient in-circuit proofs that a note was\n+unspent at the snapshot height, and *alternate nullifiers* (domain-separated\n+values derived from a note's standard nullifier using Poseidon) that\n+prevent double-claiming within an application domain while remaining\n+unlinkable across domains and to on-chain activity.\n+\n+A Claim circuit combines these primitives with standard Orchard note\n+commitment integrity, Merkle membership, spend authority, and diversified\n+address integrity checks to produce a single proof per\n+note. An optional multi-note batching extension allows proving aggregate\n+balance across several notes in one proof.\n+\n+Possible applications include private air drops, private proof-of-balance,\n+and private stake-weighted polling. Application-specific protocols (such\n+as coinholder voting) are specified in separate ZIPs that reference this\n+one.\n+\n+\n+# Motivation\n+\n+Several applications in the Zcash ecosystem require a holder to\n+demonstrate that they controlled a certain balance at a point in time,\n+without revealing which specific notes they held or linking the\n+demonstration to their on-chain transaction history. Examples include:\n+\n+- **Air drops**: distributing tokens proportional to shielded holdings.\n+- **Stake-weighted polling**: weighting votes by ZEC balance.\n+- **Proof-of-balance**: demonstrating solvency or collateral without\n+  revealing addresses.\n+\n+The naive approach of revealing standard nullifiers would link the\n+claim to future spending, destroying the privacy that Zcash's shielded\n+transactions are designed to provide. An alternative approach of actually\n+spending the claimed notes (e.g., to oneself) with an anchor at the\n+snapshot height has the disadvantage that the same notes cannot be used\n+in concurrent applications, and it permanently commits the holder to the\n+claim by recording it on the main chain.\n+\n+Domain-separated alternate nullifiers resolve both problems: they are\n+unlinkable to the standard nullifiers (even after the note is later spent\n+on-chain), and they are scoped to a specific application instance so that\n+the same notes can participate in independent, concurrent applications\n+without interference.\n+\n+The concept of alternate nullifiers and nullifier non-membership trees for\n+proof-of-balance was first described by Daira-Emma Hopwood and Jack Grigg\n+in a 2023 draft. [^draft-str4d-orchard-balance-proof] This ZIP builds on\n+that work with a concrete instantiation informed by the implementation\n+experience of the Zcash coinholder voting system.\n+\n+A key practical consideration is that many holders custody their spending\n+keys on hardware wallets such as Keystone. Without a delegation mechanism,\n+hardware wallet users must either export their spending keys to software\n+(negating the security benefit of hardware custody) or perform many more\n+hardware wallet signing interactions (one per protocol action rather than\n+a single delegation) each with limited ability to verify the signing\n+context on screen. This ZIP therefore specifies a PCZT-based signing flow (see\n+[Wallet Signing]) that works with any hardware wallet supporting\n+Orchard PCZT signing today, requiring no firmware changes.\n+\n+\n+# Privacy Implications\n+\n+**Unlinkability to standard nullifiers.** The alternate nullifier\n+$\\mathsf{nf_ {dom}}$ for a note is computed as a Poseidon hash keyed by the\n+nullifier deriving key $\\mathsf{nk}$, which is secret. An observer who\n+sees both $\\mathsf{nf_ {dom}}$ (published during a claim) and the standard\n+nullifier $\\mathsf{nf^{old}}$ (published when the note is later spent on\n+the Zcash chain) cannot link them without knowledge of $\\mathsf{nk}$.\n+This property holds under the assumption that Poseidon is a pseudorandom\n+function when keyed with a uniformly random field element (see\n+[Security Argument]).\n+\n+**Cross-domain unlinkability.** If the same note participates in two\n+independent applications with different nullifier domains $\\mathsf{dom_ 1}$\n+and $\\mathsf{dom_ 2}$, the resulting alternate nullifiers\n+$\\mathsf{nf_ {dom_ 1}}$ and $\\mathsf{nf_ {dom_ 2}}$ are unlinkable. This\n+follows from the PRF property: distinct inputs produce outputs that are\n+computationally indistinguishable from independent random values.\n+\n+**Spend authority prevents viewing-key delegation.** The circuit\n+constrains correct derivation of $\\mathsf{rk}$, and the protocol\n+requires a valid spend authorization signature under $\\mathsf{rk}$\n+(see [Spend Authorization Signature]), ensuring that a party with only\n+a viewing key cannot make claims on behalf of the holder.\n+\n+**Non-membership tree queries.** Obtaining a Merkle path in the nullifier\n+non-membership tree requires querying a data source that holds the full\n+tree. A naive query reveals which path is being requested, potentially\n+linking the querier to a nullifier range. Private information retrieval\n+(PIR) techniques can mitigate this leakage [^pir-nullifier-exclusion]; their\n+specification is out of scope for this document.\n+\n+**Hardware wallet device exposure.** When the PCZT-based hardware wallet\n+signing flow (see [Wallet Signing]) is used, the device observes\n+the PCZT but learns no information about the holder's real\n+Orchard balance or delegation context. The PCZT contains a 1-zatoshi\n+dummy note with no on-chain counterpart; the holder's actual note\n+commitments, values, and nullifiers are never transmitted to the device.\n+The application commitment, alternate nullifiers, and usage identifier are\n+committed via the dummy note's nullifier inside the ZKP circuit and\n+do not appear as plaintext fields in the PCZT. The output address\n+(application hotkey) is visible on the device but is freshly generated\n+per usage instance and is not linked to the holder's on-chain Orchard\n+addresses. An attacker with physical access to the device during signing\n+learns only that the holder is participating in a proof-of-balance\n+instance and the declared balance.\n+\n+\n+# Requirements\n+\n+- A holder can prove ownership of an unspent Orchard note at a pool\n+  snapshot without revealing the note's standard nullifier.\n+- Double-claiming the same note within the same nullifier domain is\n+  detectable.\n+- Claims are unlinkable to the holder's past or future on-chain spends,\n+  and to their claims in other nullifier domains.\n+- The holder proves spend authority (not merely viewing access).\n+- The construction allows a holder to participate in multiple independent\n+  applications using the same notes, provided each application uses a\n+  distinct nullifier domain.\n+\n+\n+# Non-requirements\n+\n+- Privacy-preserving retrieval of non-membership Merkle paths (e.g., via\n+  PIR) is out of scope.\n+- Value denomination conversions (e.g., converting zatoshi to\n+  application-specific units) are out of scope.\n+- Transaction-level encoding of claims is out of scope; this ZIP specifies\n+  only the proof statement and its verification.\n+\n+\n+# Specification\n+\n+## Pool Snapshot\n+\n+A pool snapshot at block height $h$ is a pair\n+$(\\mathsf{rt^{cm}}, \\mathsf{rt^{excl}})$ where:\n+\n+- $\\mathsf{rt^{cm}}$ is the root of the Orchard note commitment tree\n+  as of the end of block $h$. [^protocol-orchardcommitmenttree]\n+- $\\mathsf{rt^{excl}}$ is the root of the nullifier non-membership tree\n+  constructed from the set of all Orchard nullifiers revealed on the\n+  consensus chain as of the end of block $h$.\n+\n+The pool snapshot MUST be deterministically computable from the consensus\n+chain state at height $h$. Any party with access to the chain state MUST\n+be able to independently verify both roots.\n+\n+\n+## Nullifier Non-Membership Tree\n+\n+The nullifier non-membership tree is a Merkle tree whose leaves are\n+commitments to the gaps between consecutive revealed nullifiers, enabling\n+efficient in-circuit proofs that a given nullifier is absent from the\n+revealed set.\n+\n+The relationship between the sorted nullifier set and the tree is\n+illustrated below:\n+\n+```\n+            /  \\                          ..\n+           /    \\                        /\n+       gcm_0    gcm_1               gcm_2      <-- Gap commitments (tree leaves)\n+      /\\            /\\              /\\\n+  |(l_0, w_0)| |(l_1, w_1)|   |(l_2, w_2) ..  <-- Gap encodings (low, width)\n+s_0|          |s_1|         |s_2|          ..   <-- Sorted nullifier set + sentinels\n+```\n+\n+Each gap between consecutive nullifiers is encoded as a\n+$(\\mathsf{low}, \\mathsf{width})$ pair, then committed via\n+$\\mathsf{GapCommit}$ (see [Hash Function]) to produce a tree leaf.\n+\n+### Construction\n+\n+Let $S = \\{s_ 0, s_ 1, \\ldots, s_ {m-1}\\} \\subset \\mathbb{F}_ {q_ {\\mathbb{P}}}$ be the union of the set of all Orchard\n+nullifiers revealed on the consensus chain as of block height $h$ and\n+the sentinel values defined in [Sentinel Initialization]. Sort $S$ in\n+ascending order.\n+Because sentinel initialization is mandatory, $S$ is non-empty.\n+\n+For each pair of consecutive elements $(s_ i, s_ {i+1})$ where $s_ i < s_ {i+1}$\n+and $s_ {i+1} - s_ i > 1$, compute a gap encoding:\n+\n+- $\\mathsf{low} = s_ i + 1$: the first value in the gap\n+- $\\mathsf{width} = s_ {i+1} - s_ i - 2$: the number of additional values\n+  in the gap beyond $\\mathsf{low}$\n+\n+The gap encoding represents the closed interval\n+$[\\mathsf{low},\\; \\mathsf{low} + \\mathsf{width}]$, which contains exactly\n+the field elements between $s_ i$ and $s_ {i+1}$ exclusive. The\n+corresponding tree leaf is $\\mathsf{GapCommit}(\\mathsf{low}, \\mathsf{width})$.\n+\n+After processing all consecutive pairs, if $s_ {m-1} < q_ {\\mathbb{P}} - 1$,\n+a terminal gap MUST be added with:\n+\n+- $\\mathsf{low} = s_ {m-1} + 1$\n+- $\\mathsf{width} = (q_ {\\mathbb{P}} - 1) - \\mathsf{low}$\n+\n+This ensures that all unrevealed values above the largest element of $S$ are\n+represented in the tree.\n+\n+<details>\n+<summary>\n+\n+### Rationale for (low, width) encoding\n+</summary>\n+\n+An alternative encoding stores $(\\mathsf{start}, \\mathsf{end})$ pairs\n+directly, as proposed in [^draft-str4d-orchard-balance-proof]. The\n+$(\\mathsf{low}, \\mathsf{width})$ encoding precomputes\n+$\\mathsf{width} = \\mathsf{end} - \\mathsf{low}$ during tree construction,\n+saving one field subtraction in the in-circuit interval check. Since tree\n+construction is performed once per snapshot while the circuit is evaluated\n+per claim, this trade-off favors prover efficiency.\n+</details>\n+\n+### Hash Function\n+\n+The non-membership tree uses Poseidon [^protocol-poseidon] with the\n+$\\mathsf{P128Pow5T3}$ instantiation (width $t = 3$, rate 2) over the\n+Pallas base field $\\mathbb{F}_ {q_ {\\mathbb{P}}}$ for all hashing:\n+\n+- **Gap commitment:** $\\mathsf{GapCommit}(\\mathsf{low}, \\mathsf{width})$,\n+  a 2-input Poseidon hash (one permutation).\n+- **Internal node hash:** $\\mathsf{PoseidonNodeHash}(\\mathsf{left}, \\mathsf{right})$,\n+  a 2-input Poseidon hash (one permutation), where $\\mathsf{left}$ and\n+  $\\mathsf{right}$ are determined by the node's position bit at each level.\n+\n+Both functions use $\\mathsf{P128Pow5T3}$ with $\\mathsf{ConstantLength}\\langle 2 \\rangle$.\n+This is the same Poseidon instantiation used by Orchard for nullifier\n+derivation [^protocol-poseidon].\n+\n+<details>\n+<summary>\n+\n+### Rationale for Poseidon over Sinsemilla\n+</summary>\n+\n+The Orchard note commitment tree uses Sinsemilla [^protocol-sinsemilla]\n+for Merkle hashing. The non-membership tree uses Poseidon instead because\n+Poseidon operates natively on field elements, making it significantly more\n+efficient inside Halo 2 arithmetic circuits. Sinsemilla is optimized for\n+bitstring inputs and incurs overhead when hashing field elements. Since\n+the non-membership tree is a new data structure not constrained by backwards\n+compatibility, the more circuit-efficient choice is appropriate.\n+\n+Poseidon2 was considered but not adopted because no audited implementation was\n+available at the time of design. Poseidon already has an audited implementation\n+used by Orchard for nullifier derivation.\n+</details>\n+\n+### Tree Depth\n+\n+$\\mathsf{MerkleDepth^{excl}}$ MUST be large enough to accommodate the\n+number of gap leaves. Each nullifier in the pool adds at most one leaf\n+(by splitting an existing gap), so the tree requires capacity for at\n+least $|N| + 17$ leaves, where $|N|$ is the size of the Orchard\n+nullifier set and 17 is the number of sentinels for the Pallas base\n+field. Concretely:\n+\n+$$\\mathsf{MerkleDepth^{excl}} \\geq \\lceil \\log_ 2(|N| + 17) \\rceil$$\n+\n+A depth of $\\mathsf{MerkleDepth^{excl}} = 29$ is RECOMMENDED. This\n+supports up to $2^{29} \\approx 537$ million leaves. As of early 2026,\n+the Zcash Orchard pool contains roughly 51 million nullifiers, so\n+depth 29 provides about one order of magnitude of headroom.\n+\n+Unused leaf positions MUST be filled with the canonical empty leaf value\n+$\\mathsf{GapCommit}(0, 0)$. This is non-overlapping with every valid\n+gap encoding because the sentinel set always includes $0$ (see\n+[Sentinel Initialization]), so $\\mathsf{low} = 0$ can never appear in\n+a valid gap.\n+\n+### Sentinel Initialization\n+\n+Before inserting any real nullifiers, the non-membership tree MUST be\n+initialized with sentinel values that partition $\\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+into intervals each of width strictly less than $2^{250}$. The sentinel\n+set MUST include the value $0$.\n+\n+For the Pallas base field ($q_ {\\mathbb{P}} \\approx 2^{254}$),\n+implementations MUST insert 17 sentinels $s_k \\in \\mathbb{F}_ {q_ {\\mathbb{P}}}$ at:\n+\n+$$s_k = k \\cdot 2^{250}, \\quad k \\in \\{0,1,\\ldots,16\\}.$$\n+\n+This guarantees the width bound required by the in-circuit range checks:\n+\n+- For $k = 0,\\ldots,15$, the interval between consecutive sentinels has\n+  width exactly $2^{250} - 2$.\n+- The final tail interval has width\n+  $q_ {\\mathbb{P}} - 16 \\cdot 2^{250} - 2 < 2^{250}$.\n+\n+The width bound is critical for the soundness of the in-circuit range\n+checks. If any interval had width $\\geq 2^{250}$, the range check could\n+overflow, allowing a prover to falsely claim non-membership.\n+\n+These sentinel values are constants defined by this specification.\n+\n+\n+## Nullifier Domains\n+\n+A nullifier domain $\\mathsf{dom}$ is an element of\n+$\\mathbb{F}_ {q_ {\\mathbb{P}}}$ that defines a double-claim detection scope.\n+Each independent claim context (such as a specific air-drop event, a single\n+poll, or an individual voting round) MUST use its own domain.\n+\n+Applications MUST ensure that the domain value is unique across\n+independent claim contexts. Two claims that share the same domain and the\n+same underlying note will produce the same alternate nullifier, which is\n+the mechanism for double-claim prevention.\n+\n+Applications SHOULD derive the domain deterministically by hashing a\n+protocol identifier with an application-specific instance value. The\n+protocol identifier SHOULD be the application's ZIP number, encoded as\n+a little-endian Pallas base field element, which guarantees\n+cross-application uniqueness. The instance value (such as a round or\n+event identifier) ensures uniqueness across instances of the same\n+application.\n+\n+Applications MAY define their own domain derivation scheme provided it\n+satisfies the uniqueness requirement above.\n+\n+\n+## Alternate Nullifier Derivation\n+\n+Given a nullifier domain $\\mathsf{dom}$ and an Orchard note with standard\n+nullifier $\\mathsf{nf^{old}}$, the alternate nullifier $\\mathsf{nf_ {dom}}$\n+is computed as:\n+\n+$$\\mathsf{nf_ {dom}} = \\mathsf{DeriveAlternateNullifier_ {nk}}(\\mathsf{dom}, \\mathsf{nf^{old}}) = \\mathsf{Poseidon}(\\mathsf{nk}, \\mathsf{dom}, \\mathsf{nf^{old}})$$\n+\n+where:\n+\n+- $\\mathsf{nk} \\in \\mathbb{F}_ {q_ {\\mathbb{P}}}$ is the nullifier deriving\n+  key from the holder's full viewing key.\n+- $\\mathsf{dom} \\in \\mathbb{F}_ {q_ {\\mathbb{P}}}$ is the nullifier domain\n+  (see [Nullifier Domains]).\n+- $\\mathsf{nf^{old}} \\in \\mathbb{F}_ {q_ {\\mathbb{P}}}$ is the note's\n+  standard Orchard nullifier, computed as\n+  $\\mathsf{DeriveNullifier_ {nk}}(\\text{ρ}^{\\mathsf{old}}, \\text{ψ}^{\\mathsf{old}}, \\mathsf{cm^{old}})$.\n+\n+This is a 3-input Poseidon hash. Using the standard $\\mathsf{P128Pow5T3}$\n+instantiation (width $t = 3$, rate 2), this requires two permutations.\n+\n+### Properties\n+\n+**Deterministic.** The same note in the same domain always produces the\n+same alternate nullifier, enabling double-claim detection.\n+\n+**Unlinkable to standard nullifier.** An adversary who observes\n+$\\mathsf{nf_ {dom}}$ and later observes $\\mathsf{nf^{old}}$ (when the note\n+is spent on-chain) cannot link them without knowledge of $\\mathsf{nk}$.\n+\n+**Cross-domain unlinkable.** Alternate nullifiers for the same note in\n+different domains are computationally indistinguishable from independent\n+random values.\n+\n+**Collision resistant.** Distinct notes in the same domain produce\n+distinct alternate nullifiers, under the collision resistance of Poseidon.\n+\n+### Security Argument\n+\n+The security of the alternate nullifier derivation relies on Poseidon\n+being a pseudorandom function (PRF) when keyed with a uniformly random\n+element of $\\mathbb{F}_ {q_ {\\mathbb{P}}}$.\n+\n+**Unlinkability.** Model $\\mathsf{Poseidon}(\\mathsf{nk}, \\cdot, \\cdot)$\n+as a PRF keyed by $\\mathsf{nk}$. An adversary who does not know\n+$\\mathsf{nk}$ sees outputs that are indistinguishable from random. Given\n+$\\mathsf{nf_ {dom}} = \\mathsf{Poseidon}(\\mathsf{nk}, \\mathsf{dom}, \\mathsf{nf^{old}})$\n+and $\\mathsf{nf^{old}}$, the adversary cannot verify the relationship\n+without $\\mathsf{nk}$. The nullifier deriving key $\\mathsf{nk}$ is a\n+255-bit value derived from the spending key and is never revealed on-chain.\n+\n+**Cross-domain unlinkability.** For two domains $\\mathsf{dom_ 1} \\neq \\mathsf{dom_ 2}$,\n+the tuples $(\\mathsf{dom_ 1}, \\mathsf{nf^{old}})$ and\n+$(\\mathsf{dom_ 2}, \\mathsf{nf^{old}})$ are distinct inputs to the PRF.\n+Under the PRF assumption, their outputs are jointly indistinguishable from\n+independent random values.\n+\n+**Collision resistance.** If $\\mathsf{nf^{old}_ 1} \\neq \\mathsf{nf^{old}_ 2}$,\n+then $(\\mathsf{nk}, \\mathsf{dom}, \\mathsf{nf^{old}_ 1})$ and\n+$(\\mathsf{nk}, \\mathsf{dom}, \\mathsf{nf^{old}_ 2})$ are distinct Poseidon\n+inputs, so their outputs collide with negligible probability under\n+Poseidon's collision resistance.\n+\n+<details>\n+<summary>\n+\n+### Rationale for divergence from the Orchard-native construction\n+</summary>\n+\n+The earlier draft [^draft-str4d-orchard-balance-proof] proposed an\n+alternate nullifier derivation structurally parallel to the standard\n+Orchard nullifier:\n+\n+$$\\mathsf{Extract}_ {\\mathbb{P}}\\!\\left(\\!\\left[(\\mathsf{PRF^{nfAlternate}_ {nk}}(\\text{ρ}^{\\mathsf{old}}, \\mathsf{dom}) + \\text{ψ}^{\\mathsf{old}}) \\bmod q_ {\\mathbb{P}}\\right] \\mathcal{K}^\\mathsf{Orchard} + \\mathsf{cm^{old}}\\right)$$\n+\n+That construction has the advantage of sharing more circuit infrastructure\n+with the standard nullifier check.\n+\n+The Poseidon-based construction adopted here was chosen for simplicity:\n+it requires only a single Poseidon hash rather than an additional\n+fixed-base scalar multiplication, and it reuses the standard nullifier\n+$\\mathsf{nf^{old}}$ that the circuit already computes for the\n+non-membership check. The security assumption is comparable, as both\n+constructions rely on the hardness of distinguishing $\\mathsf{nk}$-keyed\n+evaluations from random.\n+\n+The current Zcash coinholder voting system was designed and implemented\n+around this construction. A future revision of this ZIP may revisit the\n+choice as part of a broader protocol revision. For example, if hardware\n+wallet firmware support (e.g., a Keystone testnet byte for governance\n+signing) restructures the spend authority flow in a way that favors\n+tighter integration with the standard Orchard nullifier derivation.\n+</details>\n+\n+\n+## Claim Circuit\n+\n+A valid instance of a Claim proof $\\pi$ assures the following statement.\n+\n+### Public Inputs\n+\n+Given a primary input:\n+\n+- $\\mathsf{rt^{cm}} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}$\n+- $\\mathsf{rt^{excl}} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}$\n+- $\\mathsf{rk} ⦂ \\mathsf{SpendAuthSig^{Orchard}.Public}$\n+- $\\mathsf{cv} ⦂ \\mathsf{ValueCommit^{Orchard}.Output}$\n+- $\\mathsf{nf_ {dom}} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}$\n+- $\\mathsf{dom} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}$\n+\n+### Auxiliary Inputs\n+\n+the prover knows an auxiliary input:\n+\n+- $\\mathsf{g_ d^{old}} ⦂ \\mathbb{P}^*$\n+- $\\mathsf{pk_ d^{old}} ⦂ \\mathbb{P}^*$\n+- $\\mathsf{v^{old}} ⦂ \\{ 0 .. 2^{\\ell_ {\\mathsf{value}}}-1 \\}$\n+- $\\text{ρ}^{\\mathsf{old}} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\text{ψ}^{\\mathsf{old}} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\mathsf{rcm^{old}} ⦂ \\{ 0 .. 2^{\\ell^{\\mathsf{Orchard}}_ {\\mathsf{scalar}}}-1 \\}$\n+- $\\mathsf{cm^{old}} ⦂ \\mathbb{P}$\n+- $\\mathsf{nf^{old}} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}$\n+- $\\mathsf{path^{cm}} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}^{[\\mathsf{MerkleDepth^{Orchard}}]}$\n+- $\\mathsf{pos^{cm}} ⦂ \\{ 0 .. 2^{\\mathsf{MerkleDepth^{Orchard}}}\\!-1 \\}$\n+- $\\mathsf{ak}^{\\mathbb{P}} ⦂ \\mathbb{P}^*$\n+- $\\mathsf{nk} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\mathsf{rivk} ⦂ \\mathsf{Commit^{ivk}.Trapdoor}$\n+- $\\mathsf{rivk\\_ {internal}} ⦂ \\mathsf{Commit^{ivk}.Trapdoor}$\n+- $\\alpha ⦂ \\{ 0 .. 2^{\\ell^{\\mathsf{Orchard}}_ {\\mathsf{scalar}}}-1 \\}$\n+- $\\mathsf{rcv} ⦂ \\{ 0 .. 2^{\\ell^{\\mathsf{Orchard}}_ {\\mathsf{scalar}}}-1 \\}$\n+- $\\mathsf{low} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\mathsf{width} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\mathsf{path^{excl}} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}^{[\\mathsf{MerkleDepth^{excl}}]}$\n+- $\\mathsf{pos^{excl}} ⦂ \\{ 0 .. 2^{\\mathsf{MerkleDepth^{excl}}}\\!-1 \\}$\n+\n+such that the following conditions hold:\n+\n+### Conditions\n+\n+**Note commitment integrity.** $\\hspace{0.5em}$\n+$\\mathsf{NoteCommit^{Orchard}_ {rcm^{old}}}(\\mathsf{repr}_ {\\mathbb{P}}(\\mathsf{g_ d^{old}}), \\mathsf{repr}_ {\\mathbb{P}}(\\mathsf{pk_ d^{old}}), \\mathsf{v^{old}}, \\text{ρ}^{\\mathsf{old}}, \\text{ψ}^{\\mathsf{old}}) \\in \\{ \\mathsf{cm^{old}}, \\bot \\}$.\n+\n+This is identical to the corresponding check in an Orchard Action\n+statement. [^protocol-actionstatement]\n+\n+**Merkle path validity for** $\\mathsf{cm^{old}}$. $\\hspace{0.5em}$\n+$(\\mathsf{path^{cm}}, \\mathsf{pos^{cm}})$ is a valid Merkle path of depth\n+$\\mathsf{MerkleDepth^{Orchard}}$, as defined in\n+§ 4.9 'Merkle Path Validity' [^protocol-merklepath], from\n+$\\mathsf{Extract}_ {\\mathbb{P}}(\\mathsf{cm^{old}})$ to the anchor $\\mathsf{rt^{cm}}$.\n+\n+**Value commitment integrity.** $\\hspace{0.5em}$\n+$\\mathsf{cv} = \\mathsf{ValueCommit^{Orchard}_ {rcv}}(\\mathsf{v^{old}})$.\n+\n+**Nullifier derivation.** $\\hspace{0.5em}$\n+$\\mathsf{nf^{old}} = \\mathsf{DeriveNullifier_ {nk}}(\\text{ρ}^{\\mathsf{old}}, \\text{ψ}^{\\mathsf{old}}, \\mathsf{cm^{old}})$.\n+\n+**Spend authority.** $\\hspace{0.5em}$\n+$\\mathsf{rk} = \\mathsf{SpendAuthSig^{Orchard}.RandomizePublic}(\\alpha, \\mathsf{ak}^{\\mathbb{P}})$.\n+\n+**Diversified address integrity.** $\\hspace{0.5em}$\n+Let\n+$\\mathsf{ivk} = \\mathsf{Commit^{ivk}_ {rivk}}(\\mathsf{Extract}_ {\\mathbb{P}}(\\mathsf{ak}^{\\mathbb{P}}), \\mathsf{nk})$\n+and\n+$\\mathsf{ivk\\_ {internal}} = \\mathsf{Commit^{ivk}_ {rivk\\_ {internal}}}(\\mathsf{Extract}_ {\\mathbb{P}}(\\mathsf{ak}^{\\mathbb{P}}), \\mathsf{nk})$.\n+Then $\\mathsf{pk_ d^{old}} = [\\mathsf{ivk}]\\, \\mathsf{g_ d^{old}}$ or\n+$\\mathsf{pk_ d^{old}} = [\\mathsf{ivk\\_ {internal}}]\\, \\mathsf{g_ d^{old}}$.\n+\n+**Nullifier non-membership.** $\\hspace{0.5em}$\n+Let $\\mathsf{leaf} = \\mathsf{GapCommit}(\\mathsf{low}, \\mathsf{width})$\n+(see [Hash Function]).\n+$(\\mathsf{path^{excl}}, \\mathsf{pos^{excl}})$ is a valid Merkle path of\n+depth $\\mathsf{MerkleDepth^{excl}}$ from $\\mathsf{leaf}$ to the anchor\n+$\\mathsf{rt^{excl}}$, using $\\mathsf{PoseidonNodeHash}$ (see [Hash Function])\n+at each level.\n+\n+Additionally, let $x = \\mathsf{nf^{old}} - \\mathsf{low} \\bmod q_ {\\mathbb{P}}$.\n+Both of the following range checks MUST hold:\n+\n+1. $x < 2^{250}$\n+2. $2^{250} - 1 - \\mathsf{width} + x < 2^{250}$\n+\n+These together enforce $\\mathsf{low} \\leq \\mathsf{nf^{old}} \\leq \\mathsf{low} + \\mathsf{width}$\n+over $\\mathbb{F}_ {q_ {\\mathbb{P}}}$, proving that the standard nullifier\n+falls within an interval of unrevealed values.\n+\n+<details>\n+<summary>\n+\n+### Derivation of the interval check\n+</summary>\n+\n+To prove $\\mathsf{low} \\leq \\mathsf{nf^{old}} \\leq \\mathsf{low} + \\mathsf{width}$:\n+\n+- Check 1 proves $\\mathsf{nf^{old}} \\geq \\mathsf{low}$: if\n+  $\\mathsf{nf^{old}} < \\mathsf{low}$, the field subtraction wraps to a\n+  value $\\geq q_ {\\mathbb{P}} - 2^{250} \\gg 2^{250}$, failing the range\n+  check. This relies on the sentinel initialization ensuring\n+  $\\mathsf{width} < 2^{250}$ and therefore all legitimate offsets are\n+  small.\n+\n+- Check 2 proves $\\mathsf{nf^{old}} \\leq \\mathsf{low} + \\mathsf{width}$:\n+  substituting, $2^{250} - 1 - \\mathsf{width} + x = 2^{250} - 1 - (\\mathsf{width} - x)$.\n+  This is in $[0, 2^{250})$ if and only if $\\mathsf{width} - x \\geq 0$,\n+  i.e., $x \\leq \\mathsf{width}$.\n+</details>\n+\n+**Alternate nullifier integrity.** $\\hspace{0.5em}$\n+$\\mathsf{nf_ {dom}} = \\mathsf{Poseidon}(\\mathsf{nk}, \\mathsf{dom}, \\mathsf{nf^{old}})$\n+(see [Alternate Nullifier Derivation]).\n+\n+### Circuit Implementation Notes\n+\n+The first six conditions (note commitment integrity through diversified\n+address integrity) are adapted from the corresponding Orchard Action\n+statement checks. In particular, note-commitment Merkle membership uses\n+$\\mathsf{Extract}_ {\\mathbb{P}}(\\mathsf{cm^{old}})$ as in the Orchard\n+Action statement. [^protocol-actionstatement] Implementations SHOULD share\n+circuit gadgets with an Orchard implementation to minimize new code\n+requiring review.\n+\n+The nullifier non-membership check requires a Poseidon-based Merkle path\n+verification (29 levels, 30 total Poseidon calls per note) and two 250-bit\n+range checks.\n+\n+The alternate nullifier integrity check is a single Poseidon hash\n+(two permutations at width 3).\n+\n+\n+## Spend Authorization Signature\n+\n+Each Claim proof MUST be accompanied by a spend authorization signature\n+$\\sigma$, which is a $\\mathsf{SpendAuthSig^{Orchard}}$ signature under\n+the randomized key $\\mathsf{rk}$ that is a public input to the circuit.\n+\n+The signature MUST be computed over a sighash that binds the signature\n+to the specific claim, ensuring that it cannot be replayed for a\n+different set of public inputs. Applications MAY achieve this binding\n+directly (by including the public inputs in the sighash) or indirectly\n+(through values that the Claim circuit constrains to be consistent with\n+the public inputs).\n+\n+The concrete sighash derivation is application-defined. The [Wallet Signing]\n+section specifies a PCZT-based signing flow that reuses the standard\n+ZIP 244 [^zip-244] sighash, enabling compatibility with existing hardware\n+wallets.\n+\n+\n+## Out-of-Circuit Verification\n+\n+A verifier that receives a Claim proof $\\pi$ together with a spend\n+authorization signature $\\sigma$ MUST perform the following checks:\n+\n+1. Verify $\\pi$ against the public inputs\n+   $(\\mathsf{rt^{cm}}, \\mathsf{rt^{excl}}, \\mathsf{rk}, \\mathsf{cv}, \\mathsf{nf_ {dom}}, \\mathsf{dom})$.\n+\n+2. Verify $\\sigma$ as a valid $\\mathsf{SpendAuthSig^{Orchard}}$ signature\n+   on the sighash (see [Spend Authorization Signature]), under the\n+   randomized key $\\mathsf{rk}$.\n+\n+3. Verify that $\\mathsf{rt^{cm}}$ corresponds to the Orchard note\n+   commitment tree root at the declared snapshot height.\n+\n+4. Verify that $\\mathsf{rt^{excl}}$ corresponds to the nullifier\n+   non-membership tree root at the declared snapshot height.\n+\n+5. Verify that $\\mathsf{dom}$ is valid for the current application\n+   instance.\n+\n+6. Verify that $\\mathsf{nf_ {dom}}$ has not been previously accepted in the\n+   same nullifier domain. If it has, reject the claim as a double-claim.\n+\n+\n+## Multi-Note Batching\n+\n+The Claim circuit defined in [Claim Circuit] proves a statement about a\n+single note. Applications that require proving aggregate balance across\n+multiple notes MAY use the following batching extension. Applications\n+that use the PCZT-based signing flow (see [Wallet Signing]) MUST use\n+the batched circuit, as the dummy signed note (see [Dummy Signed Note])\n+requires binding to the claimed note slots.\n+\n+### Batched Claim Circuit\n+\n+A batched Claim circuit for $N$ notes modifies the single-note circuit\n+as follows:\n+\n+- The public inputs $\\mathsf{rt^{cm}}$, $\\mathsf{rt^{excl}}$,\n+  $\\mathsf{rk}$, and $\\mathsf{dom}$ are shared across all $N$ notes.\n+- Each note $i$ contributes its own value commitment\n+  $\\mathsf{cv_ i}$ and alternate nullifier\n+  $\\mathsf{nf_ {dom,i}}$ as public inputs.\n+- A single $\\mathsf{ivk}$ (derived from the shared $\\mathsf{ak}$ and\n+  $\\mathsf{nk}$) is constrained to own all $N$ notes, ensuring they\n+  belong to the same holder.\n+- All $N$ note commitment integrity, value commitment integrity,\n+  Merkle path, nullifier derivation, non-membership, and alternate\n+  nullifier checks are performed independently per note.\n+\n+### Dummy Signed Note\n+\n+When the PCZT-based signing flow (see [Wallet Signing]) is used, the\n+batched circuit MUST include an additional slot for the dummy signed\n+note alongside the $N_{\\max}$ claimed note slots. This slot has the\n+following additional public input:\n+\n+- $\\mathsf{nf^{signed}} ⦂ \\{ 0 .. q_ {\\mathbb{P}}-1 \\}$\n+\n+and the following additional auxiliary inputs:\n+\n+- $\\mathsf{g_ d^{signed}} ⦂ \\mathbb{P}^*$\n+- $\\mathsf{pk_ d^{signed}} ⦂ \\mathbb{P}^*$\n+- $\\text{ρ}^{\\mathsf{signed}} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\text{ψ}^{\\mathsf{signed}} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\mathsf{rcm^{signed}} ⦂ \\{ 0 .. 2^{\\ell^{\\mathsf{Orchard}}_ {\\mathsf{scalar}}}-1 \\}$\n+- $\\mathsf{cm^{signed}} ⦂ \\mathbb{P}$\n+- $\\mathsf{app\\_commitment} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+- $\\mathsf{instance\\_id} ⦂ \\mathbb{F}_ {q_ {\\mathbb{P}}}$\n+\n+The following conditions MUST hold for the dummy signed note:\n+\n+**Signed note commitment integrity.** $\\hspace{0.5em}$\n+$\\mathsf{NoteCommit^{Orchard}_ {rcm^{signed}}}(\\mathsf{repr}_ {\\mathbb{P}}(\\mathsf{g_ d^{signed}}), \\mathsf{repr}_ {\\mathbb{P}}(\\mathsf{pk_ d^{signed}}), 1, \\text{ρ}^{\\mathsf{signed}}, \\text{ψ}^{\\mathsf{signed}}) \\in \\{ \\mathsf{cm^{signed}}, \\bot \\}$.\n+\n+The value is fixed to 1 (zatoshi) to match the PCZT value used for\n+hardware wallet display (see [Wallet Signing]). The dummy signed note's\n+value does not contribute to the claimed balance.\n+\n+**Signed note nullifier derivation.** $\\hspace{0.5em}$\n+$\\mathsf{nf^{signed}} = \\mathsf{DeriveNullifier_ {nk}}(\\text{ρ}^{\\mathsf{signed}}, \\text{ψ}^{\\mathsf{signed}}, \\mathsf{cm^{signed}})$.\n+\n+**Rho binding.** $\\hspace{0.5em}$\n+$\\text{ρ}^{\\mathsf{signed}} = \\mathsf{Poseidon}\\bigl(\\mathsf{cmx_ 1}, \\ldots, \\mathsf{cmx_ {N_ {\\max}}}, \\mathsf{app\\_commitment}, \\mathsf{instance\\_id}\\bigr)$\n+\n+where $\\mathsf{cmx_ i} = \\mathsf{Extract}_ {\\mathbb{P}}(\\mathsf{cm^{old}_ i})$\n+is the extracted note commitment of the $i$-th claimed note slot\n+(real or dummy). This binds the hardware wallet's signature to the\n+exact set of notes being claimed and the application context.\n+\n+**Spend authority.** $\\hspace{0.5em}$\n+$\\mathsf{rk} = \\mathsf{SpendAuthSig^{Orchard}.RandomizePublic}(\\alpha, \\mathsf{ak}^{\\mathbb{P}})$.\n+\n+This is the same $\\mathsf{rk}$ shared across the $N_{\\max}$ claimed\n+note slots.\n+\n+**Diversified address integrity.** $\\hspace{0.5em}$\n+$\\mathsf{pk_ d^{signed}} = [\\mathsf{ivk}]\\, \\mathsf{g_ d^{signed}}$\n+where $\\mathsf{ivk}$ is derived from the shared\n+$\\mathsf{ak}^{\\mathbb{P}}$ and $\\mathsf{nk}$ as in the claimed note\n+conditions.\n+\n+### Note Padding\n+\n+To avoid leaking the number of notes a holder is claiming, the circuit\n+SHOULD accept a fixed number of note slots $N_ {\\max}$ (for example,\n+$N_ {\\max} = 5$). Unused slots are filled with dummy notes (Orchard\n+notes with $\\mathsf{v^{old}} = 0$).\n+\n+Dummy notes use the same Merkle path validity check as the standard\n+Orchard Action circuit [^protocol-actionstatement]: if\n+$\\mathsf{v^{old}} = 0$, the Merkle path check is skipped.\n+\n+All other conditions are enforced unconditionally:\n+\n+- Dummy notes commit to value 0 with a fresh $\\mathsf{rcv}$.\n+- Dummy notes are derived from the same full viewing key as real notes\n+  (using a distinct diversifier index per slot).\n+- Dummy notes MUST provide a valid non-membership proof against\n+  $\\mathsf{rt^{excl}}$. Because dummy nullifiers are random field\n+  elements, they fall within an unrevealed interval with overwhelming\n+  probability.\n+- The alternate nullifier is derived and published for every slot. The\n+  application verifier MAY ignore alternate nullifiers corresponding\n+  to zero-value slots.\n+\n+\n+## Sub-Circuit Instantiation\n+\n+When the Claim circuit is used as a sub-circuit of an application-specific\n+circuit, the application circuit has direct access to the private witnesses\n+including the note value $\\mathsf{v^{old}}$. In this case, the value\n+commitment $\\mathsf{cv}$ MAY be omitted from the public inputs if the\n+application circuit binds to the note values through its own mechanism.\n+For example, an application circuit that converts note values to\n+application-specific units (such as vote weight) may bind to\n+$\\mathsf{v^{old}}$ directly within the circuit and expose the converted\n+result through its own public outputs. All other conditions of the Claim\n+circuit MUST still be enforced.\n+\n+\n+## Application Requirements\n+\n+An application instantiating this ZIP MUST define the following:\n+\n+- An **application hotkey**: a key pair generated locally by the wallet,\n+  freshly generated per proof-of-balance instance, and not linked to the\n+  holder's on-chain Orchard addresses. When the PCZT-based signing flow\n+  is used, the output is addressed to this key (see [Wallet Signing]).\n+  All subsequent application-level operations use the hotkey in place of\n+  the holder's spending key.\n+- An **application verifier**: the component that accepts or rejects\n+  claims by performing the checks in [Out-of-Circuit Verification]. It\n+  MUST maintain a persistent set of accepted alternate nullifiers per\n+  domain for double-claim detection.\n+- A **nullifier domain** derivation, as specified in [Nullifier Domains].\n+- An **application commitment** ($\\mathsf{app\\_commitment}$): a value\n+  that commits to application-specific context, included in the rho\n+  binding (see [Dummy Signed Note]). For example, a voting protocol\n+  might commit to the delegated vote weight and recipient address.\n+- An **instance identifier** ($\\mathsf{instance\\_id}$): a unique\n+  identifier for the proof-of-balance instance, included in the rho\n+  binding (see [Dummy Signed Note]).\n+- A **sighash derivation**: the method for computing the sighash for the\n+  spend authorization signature (see [Spend Authorization Signature]).\n+  Applications using the PCZT-based signing flow use the ZIP 244\n+  [^zip-244] sighash.\n+\n+\n+## Wallet Signing\n+\n+The signing flow proceeds in five steps:\n+\n+1. The wallet generates an application hotkey on the local device.\n+2. The wallet constructs a dummy signed note whose nullifier commits\n+   to the delegation context.\n+3. The wallet builds a PCZT containing the dummy signed note.\n+4. The hardware wallet device signs the PCZT and returns the signature.\n+5. The wallet extracts the signature and assembles the delegation\n+   submission.\n+\n+Each interaction with the hardware wallet device delegates up to\n+$N_{\\max}$ Orchard notes (we utilize the default batch size of 5).\n+A holder with more than $N_{\\max}$ notes repeats the flow for each batch,\n+producing a separate application commitment per batch. The holder MAY choose\n+to delegate fewer batches than their full note set, claiming only the balance\n+covered by the delegated batches.\n+\n+### Dummy Signed Note Construction\n+\n+The wallet constructs a dummy Orchard note as follows:\n+\n+1. **Address.** The signed note's address is derived from the holder's\n+   full viewing key at diversifier index 0 with external scope:\n+   $\\mathsf{addr}^{\\mathsf{signed}} = \\mathsf{fvk.address\\_at}(0, \\mathsf{External})$.\n+   A fixed index is used because the address is a private witness inside\n+   the ZKP circuit and is never publicly transmitted.\n+\n+2. **Nullifier commitment.** The signed note's $\\text{ρ}$ is set to\n+   the value specified by the rho binding condition in [Dummy Signed Note].\n+   Because the dummy note has no on-chain existence, $\\text{ρ}$ can be\n+   freely chosen; this design binds the hardware wallet's signature to\n+   the exact set of notes being claimed and the application context via\n+   the ZKP circuit, avoiding the need for a custom signing protocol.\n+\n+3. **Value.** The note value MUST be set to 1 zatoshi (0.00000001 ZEC)\n+   in the PCZT, matching the value used in the signed note commitment\n+   integrity condition (see [Dummy Signed Note]).\n+\n+4. **Rseed.** A fresh random $\\mathsf{rseed}$ is sampled for the note.\n+\n+5. **Note construction.** The note is constructed via standard Orchard\n+   note construction using the address, value (1 zatoshi), rho, and\n+   rseed above.\n+\n+For fewer than $N_{\\max}$ real notes, the wallet pads the remaining\n+slots with zero-value dummy notes at diversifier indices $1000 + i$\n+(external scope) from the holder's full viewing key. These padding notes\n+enter the $\\text{ρ}$ commitment via their $\\mathsf{cmx}$ values but do\n+not correspond to real on-chain notes.\n+\n+### PCZT Construction\n+\n+The wallet constructs a PCZT as a single-action Orchard transaction:\n+\n+#### Spend Side\n+\n+- The single spend consumes the dummy signed note constructed above.\n+- A dummy Merkle authentication path (all-zero siblings, position 0)\n+  is used. The path is not verified on-chain; spend authorization is\n+  established by the ZKP circuit.\n+- The Orchard bundle builder generates the spend authorization\n+  randomizer $\\alpha$ and the randomized verification key\n+  $\\mathsf{rk} = \\mathsf{ak} + [\\alpha]\\, G$ internally.\n+\n+#### Output Side\n+\n+- A single output of 1 zatoshi is addressed to the application hotkey.\n+- The output memo SHOULD contain a human-readable delegation description.\n+  For example, a voting application might use:\n+\n+  `\"I am authorizing this hotkey managed by my wallet to participate`\n+  `in {instance_name} with {amount}.{frac} ZEC.\"`\n+\n+  where `{instance_name}` identifies the proof-of-balance instance and\n+  `{amount}.{frac}` is the holder's eligible ZEC balance.\n+\n+#### ZIP-32 Derivation\n+\n+The spend MUST include a ZIP 32 [^zip-32] derivation path so that the\n+hardware wallet device can derive the correct spending key:\n+\n+$$\\mathsf{path} = [32', \\mathsf{coin\\_type}', \\mathsf{account}']$$\n+\n+where $\\mathsf{coin\\_type}$ is 133 for Zcash mainnet.\n+\n+#### Finalization\n+\n+The wallet applies the PCZT Creator and IoFinalizer roles. The\n+IoFinalizer computes the ZIP 244 [^zip-244] transaction identifier\n+(sighash) that the hardware wallet device will sign.\n+\n+### Device Display\n+\n+During signing, the hardware wallet device displays the PCZT as a\n+standard Orchard transaction. The user sees fields such as:\n+\n+    Amount: 0.00000001 ZEC\n+    Fee: 0 ZEC\n+\n+    Orchard\n+    From\n+    #1 0.00000001 ZEC Mine\n+    <shielded>\n+\n+    To\n+    #1 0.00000001 ZEC\n+    {application hotkey address}\n+    Memo: I am authorizing this hotkey managed by my\n+          wallet to participate in {instance_name} with\n+          {amount}.{frac} ZEC\n+\n+The 0.00000001 ZEC amount (1 zatoshi) and 0 ZEC fee confirm that no\n+real funds are being transferred. The \"To\" address matches the\n+application hotkey address displayed in the wallet application. The memo\n+provides human-readable context for what the user is authorizing.\n+\n+The hardware wallet device has no awareness of application semantics. It\n+interprets the PCZT identically to any other Orchard transaction.\n+\n+### Signature Extraction and Submission\n+\n+After receiving the signed PCZT from the hardware wallet device, the\n+wallet:\n+\n+1. Parses the signed PCZT and reads the `spend_auth_sig` field from\n+   the relevant action.\n+\n+2. Recomputes the sighash that the device signed. This is the ZIP 244\n+   transaction identifier computed over the PCZT.\n+\n+3. Assembles the delegation submission by combining the hardware wallet\n+   signature and sighash with the Claim proof and other public inputs.\n+   The submission format is application-defined.\n+\n+The delegation submission is sent to the application verifier. The\n+wallet does not need the hardware wallet device again for the remainder\n+of the usage instance; all subsequent operations use the application\n+hotkey.\n+\n+# Rationale\n+\n+## Why Alternate Nullifiers Instead of Actual Spends\n+\n+The most obvious approach to proof-of-balance (actually spending the\n+notes to oneself at the snapshot anchor) fails because it consumes the\n+notes on-chain. A holder who participates in one application cannot reuse\n+the same notes in a concurrent application. Alternate nullifiers avoid\n+this by operating entirely off the main chain: the standard nullifiers are\n+never revealed, and the notes remain spendable.\n+\n+## Why Sentinel Initialization\n+\n+Without sentinels, the initial non-membership tree would contain a single\n+leaf spanning the entire field $[0, q_ {\\mathbb{P}}-1]$, with width\n+$\\approx 2^{254}$. The in-circuit range checks use 250-bit windows\n+(25 limbs of the 10-bit lookup table). An interval wider than $2^{250}$\n+would allow the upper-bound range check to wrap, enabling a prover to\n+falsely claim non-membership for a value outside the interval.\n+\n+Sentinels partition the field into intervals each narrower than $2^{250}$\n+at initialization. Real nullifier insertions only split intervals into\n+smaller ones, so the width bound is maintained permanently.\n+\n+## Why 1 Zatoshi Instead of 0\n+\n+The dummy signed note uses a value of 1 zatoshi (0.00000001 ZEC) in the\n+PCZT rather than 0. When an Orchard Action has a 0-value\n+note, Keystone's suppress the display of transaction fields\n+(amount, fee, addresses, memo), presenting the user with insufficient\n+information to make an informed signing decision. Setting the value to\n+1 zatoshi causes the device to render all fields normally.\n+\n+The Batched Claim circuit uses the 1-zatoshi value in the signed note\n+commitment integrity check (see [Dummy Signed Note]), matching the\n+PCZT value. The dummy signed note's value does not contribute to the\n+claimed balance.\n+\n+## Why a Dummy Note Instead of a Real Note\n+\n+A holder's real Orchard notes are not spent or consumed during\n+delegation. The dummy signed note is constructed specifically for the\n+proof-of-balance flow and never appears in any on-chain note commitment\n+tree. This design has three advantages:\n+\n+- **No fund risk.** Because the proof-of-balance transaction is never\n+  broadcast to the Zcash mainchain and the signed note has no on-chain\n+  existence, there is no scenario in which the delegation signing could\n+  result in loss of funds.\n+- **Note reusability.** Because real notes are never consumed on\n+  mainchain, they remain fully spendable and available for other\n+  applications that use the proof-of-balance mechanism. Alternate\n+  nullifiers are domain-separated by $\\mathsf{instance\\_id}$, so the\n+  same notes can participate in concurrent proof-of-balance instances\n+  without conflict.\n+- **PCZT compatibility.** The dummy note reuses the standard Orchard\n+  Action structure, allowing the PCZT to pass through the\n+  hardware wallet's existing PCZT parser and signer without\n+  modification.\n+\n+## Why the Dummy Note's ρ Provides Non-Replayability\n+\n+Because the dummy signed note does not correspond to any on-chain note,\n+its inputs can be freely chosen. The design sets\n+$\\text{ρ}^{\\mathsf{signed}}$ to a Poseidon hash of the delegated note\n+commitments, the application commitment, and the usage identifier.\n+Because $\\text{ρ}$ enters the note commitment (and therefore the\n+sighash), the hardware wallet's signature is cryptographically bound to\n+the exact delegation context. An attacker cannot replay the signature\n+for a different set of notes, a different application commitment, or a\n+different usage instance.\n+\n+This binding is enforced by the Batched Claim circuit's rho binding\n+condition (see [Dummy Signed Note]), which the application verifier\n+checks. The hardware wallet device does not need to understand\n+the binding; it is sufficient that the device signs the sighash derived\n+from the PCZT containing the committed $\\text{ρ}$. No custom signing\n+protocol is required — the standard Orchard PCZT flow is reused, and\n+all existing hardware wallets that support Orchard signing are\n+compatible without firmware changes.\n+\n+## Why ZIP 244 Sighash\n+\n+The PCZT uses the standard ZIP 244 [^zip-244] transaction\n+identifier as the sighash. This is the only sighash format that\n+existing hardware wallet Orchard signing implementations can produce.\n+Using an application-specific sighash would require firmware changes,\n+which this specification explicitly avoids.\n+\n+The ZIP 244 sighash commits to the full Orchard bundle structure\n+(including the dummy note's commitment, which embeds the bound\n+$\\text{ρ}$), providing the necessary cryptographic binding without\n+a custom signature scheme.\n+\n+## Why Not a Custom Signing Protocol\n+\n+A future version of this specification could define a custom signing\n+protocol purpose-built for proof-of-balance, rather than reusing the\n+standard Orchard PCZT format. This would require ecosystem-wide\n+coordination (firmware updates to all participating hardware wallets)\n+but could yield several improvements:\n+\n+- **Binding application actions to the signature.** Many use cases want\n+  to prove balance *and* take an action in a single step (e.g., casting\n+  a vote). A custom signing protocol could commit to the application\n+  action directly in the signature, reducing the number of ZKPs required\n+  by downstream protocols.\n+- **Removing the hotkey indirection.** With a richer signing format, the\n+  holder could authorize actions directly from the hardware wallet\n+  without delegating to an application hotkey, simplifying the trust\n+  model.\n+- **Improved on-device display.** A custom format could present\n+  application-aware context (e.g., \"Vote on proposal X with Y ZEC\")\n+  rather than the generic Orchard transaction fields shown today.\n+\n+This specification intentionally avoids a custom signing protocol to\n+maximize compatibility with existing wallets. The trade-off is accepted:\n+downstream protocols may require additional ZKPs or hotkey indirection\n+that a purpose-built signing format could eliminate.\n+\n+\n+# Deployment\n+\n+This ZIP does not specify a consensus change. Deployment considerations\n+are application-specific.\n+\n+The hardware wallet signing flow specified in [Wallet Signing]\n+requires no firmware changes and no changes to Zcash mainchain consensus\n+rules. Any hardware wallet that supports Orchard PCZT signing can\n+participate immediately.\n+\n+Software wallets that hold the spending key directly do not need the\n+PCZT construction; they follow the same Claim proof specification but\n+sign the application-defined sighash directly.\n+\n+\n+# Reference implementation\n+\n+A reference implementation of the Claim circuit (including the\n+non-membership tree, alternate nullifier derivation, and multi-note\n+batching for $N_ {\\max} = 5$) is available at\n+[valargroup/voting-circuits](https://github.com/valargroup/voting-circuits).\n+\n+\n+# Open issues\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later](protocol/protocol.pdf)\n+\n+[^protocol-orchardcommitmenttree]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.1: Note Commitment Trees](protocol/protocol.pdf#merkletree)\n+\n+[^protocol-actionstatement]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.17.4: Action Statement (Orchard)](protocol/protocol.pdf#actionstatement)\n+\n+[^protocol-merklepath]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.9: Merkle Path Validity](protocol/protocol.pdf#merklepath)\n+\n+[^protocol-sinsemilla]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.1.9: Sinsemilla Hash Function](protocol/protocol.pdf#concretesinsemillahash)\n+\n+[^protocol-poseidon]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.2: Pseudo Random Functions](protocol/protocol.pdf#concreteprfs)\n+\n+[^poseidon]: [Poseidon: A New Hash Function for Zero-Knowledge Proof Systems](https://eprint.iacr.org/2019/458)\n+\n+[^pir-nullifier-exclusion]: [Draft ZIP: Private Information Retrieval for Nullifier Exclusion Proofs](https://github.com/zcash/zips/pull/1198)\n+\n+[^draft-str4d-orchard-balance-proof]: [Draft ZIP: Air drops, Proof-of-Balance, and Stake-weighted Polling](draft-str4d-orchard-balance-proof.md)\n+\n+[^voting-protocol]: [Shielded Voting Protocol](draft-valargroup-shielded-voting.md)\n+\n+[^zip-244]: [ZIP 244: Transaction Identifier Non-Malleability](zip-0244.rst)\n+\n+[^zip-32]: [ZIP 32: Shielded Hierarchical Deterministic Wallets](zip-0032.rst)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-valargroup-orchard-balance-proof.md","title":"Orchard Proof-of-Balance","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"pool-snapshot","text":"Pool Snapshot"},{"level":2,"id":"nullifier-non-membership-tree","text":"Nullifier Non-Membership Tree"},{"level":3,"id":"construction","text":"Construction"},{"level":3,"id":"rationale-for-low-width-encoding","text":"Rationale for (low, width) encoding"},{"level":3,"id":"hash-function","text":"Hash Function"},{"level":3,"id":"rationale-for-poseidon-over-sinsemilla","text":"Rationale for Poseidon over Sinsemilla"},{"level":3,"id":"tree-depth","text":"Tree Depth"},{"level":3,"id":"sentinel-initialization","text":"Sentinel Initialization"},{"level":2,"id":"nullifier-domains","text":"Nullifier Domains"},{"level":2,"id":"alternate-nullifier-derivation","text":"Alternate Nullifier Derivation"},{"level":3,"id":"properties","text":"Properties"},{"level":3,"id":"security-argument","text":"Security Argument"},{"level":3,"id":"rationale-for-divergence-from-the-orchard-native-construction","text":"Rationale for divergence from the Orchard-native construction"},{"level":2,"id":"claim-circuit","text":"Claim Circuit"},{"level":3,"id":"public-inputs","text":"Public Inputs"},{"level":3,"id":"auxiliary-inputs","text":"Auxiliary Inputs"},{"level":3,"id":"conditions","text":"Conditions"},{"level":3,"id":"derivation-of-the-interval-check","text":"Derivation of the interval check"},{"level":3,"id":"circuit-implementation-notes","text":"Circuit Implementation Notes"},{"level":2,"id":"spend-authorization-signature","text":"Spend Authorization Signature"},{"level":2,"id":"out-of-circuit-verification","text":"Out-of-Circuit Verification"},{"level":2,"id":"multi-note-batching","text":"Multi-Note Batching"},{"level":3,"id":"batched-claim-circuit","text":"Batched Claim Circuit"},{"level":3,"id":"dummy-signed-note","text":"Dummy Signed Note"},{"level":3,"id":"note-padding","text":"Note Padding"},{"level":2,"id":"sub-circuit-instantiation","text":"Sub-Circuit Instantiation"},{"level":2,"id":"application-requirements","text":"Application Requirements"},{"level":2,"id":"wallet-signing","text":"Wallet Signing"},{"level":3,"id":"dummy-signed-note-construction","text":"Dummy Signed Note Construction"},{"level":3,"id":"pczt-construction","text":"PCZT Construction"},{"level":4,"id":"spend-side","text":"Spend Side"},{"level":4,"id":"output-side","text":"Output Side"},{"level":4,"id":"zip-32-derivation","text":"ZIP-32 Derivation"},{"level":4,"id":"finalization","text":"Finalization"},{"level":3,"id":"device-display","text":"Device Display"},{"level":3,"id":"signature-extraction-and-submission","text":"Signature Extraction and Submission"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"why-alternate-nullifiers-instead-of-actual-spends","text":"Why Alternate Nullifiers Instead of Actual Spends"},{"level":2,"id":"why-sentinel-initialization","text":"Why Sentinel Initialization"},{"level":2,"id":"why-1-zatoshi-instead-of-0","text":"Why 1 Zatoshi Instead of 0"},{"level":2,"id":"why-a-dummy-note-instead-of-a-real-note","text":"Why a Dummy Note Instead of a Real Note"},{"level":2,"id":"why-the-dummy-notes-ρ-provides-non-replayability","text":"Why the Dummy Note's ρ Provides Non-Replayability"},{"level":2,"id":"why-zip-244-sighash","text":"Why ZIP 244 Sighash"},{"level":2,"id":"why-not-a-custom-signing-protocol","text":"Why Not a Custom Signing Protocol"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1209,"title":"[ZIP-231]  Add Newsletter, Reply-to and Application Layer Use Cases ","author":"pacu","createdAt":"2026-03-09T19:51:46Z","updatedAt":"2026-03-31T21:49:25Z","url":"https://github.com/zcash/zips/pull/1209","diffUrl":"https://github.com/zcash/zips/pull/1209.diff","headRef":"zip-231-motivation-addenda","headRepo":"zcash/zips","headSha":"733416e9c0d3f0fdfd6f026a4364a61ce35f732d","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This addition was discussed during https://github.com/zcash/lcwg/pull/157 The idea of this addition is to include two clear use cases where Memo Bundles will contribute to both Zcash UX and security. This continues work carried out in https://github.com/zcash/zips/pull/1185 because the base branch w","zipRefs":[231],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0231.md","status":"modified","additions":58,"deletions":1,"patch":"@@ -75,7 +75,7 @@ hides the sender(s) (that is, the addresses corresponding to the keys used to\n spend the input notes) from all of the recipients. For certain kinds of\n transactions, it is desirable to make one or more sender addresses available to\n one or more recipients (for example, a reply address). In such circumstances it\n-is important to authenticate the sender address(es), to give the recipient a\n+is important to authenticate the sender addresses, to give the recipient a\n guarantee that the address is controlled by a sender of the transaction;\n failure to authenticate this address can enable phishing attacks. These\n Authenticated Reply Addresses require zero-knowledge proofs, and for the\n@@ -119,6 +119,61 @@ recipient. By separating memo data from the decryption capability for those\n memos, it admits a greater variety of applications that utilize memo data,\n while decreasing the amount of data that needs to be stored on-chain overall.\n \n+# Use Cases\n+\n+## Authenticated Reply-to addresses\n+In a peer-to-peer transaction Bob and Alice can split a lunch check. \n+Bob can send a shielded memo to Alice that says \"Nice Lunch! It was fun! this \n+is my half + tips. Hugs, Bob.\". Bob would show Alice his transaction detail in \n+his wallet and Alice can visibly check hers and verify that the memo contained \n+in that transaction was authored by Bob. That in-person manual verification \n+process is only valid for that one transaction. Anyone that knows Alice's address\n+and the fact that she exchanged messages over Zcash memos with Bob could spoof a\n+shielded memo impersonating Bob. Neither Alice or her wallet have an aid to judge \n+the authorship of any of the received messages. \n+\n+If Bob's wallet had the capability to prove the authorship of a message, Alice's\n+wallet could help her notice such a spoofing attempt. The rationale of this use case \n+is that application developers can use this mechanism to build a user experience\n+that can help users tell apart trustworthy memos from those whose origin cannot \n+be certainly determined. \n+\n+It has to be noted that providing a mechanism for wallet applications to attach \n+data that can help prove the origin of a transaction through the memo field will\n+not prevent that spoofers attempt such attacks. Spoofers continue to send memos \n+to attempt to deceive a user. The proving data is not self-sufficient to determine \n+that the author of a shielded memo shall be trusted. Further verification \n+steps would be needed to determine a such a level of trust. This specification \n+enables wallet developers to build a user experience that helps Zcash wallet\n+users to identify authentic memos containing Reply-to addresses from spoofed ones.\n+The definition of such UX is outside of the scope of this ZIP.\n+\n+## Memos as input for application layer\n+Using unverifiable memo data as input for business logic is unadvisable. \n+Repeatedly, application developers have discussed memos an on-chain data layer for \n+privacy-minded software such as Instant (or ansynchronous) messaging applications\n+or marketplaces. These applications require that the memo information can\n+be _trusted_ and therefore be treated as _actionable_ data by the application\n+business logic. Without such a mechanism the application does not have a way to \n+choose which messages should be trusted and which ones shall be ignored. The \n+ability to attach proofs to memos provides application developers with mechanisms\n+to distinguish plausible user intent from arbitrary memos generated outside the \n+application domain.\n+\n+## Broadcasting a newsletter\n+The ZecHub DAO runs a \"Shielded Newsletter\" service [^shielded-newsletter]. Users subscribe by sending a\n+one-time transaction with a subscription fee and a shielded memo that tells the \n+service where to send the newsletter to. As discussed in the motivation section, \n+Memo bundles will help the newsletter service to avoid chain bloat by enabling \n+subscribers to share a single key to decrypt a single memo instead of sending \n+individual memos to each one of them. Additionally, by requiring subscribers to \n+prove the authorship of their subscription memos, the newsletter can verify that\n+the recipient address indicated in the one-time subscription memo is indeed the\n+author of the subscription and not a subscription bot attempting to perform a pseudospoofing\n+attack against the service. Finally, the newsletter service can require one-time\n+unsubscribe messages to attach a proof-of-authorship that proves that the sender\n+owns spend authority over the address that the unsubcription is being requested\n+for.\n \n # Privacy Implications\n \n@@ -816,3 +871,5 @@ TBD\n [^pczt]: [zcash/zips issue #693: Standardize a protocol for creating shielded transactions offline](https://github.com/zcash/zips/issues/693)\n \n [^rfc-8439]: [RFC 8439: ChaCha20 and Poly1305 for IETF Protocols](https://www.rfc-editor.org/rfc/rfc8439.html)\n+\n+[^shielded-newsletter]: [ZecHub Shielded Newsletter](https://zechub.wiki/newsletter)\n\\ No newline at end of file"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0231.md","title":"Memo Bundles","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"use-cases","text":"Use Cases"},{"level":2,"id":"authenticated-reply-to-addresses","text":"Authenticated Reply-to addresses"},{"level":2,"id":"memos-as-input-for-application-layer","text":"Memos as input for application layer"},{"level":2,"id":"broadcasting-a-newsletter","text":"Broadcasting a newsletter"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"memo-bundle","text":"Memo bundle"},{"level":2,"id":"memo-encryption","text":"Memo encryption"},{"level":3,"id":"memo-bundle-padding","text":"Memo bundle padding"},{"level":2,"id":"memo-decryption","text":"Memo decryption"},{"level":2,"id":"encoding-in-transactions","text":"Encoding in transactions"},{"level":2,"id":"transaction-sighash","text":"Transaction sighash"},{"level":2,"id":"changes-to-zip-317-14","text":"Changes to ZIP 317 14"},{"level":2,"id":"network-protocol","text":"Network protocol"},{"level":2,"id":"changes-to-the-zcash-protocol-specification","text":"Changes to the Zcash Protocol Specification"},{"level":2,"id":"applicability","text":"Applicability"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":2,"id":"interaction-with-zip-302-10","text":"Interaction with ZIP 302 10"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"memo-bundle-size-restriction","text":"Memo bundle size restriction"},{"level":2,"id":"independent-fee-policy-for-memo-data","text":"Independent fee policy for memo data"},{"level":2,"id":"memo-chunk-size","text":"Memo chunk size"},{"level":2,"id":"memo-key-size","text":"Memo key size"},{"level":2,"id":"encryption-format","text":"Encryption format"},{"level":2,"id":"domain-separation-of-note-ciphertexts","text":"Domain separation of note ciphertexts"},{"level":2,"id":"pruned-encoding","text":"Pruned encoding"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"alternatives","text":"Alternatives"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":638,"title":"ZIP 302: Structured memos","author":"str4d","createdAt":"2022-10-06T18:24:23Z","updatedAt":"2026-03-31T21:39:09Z","url":"https://github.com/zcash/zips/pull/638","diffUrl":"https://github.com/zcash/zips/pull/638.diff","headRef":"zip302-structured-memos","headRepo":"zcash/zips","headSha":"82fe03dd9bc95d7d6e1939e490eaa251c6d52531","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #1208. Closes COR-1074.","zipRefs":[302],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0302.rst","status":"modified","additions":106,"deletions":1,"patch":"@@ -48,12 +48,15 @@ contents of a memo. It does not define consensus requirements.\n + If the first byte has a value of ``0xF6``, and the remaining 511 bytes are ``0x00``,\n   then the user supplied no memo, and the encrypted memo field is to be treated as empty.\n \n++ If the first byte has a value of ``0xF7``, then the reader MUST interpret the memo as a\n+  structured memo; see the next section for its format.\n+\n + If the memo matches any of these patterns, then this memo is from the future, because\n   these ranges are reserved for future updates to this specification:\n \n      + The first byte has a value of ``0xF6``, and the remaining 511 bytes are not all\n        ``0x00``.\n-     + The first byte has a value between ``0xF7`` and ``0xFE`` inclusive.\n+     + The first byte has a value between ``0xF8`` and ``0xFE`` inclusive.\n \n + If the first byte has a value of ``0xFF`` then the reader should not make any other\n   assumption about the memo. In order to put arbitrary data into a memo field (that\n@@ -64,6 +67,88 @@ contents of a memo. It does not define consensus requirements.\n   assumption about the memo. This value was used ambiguously in the past by private\n   agreement; applications SHOULD prefer ``0xFF`` which is unambiguously for this purpose.\n \n+Structured memos\n+----------------\n+\n+A memo can be used to store structured data by setting its first byte to a value of\n+``0xF7``. The remaining bytes contain a set of \"memo parts\", encoded as concatenated\n+Type-Version-Length-Value tuples, followed by fixed padding (if any memo bytes remain\n+after the TVLV tuples).\n+\n+Multiple memo parts with the same type are not permitted in structured memos.\n+\n+Parsing a structured memo\n+`````````````````````````\n+\n+After the leading ``0xF7`` byte, readers parse the remaining memo bytes as follows:\n+\n+- While there are remaining unparsed memo bytes:\n+\n+  - Parse a ``compactSize`` to obtain ``partType``. If there are insufficient bytes to\n+    parse a ``compactSize``, return an error.\n+  - If ``partType == 0``:\n+\n+    - This is a marker type indicating that there are no more TVLV tuples. If any of the\n+      remaining memo bytes are not ``0x00``, return an error.\n+\n+  - Else if ``partType`` has already been parsed in an earlier memo part, return an error.\n+  - Otherwise:\n+\n+    - Parse a ``compactSize`` to obtain ``partVersion``. If there are insufficient bytes\n+      to parse a ``compactSize``, return an error.\n+    - Parse a ``compactSize`` to obtain ``partLength``. If there are insufficient bytes to\n+      parse a ``compactSize``, return an error.\n+    - Read the next ``partLength`` bytes to obtain ``partValue``. If ``partLength`` would\n+      require the reader to read past the end of the memo bytes, return an error.\n+    - Store ``(partType, partVersion, partValue)`` and continue parsing.\n+\n+If an error occurs, readers MUST treat the entire memo as invalid (ignoring any\n+``MemoPart`` that was successfully parsed prior to the error).\n+\n+Defined memo part types\n+```````````````````````\n+\n+The following memo part types (and corresponding versioned value encodings) are defined:\n+\n++--------------+-----------------+-------------------------------------------------------------------------------------+\n+| ``partType`` | ``partVersion`` | ``partValue`` encoding                                                              |\n++==============+=================+=====================================================================================+\n+| 160          | 0               | A UTF-8 string.                                                                     |\n+|              |                 | If decoding fails then return an error; readers MUST NOT replace invalid sequences. |\n++--------------+-----------------+-------------------------------------------------------------------------------------+\n+| 255          | 0               | Unconstrained data, for private use (like ``0xFF`` outside structured memos).       |\n+|              |                 | Readers should not make any assumptions about the encoding or content of this data. |\n++--------------+-----------------+-------------------------------------------------------------------------------------+\n+| 10000-19999  | Currently unassigned range for \"assigned private use\".                                                |\n++--------------+-----------------+-------------------------------------------------------------------------------------+\n+| 65530-65535  | For temporary experimentation.                                                                        |\n++--------------+-----------------+-------------------------------------------------------------------------------------+\n+\n+All other ``partType`` integers (and ``partVersion`` integers for memo part types\n+specified in this ZIP) are reserved for future updates to this ZIP.\n+\n+Adding new types\n+~~~~~~~~~~~~~~~~\n+\n+It is intended that new memo part types SHOULD be introduced by a modification to this\n+ZIP, either directly or via a change section of a new ZIP, in accordance with the ZIP\n+Process [#zip-0000]_.\n+\n+``partType`` integers in the range 10000 to 19999 inclusive are assigned by the ZIP\n+Editors for \"assigned private use\". They are intended to support long-term usage of memo\n+part types without public ``partValue`` encoding specifications (especially in deployment\n+environments where the \"unconstrained data\" memo part type is unsuitable due to the chance\n+of colliding usage). To request one or more memo part types for assigned private use, ask\n+the ZIP Editors directly or open an issue on the ZIPs repo. Requestors MUST NOT assign\n+specific integers themselves, or assume that a particular subrange is unused.\n+\n+For experimentation prior to proposing a new memo part type in a ZIP, experimental memo\n+parts MAY be added using the reserved ``partTypes`` 65530 to 65535 inclusive. This\n+provides for six simultaneous experiments, which can be referred to as experiments A to F.\n+This should be sufficient because experiments are expected to be reasonably short-term,\n+and should otherwise be either standardized in a ZIP (and allocated a ``partType`` outside\n+this reserved range) or discontinued.\n+\n Rationale\n =========\n \n@@ -91,6 +176,24 @@ See issue `#1849`_ for further discussion.\n \n .. _`#1849`: https://github.com/zcash/zcash/issues/1849\n \n+``partVersion`` at the top level of structured memos\n+----------------------------------------------------\n+\n+It is intentional that only one memo part of each type be present in a memo; any container\n+formats should be represented within ``partValue``. However, it is also desirable that the\n+``partValue`` encodings can be updated over time as changes are made to the wallet-level\n+protocols that use them. Without an explicit version field, the only alternative would be\n+to assign a new ``partType`` for the new version, at which point it would be possible for\n+a memo to contain two versions of the same semantic data. By having an explicit version\n+field, wallets can forward-compatibly distinguish between a new version of a memo part\n+type they are currently handling, and a new memo part type they don't know about.\n+\n+A prior argument against explicit version bytes at the top level was that memo space is\n+limited to 511 bytes, and requiring every memo part type to have a version byte would add\n+25-50% overhead on top of the TLV header. However, once ZIP 231 Memo Bundles [#zip-0231]_\n+are deployed, a single memo can be at most 16384 bytes in a v6 transaction, weakening this\n+argument.\n+\n Backwards Compatibility\n =======================\n \n@@ -103,3 +206,5 @@ References\n .. [#protocol] `Zcash Protocol Specification, Version 2021.1.19 <protocol/protocol.pdf>`_\n .. [#UTF-8] `UTF-8, a transformation format of ISO 10646 <https://www.rfc-editor.org/rfc/rfc3629.html>`_\n .. [#Bitcoin-CompactSize] `Variable length integer. Bitcoin Wiki <https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer>`_\n+.. [#zip-0000] `ZIP 0: ZIP Process <zip-0000.rst>`_\n+.. [#zip-0231] `ZIP 231: Memo Bundles <zip-0231.md>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0302.rst","title":"Standardized Memo Field Format","status":"Draft","headings":[{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"structured-memos","text":"Structured memos"},{"level":3,"id":"parsing-a-structured-memo","text":"Parsing a structured memo"},{"level":3,"id":"defined-memo-part-types","text":"Defined memo part types"},{"level":4,"id":"adding-new-types","text":"Adding new types"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"partversion-at-the-top-level-of-structured-memos","text":"partVersion at the top level of structured memos"},{"level":1,"id":"backwards-compatibility","text":"Backwards Compatibility"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1176,"title":"[ZIP TBD]: Draft for historical wallet key gen/derivation schemes","author":"dorianvp","createdAt":"2026-02-08T20:21:21Z","updatedAt":"2026-03-30T14:17:41Z","url":"https://github.com/zcash/zips/pull/1176","diffUrl":"https://github.com/zcash/zips/pull/1176.diff","headRef":"key-derivation","headRepo":"dorianvp/zips","headSha":"3393c754e8db788c5c6262aec997bd94a4e7208d","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Closes #1175.","zipRefs":[],"draftRefs":["draft-key-derivation-schemes"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -230,6 +230,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-deploy-nu7</td> <td class=\"left\"><a href=\"zips/draft-arya-deploy-nu7.md\">Deployment of the NU7 Network Upgrade</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/839\">zips#839</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/???\">zips#???</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-key-derivation-schemes</td> <td class=\"left\"><a href=\"zips/draft-key-derivation-schemes.md\">Legacy Wallet Key-Derivation Behaviors for Fund Recovery</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"></td>\n   </table></embed>\n "},{"filename":"zips/draft-key-derivation-schemes.md","status":"added","additions":134,"deletions":0,"patch":"@@ -0,0 +1,134 @@\n+```\n+ZIP: TBD\n+Title: Legacy Wallet Key-Derivation Behaviors for Fund Recovery\n+Owners: Darío Paz <dorianvp@zingolabs.org>\n+        TBD\n+Credits: Kris Nuttycombe\n+Status: Draft\n+Category: Informational\n+Created: 2026-02-08\n+License: MIT\n+Pull-Request: <https://github.com/zcash/zips/pull/1176>\n+```\n+\n+# Terminology\n+\n+This document uses terminology from the Zcash Protocol Specification and ZIP 32 [^ZIP32]. In particular:\n+\n+`account index`: The ZIP 32 account index used to derive account-level spending keys.\n+\n+`diversifier` / `diversified address`: A change in receiver encoding that allows generating many addresses from the same Sapling spending key (rather than generating new keys).\n+\n+`legacy derivation`: Any historical wallet key generation/derivation behavior that differs from current best practice (e.g., BIP 39 seed phrases [^BIP-0039] + ZIP 32 HD derivation, and diversified addresses).\n+\n+# Abstract\n+\n+Over Zcash’s history, wallets have used multiple strategies to generate and derive keys. This ZIP documents select legacy behaviors that are relevant to fund recovery and wallet interoperability, with an initial focus on behaviors used by zcashd and Zecwallet Lite.\n+\n+# Motivation\n+\n+Users may possess seed material, wallet files, or partial backups produced by older wallets. If modern recovery tooling assumes today’s conventions (BIP 39 mnemonic → seed → ZIP 32 derivation, plus diversified addresses), it may fail to detect funds created under older schemes.\n+\n+Documenting legacy behaviors helps implementers:\n+\n+- Identify which recovery strategies to attempt given a wallet’s provenance.\n+- Avoid repeating past mistakes (e.g., generating new accounts when diversified addresses should be used).\n+- Build interoperable tooling that can recover funds without requiring the original wallet software.\n+\n+# Specification\n+\n+This ZIP is informational: it does not change consensus rules. It records historical wallet behaviors and recovery-relevant implications.\n+\n+# Wallet behaviors\n+\n+## `zcashd`\n+\n+### Summary\n+\n+Historically, `zcashd` generated keys from system randomness.\n+\n+Starting in `zcashd` v4.7.0, the wallet introduced an “emergency recovery phrase” and began deriving all new addresses from the HD seed derived from that phrase using HD wallet functionality described in ZIP 32 and ZIP 316.\n+\n+### Seed material\n+\n+For Sapling, `zcashd` implemented HD derivation according to ZIP 32, but used a seed generated directly from system randomness, rather than seed material derived from a BIP 39 mnemonic phrase.\n+\n+In `zcashd` v4.7.0, the wallet added support for deriving the wallet’s HD seed from a mnemonic phrase (BIP 39) and referred to the HD seed derived from the emergency recovery phrase as the wallet’s “mnemonic seed”.\n+\n+### Derivation / address generation\n+\n+Sapling keys are derived using ZIP 32.\n+\n+After v4.7.0, new addresses are derived from the mnemonic seed using ZIP 32 and ZIP 316.\n+\n+For legacy RPC behavior such as `z_getnewaddress`, `zcashd` derives Sapling addresses under a high account index `account = 0x7FFFFFFF` to avoid collision with Sapling addresses for account 0.\n+\n+### Recovery implications\n+\n+Recovery tooling may need to support Sapling HD derivation from wallet-internal seed material that is not reproducible from a BIP 39 mnemonic.\n+\n+Additionally, recovery tooling that imports the zcashd emergency recovery phrase into other wallets may fail to discover funds received to Sapling addresses generated via `z_getnewaddress`, unless it also scans the legacy account 0x7FFFFFFF.\n+\n+### Notes / TODOs\n+\n+- Add references.\n+- Document the v4.7.0+ migration flow in more detail (how/when the emergency recovery phrase is generated, confirmation requirement, export mechanism via `z_exportwallet`, `zcashd-wallet-tool` behavior).\n+- Specify the exact derivation path structure used for the legacy account and any gap-limit expectations for recovery.\n+\n+## Zecwallet Lite\n+\n+### Summary\n+\n+Zecwallet Lite incorrectly generated “new Sapling addresses” by deriving fresh accounts (incrementing ZIP 32 account index) when it should have generated diversified addresses from the same Sapling spending key.\n+\n+### Seed material\n+\n+(TODO: document whether mnemonic/seed originates from BIP 39 and how it was stored/encoded.)\n+\n+### Derivation / address generation\n+\n+Observed behavior:\n+\n+**Expected**: same account spending key → many diversified addresses\n+\n+**Actual**: new account index → new spending key → address\n+\n+### Recovery implications\n+\n+Funds may exist on addresses derived from multiple sequential account indices. Recovery tooling may need to scan across a range of account indices (a “gap limit” policy) even when a user believes they used “one account”.\n+\n+### Notes / TODOs\n+\n+- Identify affected versions/commits.\n+- Specify exact rule: when does ZWL increment account index, and by how much?\n+- Clarify relationship between transparent vs shielded indexes (if applicable).\n+\n+## `zcash_client_sqlite`\n+\n+TBD\n+\n+# Security and Privacy Considerations\n+\n+This ZIP documents behaviors that may expand the search space required for recovery (e.g., scanning multiple account indices). Implementations should:\n+\n+- Avoid leaking derived addresses unnecessarily (e.g., through network queries).\n+- Provide user-visible controls for scan bounds (e.g., maximum account index / gap limit).\n+- Treat recovered seed material and derived keys as highly sensitive.\n+\n+# Reference Implementation\n+\n+None.\n+\n+# Backwards Compatibility\n+\n+Not applicable (informational).\n+\n+# Acknowledgements\n+\n+This ZIP was motivated by issue #1175 opened by Kris Nuttycombe.\n+\n+# References\n+\n+[^ZIP32]: [ZIP-32: Shielded Hierarchical Deterministic Wallets](zip-0032.rst)\n+\n+[^BIP-0039]: [BIP 39: Mnemonic code for generating deterministic keys](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-key-derivation-schemes.md","title":"[ZIP TBD]: Draft for historical wallet key gen/derivation schemes","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":1,"id":"wallet-behaviors","text":"Wallet behaviors"},{"level":2,"id":"zcashd","text":"zcashd"},{"level":3,"id":"summary","text":"Summary"},{"level":3,"id":"seed-material","text":"Seed material"},{"level":3,"id":"derivation--address-generation","text":"Derivation / address generation"},{"level":3,"id":"recovery-implications","text":"Recovery implications"},{"level":3,"id":"notes--todos","text":"Notes / TODOs"},{"level":2,"id":"zecwallet-lite","text":"Zecwallet Lite"},{"level":3,"id":"summary-1","text":"Summary"},{"level":3,"id":"seed-material-1","text":"Seed material"},{"level":3,"id":"derivation--address-generation-1","text":"Derivation / address generation"},{"level":3,"id":"recovery-implications-1","text":"Recovery implications"},{"level":3,"id":"notes--todos-1","text":"Notes / TODOs"},{"level":2,"id":"zcash_client_sqlite","text":"zcash_client_sqlite"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"backwards-compatibility","text":"Backwards Compatibility"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1189,"title":"Podmanize","author":"zancas","createdAt":"2026-02-20T23:18:30Z","updatedAt":"2026-03-29T22:39:46Z","url":"https://github.com/zcash/zips/pull/1189","diffUrl":"https://github.com/zcash/zips/pull/1189.diff","headRef":"podmanize","headRepo":"zingolabs/zips","headSha":"f9845a63d09d1dd8d3e99a47b334710427fd530e","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Fixes: #1188 This more invasive change prefers \"podman\" to \"docker\" and \"Containerfile\" to \"Dockerfile\" wherever possible. `--userns=keep-id` is used in all `podman run` invocations.","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":".github/actions/render-protocol/action.yml","status":"modified","additions":1,"deletions":1,"patch":"@@ -3,5 +3,5 @@ description: GitHub Action to compile ZIPs and also Zcash Protocol Specification\n author: Deirdre Connolly and Daira-Emma Hopwood\n runs:\n   using: docker\n-  # Runs the default `make all-docker`.\n+  # Runs the default `make all-podman`.\n   image: ../../../Dockerfile"},{"filename":"Containerfile","status":"added","additions":37,"deletions":0,"patch":"@@ -0,0 +1,37 @@\n+FROM debian:bullseye\n+\n+RUN apt-get update\n+RUN apt-get install -y \\\n+        perl \\\n+        sed \\\n+        git \\\n+        cmake \\\n+        python3 \\\n+        python3-pip \\\n+        pandoc \\\n+        biber \\\n+        latexmk \\\n+        texlive \\\n+        texlive-science \\\n+        texlive-fonts-extra \\\n+        texlive-plain-generic \\\n+        texlive-bibtex-extra\n+\n+RUN rm -f /usr/lib/python3.11/EXTERNALLY-MANAGED\n+RUN pip install 'docutils==0.21.2' 'rst2html5==2.0.1'\n+\n+# Use a fork so that we're running pinned code. The Makefile for\n+# MultiMarkdown-6 expects the `master` branch to exist for delta computation,\n+# so we also add that branch locally, even though it's otherwise unused.\n+RUN git clone -b develop https://github.com/Electric-Coin-Company/MultiMarkdown-6 && \\\n+  cd MultiMarkdown-6 && \\\n+  git branch master origin/master && \\\n+  make release && cd build && make && make install\n+\n+ENV PATH=${PATH}:/root/.local/bin\n+\n+WORKDIR \"/zips\"\n+\n+# By default this will run \"make all-podman\", but passing an argument will override the make target.\n+ENTRYPOINT [\"make\"]\n+CMD [\"all-podman\"]"},{"filename":"Dockerfile","status":"removed","additions":0,"deletions":37,"patch":"@@ -1,37 +0,0 @@\n-FROM debian:bullseye\n-\n-RUN apt-get update\n-RUN apt-get install -y \\\n-        perl \\\n-        sed \\\n-        git \\\n-        cmake \\\n-        python3 \\\n-        python3-pip \\\n-        pandoc \\\n-        biber \\\n-        latexmk \\\n-        texlive \\\n-        texlive-science \\\n-        texlive-fonts-extra \\\n-        texlive-plain-generic \\\n-        texlive-bibtex-extra\n-\n-RUN rm -f /usr/lib/python3.11/EXTERNALLY-MANAGED\n-RUN pip install 'docutils==0.21.2' 'rst2html5==2.0.1'\n-\n-# Use a fork so that we're running pinned code. The Makefile for\n-# MultiMarkdown-6 expects the `master` branch to exist for delta computation,\n-# so we also add that branch locally, even though it's otherwise unused.\n-RUN git clone -b develop https://github.com/Electric-Coin-Company/MultiMarkdown-6 && \\\n-  cd MultiMarkdown-6 && \\\n-  git branch master origin/master && \\\n-  make release && cd build && make && make install\n-\n-ENV PATH=${PATH}:/root/.local/bin\n-\n-WORKDIR \"/zips\"\n-\n-# By default this will run \"make all-docker\", but passing an argument will override the make target.\n-ENTRYPOINT [\"make\"]\n-CMD [\"all-docker\"]"},{"filename":"Dockerfile","status":"added","additions":1,"deletions":0,"patch":"@@ -0,0 +1 @@\n+Containerfile\n\\ No newline at end of file"},{"filename":"Makefile","status":"modified","additions":2,"deletions":2,"patch":"@@ -2,7 +2,7 @@\n \n MARKDOWN_OPTION?=--mmd\n \n-.PHONY: all-zips all-docker all tag-release protocol all-protocol discard\n+.PHONY: all-zips all-podman all tag-release protocol all-protocol discard\n all-zips: .Makefile.uptodate\n \techo \"$(patsubst zips/%,%,$(sort $(wildcard zips/zip-*.rst) $(wildcard zips/zip-*.md)))\" >.zipfilelist.new\n \tdiff .zipfilelist.current .zipfilelist.new || cp -f .zipfilelist.new .zipfilelist.current\n@@ -15,7 +15,7 @@ all-zips: .Makefile.uptodate\n \t$(MAKE) README.rst\n \t$(MAKE) rendered/index.html $(addprefix rendered/,$(addsuffix .html,$(basename $(patsubst zips/%,%,$(sort $(wildcard zips/*.rst) $(wildcard zips/*.md))))))\n \n-all-docker:\n+all-podman:\n \tgit config --global --add safe.directory \"$(shell pwd)\"\n \t$(MAKE) all\n "},{"filename":"README.rst","status":"modified","additions":21,"deletions":0,"patch":"@@ -43,6 +43,25 @@ and double-check the generated ``rendered/draft-*.html`` file before filing a Pu\n See `here <protocol/README.rst>`__ for the project dependencies.\n \n \n+Building with Podman\n+~~~~~~~~~~~~~~~~~~~~\n+\n+To build the rendered HTML without installing dependencies locally, use\n+`Podman <https://podman.io/>`__ with the included ``Containerfile``:\n+\n+.. code::\n+\n+   podman build -t zcash-zips-render .\n+   podman run --rm --userns=keep-id -v \"$(pwd):/zips\" zcash-zips-render\n+\n+The ``--userns=keep-id`` flag ensures that rendered files are owned by your\n+user rather than root. To render only the ZIPs (skipping protocol PDFs):\n+\n+.. code::\n+\n+   podman run --rm --userns=keep-id -v \"$(pwd):/zips\" zcash-zips-render all-zips\n+\n+\n Settled Mainnet Network Upgrade\n -------------------------------\n \n@@ -212,6 +231,7 @@ written.\n     <tr> <td>2005</td> <td class=\"left\"><a href=\"zips/zip-2005.md\">Quantum Recoverability</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1135\">zips#1135</a></td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n+    <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n   </table></embed>\n \n Drafts without assigned ZIP numbers\n@@ -384,4 +404,5 @@ Index of ZIPs\n     <tr> <td>2005</td> <td class=\"left\"><a href=\"zips/zip-2005.md\">Quantum Recoverability</a></td> <td>Draft</td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td>\n+    <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Something Short and To the Point}</a></td> <td>Draft</td>\n   </table></embed>"},{"filename":"README.template","status":"modified","additions":19,"deletions":0,"patch":"@@ -43,6 +43,25 @@ and double-check the generated ``rendered/draft-*.html`` file before filing a Pu\n See `here <protocol/README.rst>`__ for the project dependencies.\n \n \n+Building with Podman\n+~~~~~~~~~~~~~~~~~~~~\n+\n+To build the rendered HTML without installing dependencies locally, use\n+`Podman <https://podman.io/>`__ with the included ``Containerfile``:\n+\n+.. code::\n+\n+   podman build -t zcash-zips-render .\n+   podman run --rm --userns=keep-id -v \"$(pwd):/zips\" zcash-zips-render\n+\n+The ``--userns=keep-id`` flag ensures that rendered files are owned by your\n+user rather than root. To render only the ZIPs (skipping protocol PDFs):\n+\n+.. code::\n+\n+   podman run --rm --userns=keep-id -v \"$(pwd):/zips\" zcash-zips-render all-zips\n+\n+\n Settled Mainnet Network Upgrade\n -------------------------------\n "},{"filename":"render-via-docker.sh","status":"modified","additions":2,"deletions":2,"patch":"@@ -3,5 +3,5 @@ set -efuxo pipefail\n \n TAG='zcash-zips-render'\n \n-docker build -t \"$TAG\" .\n-docker run -v \"$(pwd):/zips\" \"$TAG\"\n+podman build -t \"$TAG\" .\n+podman run --rm --userns=keep-id -v \"$(pwd):/zips\" \"$TAG\""}],"filesTruncated":false,"preview":null},{"number":1162,"title":"[ZIP 272]: Zcash Consensus Accounts","author":"str4d","createdAt":"2026-01-23T21:44:05Z","updatedAt":"2026-03-24T21:17:49Z","url":"https://github.com/zcash/zips/pull/1162","diffUrl":"https://github.com/zcash/zips/pull/1162.diff","headRef":"consensus-accounts","headRepo":"zcash/zips","headSha":"225f1975f1de2102549c720aa468903fc0b6c6fd","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[272],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0272.md","status":"added","additions":693,"deletions":0,"patch":"@@ -0,0 +1,693 @@\n+    ZIP: 272\n+    Title: Zcash Consensus Accounts\n+    Owners: Kris Nuttycombe <kris@nutty.land>\n+            Jack Grigg <thestr4d@gmail.com>\n+            Daira-Emma Hopwood <daira@jacaranda.org>\n+    Status: Draft\n+    Category: Consensus\n+    Created: 2026-01-11\n+    License: MIT\n+    Discussions-To: <https://github.com/zcash/zips/issues/1165>\n+    Pull-Request: <https://github.com/zcash/zips/pull/1162>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", and \"MAY\" in this document are to be\n+interpreted as described in BCP 14 [^BCP14] when, and only when, they appear in\n+all capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in § 3.12\n+'Mainnet and Testnet'. [^protocol-networks]\n+\n+The term \"full validator\" in this document is to be interpreted as defined in\n+§ 3.3 'The Block Chain'. [^protocol-blockchain].\n+\n+The terms below are to be interpreted as follows:\n+\n+output\n+\n+: A sink of funds within the transaction that is controlled by a payment\n+  address. This excludes e.g. miner fees, or the ZIP 233 field.\n+\n+\n+# Abstract\n+\n+This ZIP introduces a system of transparent consensus accounts for receiving\n+newly-issued ZEC, with spending from each account being controlled by a ZIP 270\n+[^zip-0270] signing key. Consensus accounts supplement (but do not replace)\n+shielded coinbase outputs. These accounts can be funded either by new ZEC\n+issuance, or (in the future) by ZSA issuance transactions for other assets.\n+This ZIP eliminates the coinbase maturity rule while retaining the property\n+that all ZEC and ZSA value must enter the shielded pool(s) before it can be\n+otherwise transferred (or, in the case of ZEC, deshielded.)\n+\n+\n+# Motivation\n+\n+The Zcash development fund lockbox currently has the characteristics of a\n+transparent, account-based system. Spending from the lockbox requires the\n+introduction of a new transaction bundle that treats the lockbox as a source of\n+transaction inputs, controlled by a key that may be rotated using the mechanism\n+specified in ZIP 270 [^zip-0270]. Instead of creating a mechanism that is\n+specific to the development fund lockbox, creating a general mechanism provides\n+several benefits to consistency and usability.\n+\n+Part of the rationale for the creation of the dev fund lockbox was that the\n+process of shielding tens of thousands of development fund outputs (over a\n+thousand outputs per day) has created excessive operational friction for\n+key-holder entities; in addition, transactions that spend these outputs have\n+significant fee costs under ZIP 317 [^zip-0317]. These disadvantages do not\n+uniquely effect the key holders of the development fund, however; they are felt\n+just as acutely by miners and Zcash Community Grants. By introducing an\n+account-based system, all recipients of new issuance can gain the same benefits\n+that are available to the keyholders of the coinholder-controlled fund\n+introduced in ZIP 1016 [^zip-1016].\n+\n+In addition to eliminating hassles related to UTXO management, introducing a\n+transparent account mechanism makes it possible for individuals to make direct\n+donations to the development fund lockbox.\n+\n+While this ZIP focuses on ZEC issuance, the transaction format reserves space\n+for asset identifiers to allow future network upgrades to extend consensus\n+accounts to hold Zcash Shielded Assets (ZSAs) [^zip-0227]. Such an extension\n+could allow a ZSA issuer to finalize ZSA issuance into a consensus account\n+without immediately sending the entirety of the issuance to shielded\n+recipients; this would enable the creation of fixed-supply ZSA assets with\n+publicly-visible undistributed balance, without requiring the issuer to expose\n+their full viewing key that would reveal the recipients of subsequent transfers\n+from that finalized issuance pool.\n+\n+\n+# Privacy Implications\n+\n+At present, all ZEC issuance is transparent, in that mining outputs are either\n+sent to the lockbox, sent to a transparent address, or sent to a shielded\n+address but with the output verified to be decryptable with the all-zeroes OVK,\n+to ensure that the value of each issuance output can be verified. This ZIP\n+preserves this transparency: consensus account balances are publicly visible,\n+and shielded coinbase outputs remain subject to the all-zeroes OVK requirement.\n+\n+All spends from a consensus account are required to be shielding transactions,\n+preserving the existing property that all issued funds are initially shielded.\n+\n+\n+# Requirements\n+\n+- All new ZEC issuance is credited to a transparent consensus account.\n+- Newly-issued ZEC is credited either to a hardcoded funding stream key (until\n+  block height 4476000 as specified in [^zip-0214], this will be either the\n+  coinholder-controlled-fund key or a to-be-defined Zcash Community Grants fund\n+  key) or to a miner-controlled key.\n+- When a miner produces a coinbase transaction, they MAY include an account\n+  registration bundle and consensus account output bundle to credit their\n+  rewards to a consensus account. Alternatively, they MAY continue to use\n+  shielded coinbase outputs.\n+- Preserve the existing requirement that newly-issued ZEC is always shielded at\n+  least once.\n+- All funding streams defined after the activation of this ZIP must use\n+  consensus accounts as recipients.\n+\n+\n+# Non-requirements\n+\n+- This does not need to define a general-purpose transparent accounts\n+  mechanism.\n+\n+\n+# High-level summary\n+\n+This section and the tables below are non-normative.\n+\n+Prior to this ZIP, coinbase and non-coinbase transactions supported several\n+different kinds of inputs and outputs. Some of these were explicitly encoded in\n+the transaction (namely, spending or receiving UTXOs), while some were\n+implicitly defined by the consensus rules (e.g. fees). The full set of possible\n+actions were as follows:\n+\n+<table>\n+    <tr>\n+        <th>Transaction type</th>\n+        <td colspan=2>Coinbase</td>\n+        <td colspan=2>Non-coinbase</td>\n+    </tr>\n+    <tr>\n+        <th>Encoding</th>\n+        <td>Implicit rule</td>\n+        <td>Explicit UTXO</td>\n+        <td>Explicit rule</td>\n+        <td>Explicit UTXO</td>\n+    </tr>\n+    <tr>\n+        <th>Direction: Input</th>\n+        <td>\n+            <ul>\n+                <li>Block subsidy</li>\n+                <li>Fees</li>\n+                <li>CCF disbursement</li>\n+            </ul>\n+        </td>\n+        <td>N/A</td>\n+        <td>N/A</td>\n+        <td>\n+            <ul>\n+                <li>Spending funds</li>\n+                <li>Miner shielding</li>\n+            </ul>\n+        </td>\n+    </tr>\n+    <tr>\n+        <th>Direction: Output</th>\n+        <td>\n+            <ul>\n+                <li>CCF funding</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>Transparent coinbase</li>\n+                <li>Shielded coinbase</li>\n+                <li>Funding streams</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>NSM (ZIP 233)</li>\n+                <li>Fees (ZIP 2002)</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>Receiving funds</li>\n+            </ul>\n+        </td>\n+    </tr>\n+</table>\n+\n+After the reworking of transactions in this ZIP, the following actions are\n+supported:\n+\n+<table>\n+    <tr>\n+        <th>Transaction type</th>\n+        <td colspan=4>Coinbase</td>\n+        <td colspan=3>Non-coinbase</td>\n+    </tr>\n+    <tr>\n+        <th>Encoding</th>\n+        <td>Implicit rule</td>\n+        <td>Explicit rule</td>\n+        <td>Explicit account</td>\n+        <td>Explicit UTXO</td>\n+        <td>Explicit rule</td>\n+        <td>Explicit account</td>\n+        <td>Explicit UTXO</td>\n+    </tr>\n+    <tr>\n+        <th>Direction: Input</th>\n+        <td>\n+            <ul>\n+                <li>Block subsidy</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>Fees (ZIP NNN)</li>\n+            </ul>\n+        </td>\n+        <td>N/A</td>\n+        <td>N/A</td>\n+        <td>N/A</td>\n+        <td>\n+            <ul>\n+                <li>CCF or ZCG disbursement</li>\n+                <li>Miner shielding</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>Spending funds</li>\n+            </ul>\n+        </td>\n+    </tr>\n+    <tr>\n+        <th>Direction: Output</th>\n+        <td>\n+            <ul>\n+                <li>CCF funding (account increment)</li>\n+                <li>Funding streams (account increment)</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>NSM (ZIP 235)</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>Transparent coinbase</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>Shielded coinbase</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>NSM (ZIP 233)</li>\n+                <li>Fees (ZIP 2002)</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>CCF or ZCG donations</li>\n+            </ul>\n+        </td>\n+        <td>\n+            <ul>\n+                <li>Receiving funds</li>\n+            </ul>\n+        </td>\n+    </tr>\n+</table>\n+\n+Shielded coinbase outputs remain supported, as shown in the \"Explicit UTXO\"\n+column above. While the address receiving a shielded coinbase output is\n+publicly visible, the subsequent spend of that output is not distinguishable\n+from other shielded spends on-chain. This provides better privacy properties\n+than the transparent account model, and avoids requiring privacy-conscious\n+miners to create a new account key for each block they mine (which would\n+rapidly bloat the consensus account set).\n+\n+\n+# Specification\n+\n+## Consensus accounts\n+\n+A consensus account is uniquely identified by a `consensus_account_id`, which\n+is a 32-byte value derived from the account's initial public key:\n+\n+```\n+consensus_account_id = BLAKE2b-256(\"Zcash_ConAcctId_\", protocol_type || initial_public_key)\n+```\n+\n+where:\n+- `protocol_type` is the single-byte protocol type identifier as defined in\n+  ZIP 270 [^zip-0270] (0 for RedPallas, 1 for BIP340, etc.)\n+- `initial_public_key` is the public key provided when the account was first\n+  registered\n+\n+The `consensus_account_id` remains stable across key rotations performed via\n+ZIP 270.\n+\n+Each consensus account has a ZIP 270 tracked signing key associated with it,\n+that authorizes the spending of funds from the account. The tracked signing key\n+MAY be any protocol type supported by ZIP 270.\n+\n+## Global account state\n+\n+Consensus accounts require the following additions to the global state:\n+\n+- A map, `consensus_accounts`, from `consensus_account_id` to a tuple of:\n+    - `balance`: the current ZEC balance\n+    - `current_key`: the current signing key (protocol_type, public_key)\n+    - `rotation_key`: the current rotation key (protocol_type, public_key)\n+\n+For newly registered accounts, `current_key` and `rotation_key` are both set to\n+the initial key provided at registration.\n+\n+As of the activation of this ZIP, consensus accounts hold only ZEC. The\n+transaction format reserves space for asset identifiers to allow future network\n+upgrades to extend consensus accounts to hold other assets.\n+\n+## Initializing an account\n+\n+There are two ways to initialize a consensus account:\n+\n+- Define it in the consensus rules.\n+    - The `consensus_account_id`s of these accounts are \"visible\" to the\n+      consensus rules, and can be used in implicit rules.\n+- Create a miner account by mining a block that includes an account\n+  registration bundle with a previously-unknown key.\n+    - The `consensus_account_id`s of these accounts are \"opaque\" to the\n+      consensus rules, and their balance can only be modified by being\n+      explicitly referenced in a transaction.\n+\n+## Account registration bundle\n+\n+This ZIP defines a new bundle type for the extensible transaction format\n+specified in ZIP NNN [^zip-extfmt]. The account registration bundle registers\n+new consensus accounts.\n+\n+### Account Registration Effecting Data\n+\n+| Bytes   | Name              | Data Type                              | Description |\n+|---------|-------------------|----------------------------------------|-------------|\n+| varies  | `nRegistrations`  | `compactSize`                          | The number of account registrations. |\n+| varies  | `vRegistrations`  | `AccountRegistration[nRegistrations]`  | New account registrations. |\n+\n+### AccountRegistration\n+\n+| Bytes   | Name           | Data Type       | Description |\n+|---------|----------------|-----------------|-------------|\n+| 1       | `protocolType` | `uint8`         | Signing protocol type as defined in ZIP 270 (0=RedPallas, 1=BIP340, etc.). |\n+| varies  | `publicKey`    | `byte[keylen]`  | Public key. Length is determined by `protocolType`. |\n+\n+Upon processing an `AccountRegistration`, full validators:\n+1. Compute the `consensus_account_id` from `protocolType` and `publicKey` as\n+   specified above.\n+2. MUST reject the transaction if an account with that `consensus_account_id`\n+   already exists.\n+3. Create a new account entry in `consensus_accounts` with the computed ID, the\n+   provided key as both `current_key` and `rotation_key`, and `balance` set to\n+   zero.\n+\n+### Account Registration Authorizing Data\n+\n+| Bytes   | Name                  | Data Type                            | Description |\n+|---------|-----------------------|--------------------------------------|-------------|\n+| varies  | `vRegistrationAuths`  | `RegistrationAuth[nRegistrations]`   | Authorization signatures for account registrations. |\n+\n+### RegistrationAuth\n+\n+| Bytes   | Name        | Data Type      | Description |\n+|---------|-------------|----------------|-------------|\n+| varies  | `signature` | `byte[siglen]` | Signature over the transaction id, proving control of the corresponding private key. Length is determined by the `protocolType` of the corresponding `AccountRegistration`. |\n+\n+The `vRegistrationAuths` entries correspond positionally to `vRegistrations`\n+entries. Each signature MUST verify against the `publicKey` in the\n+corresponding `AccountRegistration`.\n+\n+### Value Balance\n+\n+The account registration bundle does not transfer value; it has no entry in the\n+value balance map.\n+\n+### Consensus Rules\n+\n+In a coinbase transaction, if an account registration bundle is present, a\n+consensus account output bundle MUST also be present, and for each registered\n+account there MUST be at least one `AccountOutput` crediting value to that\n+account's `consensus_account_id`.\n+\n+## Consensus account output bundle\n+\n+This ZIP defines a second bundle type for the extensible transaction format.\n+The consensus account output bundle credits value to consensus accounts.\n+\n+### Consensus Account Output Effecting Data\n+\n+| Bytes   | Name        | Data Type                    | Description |\n+|---------|-------------|------------------------------|-------------|\n+| varies  | `nOutputs`  | `compactSize`                | The number of account outputs. |\n+| varies  | `vOutputs`  | `AccountOutput[nOutputs]`    | The account output records. |\n+\n+### AccountOutput\n+\n+| Bytes   | Name         | Data Type                | Description |\n+|---------|--------------|--------------------------|-------------|\n+| 32      | `accountId`  | `byte[32]`               | The `consensus_account_id` of the account to credit. |\n+| 1       | `assetClass` | `uint8`                  | Asset class identifier. 0x00 for ZEC, 0x01 for other assets. |\n+| 0 or 64 | `assetId`    | `byte[0]` or `byte[64]`  | If `assetClass == 0`, the zero-length byte array; otherwise a 64-byte asset identifier. |\n+| 8       | `value`      | `uint64`                 | The value to credit to the account. |\n+\n+Upon processing an `AccountOutput`, full validators:\n+1. MUST reject the transaction if no account with the given `accountId` exists\n+   (note: registrations in the same transaction are processed first).\n+2. Add `value` to the account's `balance`.\n+\n+### Consensus Account Output Authorizing Data\n+\n+The consensus account output bundle has no authorizing data. Any party may\n+credit value to an existing account (analogous to sending funds to an address).\n+\n+### Value Balance\n+\n+The value balance map entry for the consensus account output bundle equals the\n+sum of all output values:\n+\n+```\n+valueBalance = Σ(output.value for each output in vOutputs)\n+```\n+\n+### Consensus Rules\n+\n+As of the activation of this ZIP, full validators MUST reject transactions\n+where any `AccountOutput` has `assetClass` other than 0x00. Support for\n+crediting other assets to consensus accounts may be added in a future network\n+upgrade.\n+\n+## Consensus account spend bundle\n+\n+This ZIP defines a third bundle type for the extensible transaction format: the\n+consensus account spend bundle. This bundle allows spending value from\n+consensus accounts into shielded pools.\n+\n+### Consensus Account Spend Effecting Data\n+\n+| Bytes   | Name           | Data Type                        | Description |\n+|---------|----------------|----------------------------------|-------------|\n+| varies  | `nSpends`      | `compactSize`                    | The number of account spends. |\n+| varies  | `vSpends`      | `AccountSpend[nSpends]`          | The account spend descriptions. |\n+\n+### AccountSpend\n+\n+| Bytes   | Name         | Data Type                | Description |\n+|---------|--------------|--------------------------|-------------|\n+| 32      | `accountId`  | `byte[32]`               | The `consensus_account_id` of the account to spend from. |\n+| 1       | `assetClass` | `uint8`                  | Asset class identifier. 0x00 for ZEC, 0x01 for other assets. |\n+| 0 or 64 | `assetId`    | `byte[0]` or `byte[64]`  | If `assetClass == 0`, the zero-length byte array; otherwise a 64-byte asset identifier. |\n+| 8       | `value`      | `uint64`                 | The value to spend from the account. |\n+\n+### Consensus Account Spend Authorizing Data\n+\n+| Bytes   | Name           | Data Type                    | Description |\n+|---------|----------------|------------------------------|-------------|\n+| varies  | `vSpendAuths`  | `SpendAuth[nSpends]`         | Authorization signatures for each spend. |\n+\n+### SpendAuth\n+\n+| Bytes   | Name        | Data Type      | Description |\n+|---------|-------------|----------------|-------------|\n+| varies  | `signature` | `byte[siglen]` | Signature over the transaction id authorizing the spend. Length is determined by the protocol type of the account's current signing key. |\n+\n+The `vSpendAuths` entries correspond positionally to `vSpends` entries. Each\n+signature MUST verify against the `current_key` of the account identified by\n+the corresponding `accountId`.\n+\n+### Value Balance\n+\n+The value balance map entry for the consensus account spend bundle equals the\n+negation of the sum of all spend values (since spends remove value from\n+accounts and add it to the transparent transaction value pool):\n+\n+```\n+valueBalance = -Σ(spend.value for each spend in vSpends)\n+```\n+\n+### Consensus Rules\n+\n+- Full validators MUST reject a transaction containing a consensus account\n+  spend bundle if any `AccountSpend` references an `accountId` that does not\n+  exist.\n+- Full validators MUST reject a transaction if the total value spent from any\n+  single account (summing all `AccountSpend` entries for that account) exceeds\n+  that account's current balance.\n+- Full validators MUST reject a transaction containing a consensus account\n+  spend bundle if any output within the transaction is not shielded. This\n+  preserves the requirement that all newly-issued ZEC must be shielded at least\n+  once before being transferred or deshielded.\n+- As of the activation of this ZIP, full validators MUST reject transactions\n+  where any `AccountSpend` has `assetClass` other than 0x00.\n+\n+## Changes to ZIP 270\n+\n+The following row is added to the table of key usages for tracked signing keys:\n+\n+| Usage typecode | Description |\n+| -------------- | ----------- |\n+| 1              | ZIP NNN: Consensus account spending |\n+\n+The `key_id` for this usage is the `consensus_account_id` as defined in this\n+ZIP.\n+\n+This usage typecode authorizes spending from consensus accounts. The tracked\n+signing key associated with a consensus account may be rotated using the key\n+rotation mechanism defined in ZIP 270. Upon rotation, the account's\n+`current_key` is updated to the new key, while the `consensus_account_id`\n+remains unchanged.\n+\n+## Changes to ZIP 207\n+\n+The following paragraphs are added to the section Funding Streams, after the\n+definition of Revision 1 recipient identifiers:\n+\n+> As of Revision 3, a recipient identifier MAY be a consensus account\n+> identifier as defined in ZIP NNN.\n+>\n+> Any funding stream defined after the activation of ZIP NNN MUST use a\n+> consensus account identifier as its recipient.\n+\n+## Changes to ZIP 214\n+\n+The following bullet point is added to the section Revisions:\n+\n+> - Revision 3: The Zcash Community Grants funding stream is redirected to a\n+>   consensus account at the activation height of this ZIP. The\n+>   coinholder-controlled fund (dev fund lockbox) continues unchanged.\n+\n+The Revision 2 ZCG funding stream (`FS_FPF_ZCG_H2`) ends at the activation\n+height of this ZIP. A new Revision 3 ZCG funding stream begins at that height,\n+directing outputs to a ZCG consensus account.\n+\n+The following new sections are added:\n+\n+> ## Mainnet Recipients for Revision 3\n+>\n+> ```\n+> FS_FPF_ZCG_H3 = TODO (consensus account identifier)\n+> ```\n+>\n+> ## Testnet Recipients for Revision 3\n+>\n+> ```\n+> FS_FPF_ZCG_H3 = TODO (consensus account identifier)\n+> ```\n+\n+## Changes to the Zcash Protocol Specification\n+\n+### Section 3.4 (Transactions and Treestates)\n+\n+The global chain state is extended to include a map `consensus_accounts` from\n+`consensus_account_id` to account state, as defined in this ZIP.\n+\n+### Section 7.1.1 (Transaction Consensus Rules)\n+\n+The following consensus rules are added:\n+\n+**Account registration bundle:**\n+- A transaction MAY contain an account registration bundle.\n+- Each `AccountRegistration` MUST have a valid signature in the corresponding\n+  `RegistrationAuth` entry, verifiable against the provided `publicKey`.\n+- Each `AccountRegistration` MUST NOT reference a `consensus_account_id` that\n+  already exists.\n+- In a coinbase transaction, if an account registration bundle is present, a\n+  consensus account output bundle MUST also be present, and for each registered\n+  account there MUST be at least one `AccountOutput` crediting value to that\n+  account.\n+\n+**Consensus account output bundle:**\n+- A transaction MAY contain a consensus account output bundle.\n+- Each `AccountOutput` MUST reference a `consensus_account_id` that exists\n+  (either pre-defined in consensus rules, or registered in an account\n+  registration bundle processed earlier in the same transaction, or previously\n+  registered).\n+- As of the activation of this ZIP, `assetClass` MUST be 0x00 for all\n+  `AccountOutput` entries.\n+\n+**Consensus account spend bundle:**\n+- A non-coinbase transaction MAY contain a consensus account spend bundle.\n+- If a consensus account spend bundle is present, all outputs within the\n+  transaction MUST be shielded (Sapling or Orchard outputs only; no transparent\n+  outputs).\n+- Each `AccountSpend` MUST reference a `consensus_account_id` that exists.\n+- Each `AccountSpend` MUST have a valid signature in the corresponding\n+  `SpendAuth` entry, verifiable against the account's current signing key.\n+- The total value spent from any single account within a transaction MUST NOT\n+  exceed that account's balance.\n+- As of the activation of this ZIP, `assetClass` MUST be 0x00 for all\n+  `AccountSpend` entries.\n+\n+### Section 7.8 (Block Subsidy and Founders' Reward)\n+\n+Funding stream outputs to consensus accounts are credited via implicit\n+additions to the `consensus_accounts` map, rather than via explicit transaction\n+outputs. The value balance of a coinbase transaction is computed to include\n+these implicit credits.\n+\n+\n+# Rationale\n+\n+Implicit rules are kept in the post-rework to reduce churn for miners; they\n+only need to know the fraction of block subsidy available for miner rewards,\n+instead of needing to ensure they craft the correct outputs for the current\n+consensus rules in their block templates.\n+\n+Shielded coinbase outputs are retained alongside consensus accounts because\n+they offer superior privacy properties for miners who desire them. Although the\n+receiving address of a shielded coinbase output is publicly visible, its\n+subsequent spend is indistinguishable from other shielded spends. In contrast,\n+spending from a consensus account is always a publicly visible operation.\n+Retaining shielded coinbase also avoids forcing privacy-conscious miners to\n+generate a new consensus account key for each block, which would cause\n+unbounded growth of the consensus account set.\n+\n+Account registration and account outputs are specified as separate bundles\n+because these operations have fundamentally different characteristics. Account\n+registrations require a signature proving control of the registered public key;\n+this prevents the creation of accounts with keys that no one controls, which\n+would permanently burn any funds credited to them. Account outputs, in\n+contrast, require no authorization—any party may credit value to an existing\n+account, analogous to sending funds to a known address. Separating these into\n+distinct bundles provides a cleaner abstraction: the registration bundle\n+handles identity establishment (with authorization), while the output bundle\n+handles value transfer (without authorization). The requirement that a coinbase\n+transaction with registrations must include outputs to those accounts ensures\n+that newly registered accounts receive their initial funding in a single atomic\n+operation.\n+\n+\n+# Alternatives\n+\n+An alternative design considered was a single combined bundle containing both\n+registrations and credits, with registrations including a value field. This\n+would result in a single entry in the value balance map. However, this approach\n+conflates two distinct operations (identity establishment and value transfer)\n+and requires either including asset class fields in registrations (adding\n+complexity) or limiting registrations to ZEC only (reducing future\n+flexibility). The two-bundle design provides cleaner separation of concerns and\n+allows the registration bundle to remain simple and focused on key management.\n+\n+\n+# Deployment\n+\n+TBD\n+\n+\n+# Reference implementation\n+\n+TBD\n+\n+\n+# Open issues\n+\n+TBD\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later](protocol/protocol.pdf)\n+\n+[^protocol-blockchain]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.3: The Block Chain](protocol/protocol.pdf#blockchain)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^zip-extfmt]: [ZIP NNN: Extensible Transaction Format](https://github.com/zcash/zips/pull/1156)\n+\n+[^zip-0270]: [ZIP 270: Key Rotation for Tracked Signing Keys](https://github.com/zcash/zips/pull/1117)\n+\n+[^zip-0207]: [ZIP 207: Funding Streams](zip-0207.rst)\n+\n+[^zip-0214]: [ZIP 214: Consensus rules for a Zcash Development Fund](zip-0214.rst)\n+\n+[^zip-0227]: [ZIP 227: Zcash Shielded Assets (ZSA)](zip-0227.md)\n+\n+[^zip-0317]: [ZIP 317: Proportional Transfer Fee Mechanism](zip-0317.md)\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](zip-1016.md)"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0272.md","title":"Zcash Consensus Accounts","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"high-level-summary","text":"High-level summary"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"consensus-accounts","text":"Consensus accounts"},{"level":2,"id":"global-account-state","text":"Global account state"},{"level":2,"id":"initializing-an-account","text":"Initializing an account"},{"level":2,"id":"account-registration-bundle","text":"Account registration bundle"},{"level":3,"id":"account-registration-effecting-data","text":"Account Registration Effecting Data"},{"level":3,"id":"accountregistration","text":"AccountRegistration"},{"level":3,"id":"account-registration-authorizing-data","text":"Account Registration Authorizing Data"},{"level":3,"id":"registrationauth","text":"RegistrationAuth"},{"level":3,"id":"value-balance","text":"Value Balance"},{"level":3,"id":"consensus-rules","text":"Consensus Rules"},{"level":2,"id":"consensus-account-output-bundle","text":"Consensus account output bundle"},{"level":3,"id":"consensus-account-output-effecting-data","text":"Consensus Account Output Effecting Data"},{"level":3,"id":"accountoutput","text":"AccountOutput"},{"level":3,"id":"consensus-account-output-authorizing-data","text":"Consensus Account Output Authorizing Data"},{"level":3,"id":"value-balance-1","text":"Value Balance"},{"level":3,"id":"consensus-rules-1","text":"Consensus Rules"},{"level":2,"id":"consensus-account-spend-bundle","text":"Consensus account spend bundle"},{"level":3,"id":"consensus-account-spend-effecting-data","text":"Consensus Account Spend Effecting Data"},{"level":3,"id":"accountspend","text":"AccountSpend"},{"level":3,"id":"consensus-account-spend-authorizing-data","text":"Consensus Account Spend Authorizing Data"},{"level":3,"id":"spendauth","text":"SpendAuth"},{"level":3,"id":"value-balance-2","text":"Value Balance"},{"level":3,"id":"consensus-rules-2","text":"Consensus Rules"},{"level":2,"id":"changes-to-zip-270","text":"Changes to ZIP 270"},{"level":2,"id":"changes-to-zip-207","text":"Changes to ZIP 207"},{"level":2,"id":"changes-to-zip-214","text":"Changes to ZIP 214"},{"level":2,"id":"mainnet-recipients-for-revision-3","text":"Mainnet Recipients for Revision 3"},{"level":2,"id":"testnet-recipients-for-revision-3","text":"Testnet Recipients for Revision 3"},{"level":2,"id":"changes-to-the-zcash-protocol-specification","text":"Changes to the Zcash Protocol Specification"},{"level":3,"id":"section-34-transactions-and-treestates","text":"Section 3.4 (Transactions and Treestates)"},{"level":3,"id":"section-711-transaction-consensus-rules","text":"Section 7.1.1 (Transaction Consensus Rules)"},{"level":3,"id":"section-78-block-subsidy-and-founders-reward","text":"Section 7.8 (Block Subsidy and Founders' Reward)"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"alternatives","text":"Alternatives"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"open-issues","text":"Open issues"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1075,"title":"[ZIP 1]: Network Upgrade Process and Scheduling","author":"daira","createdAt":"2025-08-25T18:23:44Z","updatedAt":"2026-03-24T12:29:15Z","url":"https://github.com/zcash/zips/pull/1075","diffUrl":"https://github.com/zcash/zips/pull/1075.diff","headRef":"zip-1","headRepo":"daira/zips","headSha":"7c7ddae9f5dbca13fbe3d5abeb1e21d7d7e03e4d","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Part of #343.","zipRefs":[1],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0001.md","status":"added","additions":265,"deletions":0,"patch":"@@ -0,0 +1,265 @@\n+\n+    ZIP: 1\n+    Title: Network Upgrade Policy and Scheduling\n+    Owners: Kris Nuttycombe <kris@nutty.land>\n+            Jack Grigg <jack@electriccoin.co>\n+            Daira-Emma Hopwood <daira-emma@electriccoin.co>\n+    Status: Draft\n+    Category: Process\n+    Created: 2025-08-25\n+    License: MIT\n+    Discussions-To: <https://github.com/zcash/zips/issues/343>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"REQUIRED\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14[^BCP14] when, and\n+only when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as defined in the Zcash protocol specification[^protocol-networks].\n+\n+The terms \"Network Upgrade\" and \"Activation Height\" are to be interpreted as defined in ZIP 200[^zip-0200].\n+\n+The term \"End-of-Service Halt Interval\" refers to the number of blocks between the approximate release height of a version of the Zcash node software and the block height at which nodes of that version will refuse to operate.\n+\n+# Abstract\n+\n+This document describes the process of preparing and executing a Zcash Network Upgrade, and the constraints on node implementations that affect network upgrade timelines.\n+\n+# Motivation\n+\n+The Zcash network performs hard-forking upgrades following the full node coordination mechanism specified in ZIP 200[^zip-0200] when changes are made to the consensus rules. In order for the network to continue operating without interruption at network upgrade boundaries, node implementers must coordinate in order to ensure that the nodes that are active on the network at the time of such an upgrade are all running compatible software.\n+\n+Various documents have been written in the past that attempted to provide frameworks or documentation for the network upgrade process at an \"engineers working on the Zcash protocol\" level:\n+\n+- [Network Upgrade Pipeline 1.0](https://electriccoin.co/blog/the-zcash-network-upgrade-pipeline/)\n+- Network Upgrade Pipeline 1.1\n+- [Network Upgrade Pipeline 1.2](https://docs.google.com/drawings/d/1WAvIkVBv_fC4L4wDoAJaMTYVh3dJbwhR5YuP5HgOjFw/edit)\n+  - This was followed for NU3 and NU4.\n+- [Network Upgrade Pipeline 2.0](https://electriccoin.co/blog/network-upgrade-pipeline-2-0/)\n+  - This was followed for NU5 and (to some extent) NU6.\n+- [Deadlines for ZIPs that want to be activated in NU7](https://forum.zcashcommunity.com/t/important-deadline-for-zips-that-want-to-be-activated-in-nu7/48998)\n+\n+These past attempts have suffered from several issues:\n+\n+- The NUPs were designed and used at a time when there was either one single feature being deployed in a network upgrade, or one single entity (usually the Electric Coin Company) developing all the NU's features. As such, and despite best efforts, they were not well-suited for parallelization and they did not accurately specify all of the constraints on timing.\n+- The NUPs were generally too optimistic about timing, especially for complicated upgrades.\n+\n+By following the steps specified by this ZIP, and ensuring that the required timelines are adhered to by node implementers, the Zcash network can maintain uninterrupted continuity of service.\n+\n+\n+# Requirements\n+\n+Engineers working towards a Zcash network upgrade should not miss any important steps if they read this ZIP.\n+\n+# Non-requirements\n+\n+This ZIP does not cover any governance issues, and it does not define any process for how candidate ZIPs should be selected for inclusion in a network upgrade beyond technical constraints.\n+\n+# Specification\n+\n+TODO:\n+- How network protocol versions are chosen.\n+- Audit requirements for upgrades.\n+- Specification requirements:\n+  - version group ID if needed;\n+  - protocol spec changes;\n+  - differences between Testnet and Mainnet;\n+  - peer protocol versions.\n+- What happens if there is a security issue or other problem that necessitates a change to the NU-supporting release?\n+  - before Testnet activation;\n+  - after Testnet activation & before release of node software that sets the Mainnet activation height;\n+  - after the release of node s/w that sets the Mainnet activation height & before Mainnet activation;\n+  - after Mainnet activation.\n+\n+## Constraints on the timing of a Network Upgrade\n+\n+The hardest and most restrictive preconditions for a Network Upgrade occur near to its activation. As such, it is easier to initially work backwards from a desired \"active on Mainnet\" state to figure out what the minimum intervals are between the various stages of NU development and deployment.\n+\n+Testnet and Mainnet activation heights for a Network Upgrade MUST be set in such a way that satisfies the constraints described below.\n+\n+### End-Of-Service Halts\n+\n+Zcash network nodes MUST implement an End-of-Service Halt mechanism, whereby nodes that reach the End-of-Service height on Mainnet automatically shut down and refuse to operate if they detect that the current block height of the network is greater than their End-of-Service height.\n+\n+Nodes SHOULD reach their End-of-Service halt **129024 blocks** (the \"End-of-Service Halt Interval\", approximately 16 weeks) after their approximate release height. The approximate release height is defined as the current block height at the time that the release tag is published, $\\pm$ 500 blocks.\n+\n+New versions of each node implementation SHOULD be released approximately every **48384 blocks** (approximately 6 weeks.)\n+\n+All nodes that do not support the network upgrade MUST have reached their End-of-Service halt height at least **8064 blocks** (approximately one week) prior to the network upgrade's activation height, and SHOULD have reached their End-of-Service halt at least **16128 blocks** prior to the activation height. This ensures that entities who do not upgrade their node software until the End-of-Service halt occurs have time to upgrade before the activation height is reached.\n+\n+<details>\n+<summary>\n+\n+#### Rationale\n+</summary>\n+\n+For the health and stability of the network, it is critical that at the block height at which a network upgrade activates, no unintended chain fork should occur. In order to ensure that this happens, all nodes of the network (but in particular nodes that perform block construction) must correctly implement and enforce the new consensus rules that become active in the activation block.\n+\n+The End-of-Service halt mechanism has been implemented in both `zcashd` and `zebrad` to ensure that incautious users of the node software do not accidentally cause a chain fork by running the wrong consensus rules against the block at the activation height and subsequent blocks. This protection supplements the protections provided by the network using a new consensus branch ID (as specified in ZIP 200 [^zip-0200]), and the nodes of the network communicating using a new peer protocol version identifier for network messages.\n+\n+The End-of-Service Halt Interval is chosen to be approximately 16 weeks to strike a balance between the frequency with which node operators are required to upgrade their software (which is desired to be as infrequent as possible), and the frequency with which it is possible to make network upgrades (which is desired to be as frequent as possible.) The End-of-Service Halt Interval sets a lower bound on the frequency of network upgrades.\n+\n+The approximate 6-week release interval is intended to ensure that there are always at least two working versions of a given node implementation in service at a given time. This allows for a 10-week window after the release of a new version of the node software during which a critical bug in such a release can be mitigated simply by running the previous version of the node software. This also means that after any release, entities in the community have a 10-week window in which to upgrade, and an entity that chooses to only upgrade to every other release has a 4-week window in which to upgrade.\n+</details>\n+\n+### Mainnet activation window\n+\n+The Mainnet activation height of a network upgrade MUST be set at least **10 weeks** prior to its expected activation date (assuming a consistent block production rate). This height SHOULD be decided by unanimous agreement among the various full node developers, to ensure that all full node implementations are able to release compatible software updates with sufficient upgrade time.\n+\n+A node implementation that supports the network upgrade MUST be released at least **4 weeks** prior to that date, and SHOULD be released at the same time that the activation height is set.\n+\n+These timings depend on the current 16-week End-of-Service Halt Interval implemented by zebrad and zcashd; they should be recalculated if the full node End-of-Service policy changes.\n+\n+<details>\n+<summary>\n+\n+#### Rationale\n+</summary>\n+\n+It takes time for people to deploy software updates, especially when they need to be coordinated between multiple organizations.\n+\n+For Zcash Network Upgrades in particular, deploying a full node update is not just upgrading the `zebrad` dependency in a stack: it can also mean making changes to internal transaction parser libraries or other private codebases. As such, network upgrades need to ensure sufficient time between the consensus rules being finalized, and the consensus rules activating on the network. Per ZIP 200, the point at which a set of consensus rules (defined by a particular `CONSENSUS_BRANCH_ID`) is finalized is when an activation height is defined.\n+\n+The 10 week minimum window comes from two places:\n+\n+- The `zcashd` release flow and End-of-Service policy in early Zcash years: a release every 6 weeks and an End-of-Service policy of 16 weeks meant that there was at least 10 weeks between the release of version $X$ and the End-of-Service of version $X-1$.\n+- 10 weeks is a little over 2 months. In practice network upgrades have been less smooth if they had fewer than 3 months between setting the Mainnet activation height (and releasing compatible node software), and activation of the NU.\n+</details>\n+\n+### Audits\n+\n+TODO\n+\n+Implementations of network upgrades SHOULD be audited and SHOULD address any critical issues reported by auditors before setting a Mainnet activation height.\n+\n+#### Auditor selection\n+\n+#### Specification auditing\n+\n+#### Implementation auditing\n+\n+<details>\n+<summary>\n+\n+#### Rationale\n+</summary>\n+\n+Auditors generally impose several requirements.\n+</details>\n+\n+## Timeline\n+\n+\n+### Specification\n+\n+Specification of the consensus rules to be activated by a network upgrade MUST be complete prior to third-party audits of consensus node software implementing those specifications can commence. In the process of specification, the consensus branch ID for the network upgrade MUST be chosen in conformance with ZIP 200[^zip-0200].\n+\n+#### Deployment ZIP\n+\n+A Deployment ZIP for the network upgrade MUST be created that specifies the ZIPs that collectively define the consensus rules that will hold once network upgrade activates, and the consensus branch ID that corresponds to that set of rules. This deployment ZIP may only transition to `Proposed` status once all of the ZIPs it deploys have transitioned to `Proposed`, in accordance with ZIP 0[^zip-0000].\n+\n+- TODO: when are global constants relevant to a network upgrade (apart from the consensus branch ID) set, specifically with respect to parameters that do not affect the operation of the consensus rules as opposed to which consensus rules are enforced.\n+\n+The consensus rules must be stable prior to the specifications transitioning to Proposed\n+- TODO: define \"stable\".\n+- TODO: This is (or should be) duplicative of the definition of Proposed in ZIP 0; once [clarified](https://github.com/zcash/zips/issues/1078) we should reference that instead.\n+\n+<details>\n+<summary>\n+\n+#### ZIP status transitions\n+</summary>\n+\n+The ZIP 0 [^zip-0000] process that describes the lifecycle for Consensus ZIPs being included in a network upgrade is roughly:\n+\n+- Transition individual ZIPs to Proposed as they stabilise\n+- Transition individual ZIPs to Implemented (as relevant)\n+- Transition the deployment ZIP to Proposed (once NU contents is fixed and thus consensus branch ID meaning is stable) along with any remaining individual ZIPs\n+- Transition remaining ZIPs to Implemented (as relevant)\n+- Set testnet activation height (and amend deployment ZIP)\n+- Activate on testnet and do testing\n+    - If a bug is found, remove activation heights and change consensus branch ID\n+- Roughly simultaneously, set mainnet activation height and release implementations with mainnet activation height set\n+- Wait for activation\n+- Transition all ZIPs to Final\n+\n+</details>\n+\n+### Implementation\n+\n+Implementation and specification work may proceed in parallel, insofar as is feasible.\n+\n+### Auditing\n+\n+Third-party audits of consensus node software MUST be complete prior to setting the mainnet activation height for the network upgrade.\n+\n+### Testing\n+\n+Node software implementing the consensus changes for a network upgrade MUST activate successfully on the Zcash test network prior to the mainnet activation height being set. Such network software SHOULD activate and be thoroughly tested on a private fork of the Zcash test network prior to the testnet activation height being set.\n+\n+<details>\n+<summary>\n+\n+#### How to run Private Test Networks\n+</summary>\n+\n+A private test network can be set up in the following manner:\n+\n+- Select a new random consensus branch ID `private_branch_id`.\n+- Select an activation height `h` that is a short period of time in the future of the current testnet.\n+  - Testnet often runs at double the speed of mainnet; plan for that when deciding how long it will take to complete the rest of the pre-activation setup.\n+- Create a private fork of the node software and librustzcash that:\n+  - Changes the consensus branch ID to `private_branch_id`.\n+  - Sets the testnet activation height to `h`.\n+  - Bumps the P2P network protocol version to the testnet protocol version.\n+  - Changes the client subversion name (useful for debugging).\n+- Prepare several machines / operators to participate in the test.\n+  - At least two machines should have sufficient CPU power to participate in mining.\n+- Ensure that all machines will be able to connect to at least one other machine within the private test group. There are two options that can work here:\n+  - Have at least one machine running with a fixed public IP address (e.g. on a VPS).\n+  - Use Tailscale to enable direct connections between machines alongside the regular test network:\n+    - Install tailscale on each machine, logging into either a common shared account, or individual accounts.\n+    - If using individual accounts, share at least one machine within Tailscale to the other Tailscale accounts.\n+    - Ensure that each shared machine has ACLs configured to permit access on the port that the node software is listening on.\n+- Build and run the private testnet node software.\n+  - If the above has taken too long and the activation height has already passed, go back to the start and select a new `private_branch_id` and activation height.\n+  - The private testnet nodes should connect to public Testnet nodes, in addition to other private testnet nodes.\n+- Wait for activation.\n+\n+What should happen is that the private testnet nodes:\n+- reject block `h` from Testnet.\n+- get stuck at block `h - 1` waiting for the network upgrade to activate.\n+- disconnect from any connected public Testnet nodes (either in advance depending on the node software, or immediately after block `h` is mined on Testnet and is rejected by the private testnet nodes).\n+\n+At this point:\n+- Start mining\n+- Exercise the new consensus rules via wallet software connected to the private testnet. [zcash-devtool](https://github.com/zcash/zcash-devtool) is good for this.\n+\n+See the [Private Testnet Testing](https://zebra.zfnd.org/dev/private-testnet.html?highlight=private#private-testnet-test) section of the Zebra book for how to set up Zebra nodes in a private Testnet.\n+</details>\n+\n+#### Test Network Activation\n+\n+A release of the node software that supports the Network Upgrade on Testnet, but not Mainnet MUST be made at least **16128 blocks** (approximately two weeks) prior to the testnet activation height. Such a release MUST set its end-of-service halt height such that nodes having this release version reach their end-of-service halt prior to mainnet activation, respecting the constraints described in [End-Of-Service Halts] above. Such a testnet release MUST correctly implement the preexisting mainnet consensus rules, and be fully usable as a mainnet consensus and/or mining node.\n+\n+- TODO: are there constraints on release heights of testnet-supporting versions related to avoiding testnet chain forks? IIRC we haven't required that existing releases EOS-halt before the testnet activation height, because we don't set testnet EOS halt heights?\n+- ^ There are checkpoints that would be updated in the second release that supports the network upgrade on Testnet, but there's no EoS halt on Testnet.\n+\n+### Partner adoption\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2024.5.1 or later](protocol/protocol.pdf)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2024.5.1. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^zip-0000]: [ZIP 0: ZIP Process](zip-0000.rst)\n+\n+[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)\n+\n+[^zip-0201]: [ZIP 201: Network Peer Management for Overwinter](zip-0201.rst)\n+"},{"filename":"zips/zip-0001.rst","status":"removed","additions":0,"deletions":7,"patch":"@@ -1,7 +0,0 @@\n-::\n-\n-  ZIP: 1\n-  Title: Network Upgrade Policy and Scheduling\n-  Status: Reserved\n-  Category: Consensus Process\n-  Discussions-To: <https://github.com/zcash/zips/issues/343>"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0001.md","title":"Network Upgrade Policy and Scheduling","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"constraints-on-the-timing-of-a-network-upgrade","text":"Constraints on the timing of a Network Upgrade"},{"level":3,"id":"end-of-service-halts","text":"End-Of-Service Halts"},{"level":4,"id":"rationale","text":"Rationale"},{"level":3,"id":"mainnet-activation-window","text":"Mainnet activation window"},{"level":4,"id":"rationale-1","text":"Rationale"},{"level":3,"id":"audits","text":"Audits"},{"level":4,"id":"auditor-selection","text":"Auditor selection"},{"level":4,"id":"specification-auditing","text":"Specification auditing"},{"level":4,"id":"implementation-auditing","text":"Implementation auditing"},{"level":4,"id":"rationale-2","text":"Rationale"},{"level":2,"id":"timeline","text":"Timeline"},{"level":3,"id":"specification-1","text":"Specification"},{"level":4,"id":"deployment-zip","text":"Deployment ZIP"},{"level":4,"id":"zip-status-transitions","text":"ZIP status transitions"},{"level":3,"id":"implementation","text":"Implementation"},{"level":3,"id":"auditing","text":"Auditing"},{"level":3,"id":"testing","text":"Testing"},{"level":4,"id":"how-to-run-private-test-networks","text":"How to run Private Test Networks"},{"level":4,"id":"test-network-activation","text":"Test Network Activation"},{"level":3,"id":"partner-adoption","text":"Partner adoption"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1117,"title":"[ZIP 270]: Key Rotation for Tracked Signing Keys","author":"nuttycom","createdAt":"2025-10-28T23:06:33Z","updatedAt":"2026-03-24T08:03:56Z","url":"https://github.com/zcash/zips/pull/1117","diffUrl":"https://github.com/zcash/zips/pull/1117.diff","headRef":"draft-ecc-key-rotation","headRepo":"zcash/zips","headSha":"ebcfb92f8e62460895249aadac346f0fcece6de3","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Part of #1047.","zipRefs":[270],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":4,"deletions":2,"patch":"@@ -181,7 +181,7 @@ written.\n     <tr> <td>235</td> <td class=\"left\"><a href=\"zips/zip-0235.md\">Remove 60% of Transaction Fees From Circulation</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/924\">zips#924</a></td>\n     <tr> <td>245</td> <td class=\"left\"><a href=\"zips/zip-0245.rst\">Transaction Identifier Digests & Signature Validation for Transparent Zcash Extensions</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/384\">zips#384</a></td>\n     <tr> <td>246</td> <td class=\"left\"><a href=\"zips/zip-0246.rst\">Digests for the Version 6 Transaction Format</a></td> <td>Draft</td> <td class=\"left\"></td>\n-    <tr> <td><span class=\"reserved\">270</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0270.md\">Key Rotation for Tracked Signing Keys</a></td> <td>Reserved</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1047\">zips#1047</a></td>\n+    <tr> <td>270</td> <td class=\"left\"><a href=\"zips/zip-0270.md\">Key Rotation for Tracked Signing Keys</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1047\">zips#1047</a></td>\n     <tr> <td>302</td> <td class=\"left\"><a href=\"zips/zip-0302.rst\">Standardized Memo Field Format</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/366\">zips#366</a></td>\n     <tr> <td><span class=\"reserved\">303</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0303.rst\">Sprout Payment Disclosure</a></td> <td>Reserved</td> <td class=\"left\"></td>\n     <tr> <td>304</td> <td class=\"left\"><a href=\"zips/zip-0304.rst\">Sapling Address Signatures</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/345\">zips#345</a></td>\n@@ -212,6 +212,7 @@ written.\n     <tr> <td>2005</td> <td class=\"left\"><a href=\"zips/zip-2005.md\">Quantum Recoverability</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1135\">zips#1135</a></td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n+    <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n   </table></embed>\n \n Drafts without assigned ZIP numbers\n@@ -326,7 +327,7 @@ Index of ZIPs\n     <tr> <td>253</td> <td class=\"left\"><a href=\"zips/zip-0253.md\">Deployment of the NU6 Network Upgrade</a></td> <td>Final</td>\n     <tr> <td><strike>254</strike></td> <td class=\"left\"><strike><a href=\"zips/zip-0254.md\">Deployment of the NU7 Network Upgrade (Withdrawn)</a></strike></td> <td>Withdrawn</td>\n     <tr> <td>255</td> <td class=\"left\"><a href=\"zips/zip-0255.md\">Deployment of the NU6.1 Network Upgrade</a></td> <td>Proposed</td>\n-    <tr> <td><span class=\"reserved\">270</span></td> <td class=\"left\"><a class=\"reserved\" href=\"zips/zip-0270.md\">Key Rotation for Tracked Signing Keys</a></td> <td>Reserved</td>\n+    <tr> <td>270</td> <td class=\"left\"><a href=\"zips/zip-0270.md\">Key Rotation for Tracked Signing Keys</a></td> <td>Draft</td>\n     <tr> <td>271</td> <td class=\"left\"><a href=\"zips/zip-0271.md\">Dev Fund Extension and One-Time Disbursement</a></td> <td>Proposed</td>\n     <tr> <td>300</td> <td class=\"left\"><a href=\"zips/zip-0300.rst\">Cross-chain Atomic Transactions</a></td> <td>Proposed</td>\n     <tr> <td>301</td> <td class=\"left\"><a href=\"zips/zip-0301.rst\">Zcash Stratum Protocol</a></td> <td>Active</td>\n@@ -384,4 +385,5 @@ Index of ZIPs\n     <tr> <td>2005</td> <td class=\"left\"><a href=\"zips/zip-2005.md\">Quantum Recoverability</a></td> <td>Draft</td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td>\n+    <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Something Short and To the Point}</a></td> <td>Draft</td>\n   </table></embed>"},{"filename":"zips/zip-0270.md","status":"modified","additions":343,"deletions":1,"patch":"@@ -5,8 +5,350 @@\n             Kris Nuttycombe <kris@nutty.land>\n             Daira-Emma Hopwood <daira@jacaranda.org>\n             Arya <arya@zfnd.org>\n-    Status: Reserved\n+    Status: Draft\n     Category: Consensus\n     Created: 2025-09-16\n     License: MIT\n     Discussions-To: <https://github.com/zcash/zips/issues/1047>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"REQUIRED\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [^BCP14] when, and\n+only when, they appear in all capitals.\n+\n+The term \"full validator\" in this document is to be interpreted as defined in\n+the Zcash protocol specification [^protocol-blockchain].\n+\n+The term \"network upgrade\" in this document is to be interpreted as described in\n+ZIP 200. [^zip-0200]\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in\n+§ 3.12 ‘Mainnet and Testnet’. [^protocol-networks]\n+\n+# Abstract\n+\n+This ZIP describes a mechanism for users to rotate their signing keys.\n+\n+# Motivation\n+\n+There are two facilities proposed to be added in the NU7 Network Upgrade that\n+define authorization keys in consensus:\n+\n+* The Coinholder-Controlled Fund introduced by the Community and Coinholder\n+  Funding Model ZIP [^zip-1016] has a disbursement authorization key held by\n+  the Key-Holder Organizations. This key will initially be hard-coded into the\n+  consensus rules, and there needs to be some transaction-based mechanism to\n+  rotate it.\n+* Each ZSA Issuer has an issuance authorizing key [^zip-0227-issuance-keys]\n+  that it uses to authorize asset issuance. The corresponding validating key is\n+  initially set by the first issuance bundle containing that key.\n+\n+It would be beneficial for this mechanism to support rotation of both of these\n+key types, and any similar key types defined by future facilities of the Zcash\n+consensus protocol.\n+\n+## Use cases\n+\n+### Small-scale single issuer (e.g. toy usage)\n+\n+- Each issued asset uses `ik` as both the issuance key and rotation key (per default)\n+- User's backup includes `isk` (or its derivation root)\n+- If both consensus and the wallet is upgraded to prefer ML-DSA, then the\n+  wallet can migrate the user's assets to ML-DSA by deriving `ik_ML-DSA` (or\n+  generating and backing it up), then creating a single key rotation bundle\n+  that:\n+    - Rotates issuer `ik`'s issuance key from `ik` to `ik_ML-DSA`\n+    - Rotates issuer `ik`'s rotation key from `ik` to `ik_ML-DSA`\n+\n+### ZSA asset bridges\n+\n+- `ik` is a FROST key shared among the bridge validators\n+- Rotation key is held by the bridge operator (or operators via FROST)\n+- Bridge operator can change the bridge validator set by rotating `ik`\n+- Two bridge operators can choose to use the same validator set `ik_C`:\n+    - Bridge operator A uses `rotation_key_A` to rotate their `ik_A` issuer to\n+      `ik_C` (with `ik_C`'s consent)\n+    - Bridge operator B uses `rotation_key_B` to rotate their `ik_B` issuer to\n+      `ik_C` (with `ik_C`'s consent)\n+    - If bridge operator A decides to unmerge, they use `rotation_key_A` to\n+      rotate their `ik_A` issuer to `ik_A'` (or back to `ik_A` would work I\n+      guess), with the consent of `ik_A'` but not `rotation_key_B` or `ik_C`\n+\n+### Issuer merging\n+\n+An issuer can choose to transfer all of their controlled assets to another issuer, by creating a single key rotation bundle that:\n+- Rotates issuer `ik_A`'s issuance key from `ik_A` to `ik_B`, authorized by `rotation_key_A` and `ik_B`\n+- Rotates issuer `ik_A`'s rotation key from `rotation_key_A` to `rotation_key_B`, authorized by `rotation_key_A` and `rotation_key_B`\n+- Issuer B now can issue all of issuer `ik_A`'s assets, without knowledge of `ik_A` (but needs to continue to use `ik_A` as the identifier for those assets due to how `assetDigest` is defined)\n+\n+# Privacy Implications\n+\n+The mechanisms defined in this ZIP are only suitable for rotation of keys that\n+are publically visible in the state of the consensus protocol.\n+\n+# Requirements\n+\n+- The mechanism must support both keys that are initially hard-coded in\n+  consensus rules, and keys that are \"register on first use\".\n+- The key rotation mechanism must not impose any additional requirements\n+  beyond those of the signature scheme for the public key used to verify\n+  the rotation being authorized.\n+\n+# Non-requirements\n+\n+- This ZIP does not address rotation of spending keys associated with user\n+  addresses. If users need to rotate spending keys (for example if a wallet\n+  seed is compromised), it is RECOMMENDED to transfer all funds out of the\n+  wallet into a new wallet, and to inform counterparties that the old addresses\n+  should no longer be used.\n+- If a threshold signature scheme is used, the threshold need not be\n+  consensus-visible.\n+\n+# Specification\n+\n+## Tracked signing keys\n+\n+A tracked signing key is a signing key that is tracked by full validators, and provided\n+by them as part of verifying changes to the global chain state made by any relevant\n+transaction.\n+\n+Theses are distinct from untracked (normal) signing keys, which are provided by\n+transactions themselves, and are only used within the context of validating that single\n+transaction.\n+\n+Each tracked signing key has an associated `protocol_type`, identifying the signing\n+protocol that the key is for. Tracked signing keys MUST use a protocol type defined in\n+this ZIP.\n+\n+The following protocol types are currently valid in consensus:\n+\n+| Protocol typecode | Description |\n+| ----------------- | ----------- |\n+| 0                 | `RedPallas` |\n+| 1                 | `BIP340`    |\n+\n+Additional protocol types MAY be added to this ZIP via modifications specified\n+in other ZIPs.\n+\n+## Rotation keys\n+\n+Every tracked signing key has, in any given chain state, a corresponding\n+\"rotation key\" that authorizes key rotation operations that change the tracked\n+signing key.\n+\n+Rotation keys are themselves tracked signing keys, and thus can also be\n+rotated. As of the current revision of this ZIP, a rotation key is always its\n+own rotation key; there are no \"depth-2\" rotation keys.\n+\n+Rotation keys MAY be any valid protocol type.\n+\n+## Key usage\n+\n+Each tracked signing key has an associated \"usage\", specifying which\n+sub-protocol (within the overall Zcash protocol) it is used within.\n+\n+The set of defined key usages for tracked signing keys is as follows:\n+\n+| Usage typecode | Description                    |\n+| -------------- | ------------------------------ |\n+| 1              | ZIP TBD: Lockbox disbursements |\n+| 2              | ZIP 227: ZSA Issuance          |\n+\n+Additional key usages MAY be added to this ZIP via modifications specified in\n+other ZIPs.\n+\n+Particular key usages MAY require a strict subset of the defined protocol\n+types.\n+\n+Note that no Rotation usage type is defined.\n+\n+## Key identifiers\n+\n+Each tracked signing key has a corresponding \"key identifier\" `key_id` that\n+represents it in the global chain state.\n+\n+`key_id` is treated as an opaque byte string within the context of this ZIP.\n+The format for any particular `key_id` is defined by the corresponding key\n+usage in its relevant ZIP.\n+\n+Consensus rule: `(key_usage, key_id)` MUST be globally unique.\n+\n+## Key Rotation Bundle\n+\n+The transaction format for transaction version 6 is modified to contain an\n+additional section:\n+\n++-------------------------+---------------------+-----------------+--------------------------------------------------------------+\n+| varies                  | ``nKeyRotations``   | ``compactSize`` | The number of KeyRotationAction entries in ``vKeyRotations`` |\n++-------------------------+---------------------+-----------------+--------------------------------------------------------------+\n+| ??? * ``nKeyRotations`` | ``vKeyRotations``   | ???             | A sequence of key rotation action descriptions               |\n++-------------------------+---------------------+-----------------+--------------------------------------------------------------+\n+\n+## Global state\n+\n+A `key_pointer` is the tuple `(key_usage, key_id)`.\n+    - `key_usage` is defined above\n+    - `key_id` is a byte string defined at key registration or by consensus\n+\n+The global chain state tracks:\n+- a mapping of current keys `key_pointer -> (application_key, rotation_key)` where:\n+    - `application_key` is the public key used to authorize subprotocol\n+      operations, such as ZSA issuance or lockbox disbursement.\n+    - `rotation_key` is the public key used to authorize rotations of both\n+      `application_key` and `rotation_key`.\n+\n+## Key registration\n+\n+When registering a new key, the initial rotation key MAY be the same as the\n+signing key. This will be the case for ZSA issuance keys.\n+\n+## Key rotation\n+\n+A Key Rotation operation modifies the global state, by replacing the current\n+key for a given key pointer with a new key.\n+\n+- `key_pointer`: A pointer to the key to be rotated.\n+    - The original key that caused the creation of the map entry:\n+        - protocol type (e.g. `RedPallas`, `BIP340`)\n+        - `pubkey`\n+- `new_key`: The new key to replace it with:\n+    - protocol type (e.g. `RedPallas`, `BIP340`) that is supported in consensus by `key_usage`\n+    - `pubkey`\n+- `rotation_auth_sig`\n+    - Using protocol of the existing `rotation_key` key that authorizes rotation of `prev_key`\n+        - in the case of `key_usage = rotation_key` would be precisely `prev_key`)\n+- `evidence_sig`\n+    - A signature that is performed using `new_key` to prevent accidentally bricking yourself (or mischief from rotating to a key held by someone else). This should be required at least if `key_usage == rotation_key`. The new key is \"consenting\" to be rotated in by signing the txid.\n+\n+A key rotation operation proceeds as follows:\n+\n+- Resolve `key_pointer` to obtain the existing key `prev_key`, and its current `rotation_key`.\n+- Verify `rotation_auth_sig` with `rotation_key`.\n+- Verify `evidence_sig` with `new_key`.\n+- Update the global state at `key_pointer` to replace `prev_key` with `new_key`.\n+\n+WIP alternate encoding:\n+- `key_pointer`: A pointer to the global map entry that currently uses existing key `prev_key` in global state:\n+    - `key_usage: lockbox_sig | zsa_issuance`\n+    - `key_depth`\n+    - `key_id`: The original key that caused the creation of the map entry:\n+        - protocol type (e.g. `RedPallas`, `BIP340`)\n+        - `pubkey`\n+\n+### Key Rotation Actions\n+\n+```rust\n+struct KeyRotation {\n+  /// The purpose of the key that is being rotated. Key purpose cannot change.\n+  key_purpose: KeyPurpose,\n+  /// The original key in the chain of keys that we're rotating. This implies the current rotation key.\n+  orig_key_pubkey: Vec<u8>,\n+  /// The protocol type of the new key being registered. Allowed changes to the protocol type are controlled by consensus;\n+  /// at present, this must always correspond to the protocol type for the existing key being rotated.\n+  new_key_protocol: KeyProtocol,\n+  /// The pubkey that will replace the current key for the given purpose in the chain descending from `orig_key_pubkey`.\n+  new_key_pubkey: Vec<u8>,\n+  /// The signature that validates against the rotation key for the key being replaced. This should sign the txid.\n+  rotation_auth_sig: Vec<u8>,\n+  /// A signature that proves control over the signing keys for `new_key_pubkey`. This should also sign the txid.\n+  evidence_sig: Vec<u8>,\n+}\n+```\n+\n++-------------------------------+---------------------+-----------------+--------------------------------------------------------------+\n++-------------------------------+---------------------+-----------------+--------------------------------------------------------------+\n++-------------------------------+---------------------+-----------------+---------------------------------------------------------------------+\n++-------------------------------+---------------------+-----------------+---------------------------------------------------------------------+\n++-------------------------------+---------------------+-----------------+---------------------------------------------------------------------+\n+\n+A new Key Rotation bundle\n+\n+* Vec of rotation operations, applied to consensus in order (to match e.g. order that txs are applied within blocks):\n+  * Pointer to the global map entry that currently uses existing key `prev_key` in global state:\n+    * `key_usage: key_rotation | lockbox_sig | zsa_issuance`\n+    * The original key that cause the creation of the map entry:\n+      * protocol type (e.g. `RedPallas`, `BIP340`)\n+      * `pubkey`\n+  * New key `new_key` to replace it with:\n+    * protocol type (e.g. `RedPallas`, `BIP340`) that is supported in consensus by `key_usage`\n+    * `pubkey`\n+  * `rotation_auth_sig`\n+    * Using protocol of the existing `key_rotation` key that authorizes rotation of `prev_key`\n+      * in the case of `key_usage = key_rotation` would be precisely `prev_key`)\n+  * `evidence_sig`\n+    * A signature that is performed using `new_key` to prevent accidentally bricking yourself (or mischief from rotating to a key held by someone else). This should be required at least if `key_usage == key_rotation`. The new key is \"consenting\" to be rotated in by signing the txid.\n+\n+## Consensus keys & Rotation Keys\n+\n+### Rotation keys\n+\n+A rotation key provides the keyholder with the capability to create a valid signature authorizing the effects of a key rotation bundle.\n+\n+# Notes / Open Questions\n+\n+- When rotating ZSA rotation keys, an entry can be added or modified to map the latest ik(s) to the original ik\n+    - Creating the first ZSA issuance key may need a pointer to the existing or original rotation-key/ik without replacing it\n+- Should the AssetBurn encoding include an asset_desc_hash instead of an asset_base?\n+- Could we defer the parts of this outside the orchard tx encoding to NU7.1 or NU8? It'll need another format change but at least it'll be a simple one, and while this is a small change on its own, NU7's scope has crept a little more than would be ideal. On the other hand, we did already defer this from NU6.1 to NU7. Spending from lockbox inputs seems closer to trivial, maybe we could allow spending from lockbox inputs in NU7 and do key rotation in the following NU?\n+    - The Deferred Dev Fund Lockbox Disbursement ZIP that was voted on requires key rotation due to its Requirements section having these two requirements:\n+        - Funds are held in a multisig resistant to compromise of some of the parties' keys, up to a given threshold.\n+        - No single party’s non-cooperation or loss of keys is able to cause any Protocol-defined Ecosystem Funding to be locked up unrecoverably.\n+    - The combination of these two requirements forces us to specify and implement key rotation as part of Lockbox Disbursement.\n+- Have a separate rotation key, independent of the signing key.\n+  - Rotation key can rotate either itself or the signing key.\n+  - Signing key can authorize transactions (ZSA issuance or lockbox disbursement).\n+  - For ZSAs, the initial rotation key is the same as the signing key.\n+\n+## Previously considered global state alternative (WIP)\n+\n+The global chain state tracks maps of:\n+- `current_key -> Vec<original_key>`, and\n+- `original_key -> key_state`\n+where\n+- `original_key` is the original key that was added to the global state as a\n+  tracked signing key\n+- `current_key` is a tracked signing key that should be expected instead of\n+  `original_key`\n+- `key_state = (current_key, Vec<parent_original_key>, Vec<child_original_key>)`\n+  - `current_key` is the key required to authenticate control of the key slot\n+  - `parent_original_key` is a `original_key` of a `current_key` that may\n+    authorize mutations to the `current_key` and `child_original_key` of this\n+    `key_state`\n+  - `child_original_key` is a `original_key` for a `key_state` for which\n+    mutations may be authorized by the `current_key` of this `key_state`\n+\n+If a the `key_state` of an `original_key` has no `parent_original_key`, it may\n+mutate itself.\n+\n+\n+## Rationale\n+\n+- Separate key usages: enables independent rotation (e.g. if a key-holder org accidentally uses their lockbox disbursement key for issuing an asset, they can recover from that mistake)\n+\n+- Vec of rotation operations that each rotate a single key:\n+    - It is equivalent to a struct that contains optional fields (would need to define optionality of those fields, and either representation requires defining ordering)\n+    - It enables extending rotation key concept in future to permit multiple rotation keys, without requiring a transaction format change.\n+\n+\n+# Deployment\n+\n+This ZIP is proposed to activate with Network Upgrade 7. [^draft-arya-deploy-nu7]\n+\n+\n+# Reference implementation\n+\n+TBD\n+\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.1 [NU6.1] or later](protocol/protocol.pdf)\n+\n+[^draft-arya-deploy-nu7]: [draft-arya-deploy-nu7: Deployment of the NU7 Network Upgrade](draft-arya-deploy-nu7.md)"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0270.md","title":"Key Rotation for Tracked Signing Keys","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":2,"id":"use-cases","text":"Use cases"},{"level":3,"id":"small-scale-single-issuer-eg-toy-usage","text":"Small-scale single issuer (e.g. toy usage)"},{"level":3,"id":"zsa-asset-bridges","text":"ZSA asset bridges"},{"level":3,"id":"issuer-merging","text":"Issuer merging"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"tracked-signing-keys","text":"Tracked signing keys"},{"level":2,"id":"rotation-keys","text":"Rotation keys"},{"level":2,"id":"key-usage","text":"Key usage"},{"level":2,"id":"key-identifiers","text":"Key identifiers"},{"level":2,"id":"key-rotation-bundle","text":"Key Rotation Bundle"},{"level":2,"id":"global-state","text":"Global state"},{"level":2,"id":"key-registration","text":"Key registration"},{"level":2,"id":"key-rotation","text":"Key rotation"},{"level":3,"id":"key-rotation-actions","text":"Key Rotation Actions"},{"level":2,"id":"consensus-keys--rotation-keys","text":"Consensus keys &#x26; Rotation Keys"},{"level":3,"id":"rotation-keys-1","text":"Rotation keys"},{"level":1,"id":"notes--open-questions","text":"Notes / Open Questions"},{"level":2,"id":"previously-considered-global-state-alternative-wip","text":"Previously considered global state alternative (WIP)"},{"level":2,"id":"rationale","text":"Rationale"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1107,"title":"[ZIP TBD]: Circle STARK Verification as a Transparent Zcash Extension","author":"AbdelStark","createdAt":"2025-10-14T16:46:41Z","updatedAt":"2026-03-24T07:19:04Z","url":"https://github.com/zcash/zips/pull/1107","diffUrl":"https://github.com/zcash/zips/pull/1107.diff","headRef":"main","headRepo":"AbdelStark/zips","headSha":"2b371d4f9d286e5896aad664dd641ec3c9a541da","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"# About This ZIP defines a new **Transparent Zcash Extension** that verifies a bounded‑size **Circle STARK** proof (Stwo). The extension has a single `type` and a **single mode**, and exposes a compact precondition/witness interface suitable for verifying L2 validity proofs on Zcash L1. ## Motivatio","zipRefs":[],"draftRefs":["draft-stark-verify-nu"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-stark-verify-nu.md","status":"added","additions":173,"deletions":0,"patch":"@@ -0,0 +1,173 @@\n+    ZIP: XXX\n+    Title: Circle STARK Verification as a Transparent Zcash Extension\n+    Owners: Abdel <abdel.dev.bitcoin@proton.me>\n+            Michael Zaikin <michael.z@starkware.co>\n+    Status: Draft\n+    Category: Consensus\n+    Created: 2025-10-14\n+    License: MIT\n+    Discussions-To: <https://forum.zcashcommunity.com/t/stark-verification-as-a-transparent-zcash-extension-tze-i-e-enabler-for-a-starknet-style-l2-on-zcash/52486>\n+\n+# Terminology\n+\n+The key words \"MUST\", \"SHOULD\", and \"MAY\" are to be interpreted as described in BCP 14 [^BCP14] when, and only when, they appear in all capitals.\n+\n+## Abstract\n+\n+This ZIP defines a new Transparent Zcash Extension that verifies a bounded‑size Circle STARK [^circle-stark-paper] proof. The extension has a single `type` and a single mode, and exposes a compact precondition/witness interface suitable for verifying validity proofs of a Starknet-style scaling solution [^sn-stack] on Zcash. It specifies:\n+\n+- a prefix‑free encoding of precondition and witness;\n+- pinned verifier parameter set via a `param_id` namespace;\n+- strict consensus bounds on sizes and verifier parameters for DoS safety; and\n+- digest integration consistent with a future transaction version that supports TZEs [^zip-0245].\n+\n+This ZIP does not change Sapling/Orchard; it only adds a TZE that can be used to enforce state transition of a Starknet chain or other validity claims.\n+\n+## Motivation\n+\n+Zcash's programmability is restricted by the Bitcoin script, which lacks sufficient expressiveness and implies high costs that limit throughput. A Starknet‑style scaling solution can address the issue by providing an expressive programming language (Cairo [^cairo-paper]), a ZK friendly virtual machine, and an effective proving system (Stwo [^stwo]) that compresses large computation into a succinct proof, suitable for onchain verification. A custom transparent extension [^zip-0222] is the cleanest way to introduce such verifier and enable scaling solutions on top of Zcash.\n+\n+## Requirements\n+\n+* Define a TZE `type` for Stwo/Circle STARK verification, and fully specify how this type is to be encoded, verified, and integrated into transaction digest computation, without allowing implementation‑defined behavior.\n+* Define a `param_id` namespace that pins the specification of the Stwo proof relation, the proof object format, field encodings, transcript/Merkle hash suite and personalization, FRI expansion and query caps, any grinding bounds, and any batch‑verification settings.\n+* A reference implementation of the prover and verifier, and reference test vectors (valid and invalid) must be provided for each such `param_id`.\n+* Limits on proof sizes, etc. must be chosen to avoid potential denial of service, and the necessary updates to ZIP 317 fee calculation [^zip-0317] must be specified.\n+\n+## Specification\n+\n+### TZE identification\n+\n+- `tze_id`: TBD (assigned by ZIP Editors).\n+- `tze_mode`: 0 (only one mode is defined by this ZIP). Any other value MUST be rejected. \n+\n+All serialization below is inside the `tze_data_payload` defined by [^zip-0222]; lengths are `compactSize`.\n+\n+### Precondition (prefix‑free)\n+\n+```\n+u16  param_id         // selects a pinned Stwo verifier parameter set (see below)\n+u8   flags            // bit 0 = bind_ctx; all other bits MUST be 0\n+vec  pub_in           // public inputs to the proved statement (len + bytes)\n+if (flags & 0x01) {\n+    bytes32 ctx_digest   // non-malleable transaction digest\n+}\n+```\n+\n+- `param_id` selects an enumerated, pinned Stwo parameter set. Unknown `param_id` MUST be rejected.\n+- `pub_in` is an opaque byte string to consensus; it MUST be absorbed by the verifier transcript exactly as specified (byte‑for‑byte).\n+- If `bind_ctx` is set, `ctx_digest` MUST equal the transaction's non‑malleable digest [^zip-0244]. A mismatch MUST cause rejection.\n+\n+### Witness (prefix‑free)\n+\n+```\n+vec  proof            // the Stwo (Circle STARK) proof object (len + bytes)\n+```\n+\n+The `proof` MUST conform to the Stwo proof object format pinned by `param_id`.\n+\n+### Verification\n+\n+`bool tze_verify(0, precondition, witness, context)`:\n+\n+1. Parse `param_id`; load the exact pinned parameter set. Reject if unknown.\n+2. Parse `flags`; if any reserved bits set, reject.\n+3. If `bind_ctx`, compute the required `ctx_digest` from `context` and compare; on mismatch, reject. ([Zcash Zips][3])\n+4. Parse `pub_in` and `proof`. Enforce all consensus bounds (see Bounds).\n+5. Initialize the Stwo transcript/channel, **absorb `pub_in` exactly**, and verify `proof` using the parameters pinned by `param_id`.\n+6. Return `true` iff the verifier accepts; otherwise return `false`.\n+\n+### Parameter sets (`param_id`)\n+\n+The namespace is 16‑bit (0x0001..0xFFFF). This ZIP introduces:\n+\n+- `param_id = 0x0001` — `STWO_V1_P1`\n+\n+  - Upstream: `starkware-libs/stwo` [^stwo-repo] at tag TBD, commit `TBD`.\n+  - Proving system: Stwo [^stwo] based on Circle STARK over the 31‑bit Mersenne field [^circle-stark-paper]\n+  - Proof format:\n+    - Stwo proof structure layout (TBD)\n+    - Binary codec and compression algorithm (TBD)\n+  - Prover parameters:\n+    - Stwo channel hash function (TBD)\n+    - Stwo preprocessed AIR configuration (TBD)\n+    - PoW difficulty target (TBD)\n+    - FRI configuration (TBD)\n+\n+> Normative reference material for `STWO_V1_P1` (encodings, proof format, prover parameters) is included alongside this ZIP in `zip-XXXX/params/STWO_V1_P1.json` and the accompanying test vectors. These files are to be treated as normative parts of this specification.\n+\n+Future parameter sets (e.g., different hash suite or query caps) **MAY** be proposed under new `param_id`s in a subsequent ZIP.\n+\n+### Bounds (consensus)\n+\n+Let the following consensus constants be defined for this `(type, mode)`:\n+\n+- `PUBIN_MAX_BYTES = TBD` (discussion range: ≤ 4096).\n+- `PROOF_MAX_BYTES = TBD` (discussion range: 64–192 KiB; finalize from measurements on realistic proof instances).\n+\n+Nodes MUST reject if any bound is exceeded, and MUST NOT allow denial of service via memory exhaustion due to allocating memory before bounds are checked.\n+\n+### Fee model\n+\n+Follows ZIP-317 [^zip-0317].\n+\n+TODO: introduce an adapted fee model with discounted price per byte (rationale: large proof size does not reflect the cost of the verification)\n+\n+TODO: introduce a rate limiting policy that prevents crowding out non-TZE transactions\n+\n+## Rationale\n+\n+- TZE over opcodes: Zcash's extensibility surface for new consensus checks is TZE [^zip-0222]. It avoids entangling with shielded circuit changes and keeps the change set minimal and auditable.\n+- Single system first: Limiting to Stwo keeps the ZIP focused and uses a Rust verifier that integrates cleanly with Zebra and (via FFI) with zcashd. We can consider additional systems (or new Stwo parameter sets) later via new `param_id`s or a new `type`.\n+- Optional context binding: Allowing `bind_ctx` covers common safe usage (proof binds to the transaction's non‑malleable digest) while still permitting protocols that bind to application‑level commitments inside `pub_in`. \n+\n+## Privacy Implications\n+\n+No changes to Sapling/Orchard. Protocols built on top MUST document their own privacy model (e.g., DA choices, metadata leakage). This ZIP's verifier only checks validity of a statement over public inputs; it does not guarantee zero‑knowledge unless the prover uses a ZK proof generation mode.\n+\n+## Backward Compatibility\n+\n+Nodes that do not implement this TZE will reject transactions that use it after activation. Because TZE uses a distinct TZE unspent set and separate fields (`tze_inputs`/`tze_outputs`), existing transparent and shielded logic is unaffected.\n+\n+## Deployment\n+\n+- Network upgrade: Activate in a future NU alongside [^zip-0244] and [^zip-0245].\n+- Activation gating: Require published test vectors and an external review of the Stwo binding.\n+\n+## Security Considerations\n+\n+- DoS safety: Strict size caps; streaming, fail‑fast parsing; bounded allocations; early rejection on malformed encodings.\n+- Determinism & non‑malleability: Encodings are prefix‑free; any malleation of `precondition` MUST cause rejection; `tze_verify` is deterministic per [^zip-0222].\n+- Hash assumptions: Security reduces primarily to collision/preimage resistance of the pinned hash suite plus IOP soundness. No EC‑based hash is assumed; the specifics are pinned by `param_id`.\n+- Implementation risk: Treat the Rust verifier as consensus‑critical code; pin commit/tag and compiler settings; include cross‑checks against test vectors.\n+\n+## Reference Implementation\n+\n+> TBD\n+\n+## Test Vectors\n+\n+> TBD\n+\n+## References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^zip-0222]: [ZIP‑222: Transparent Zcash Extensions (TZE)](https://zips.z.cash/zip-0222)\n+\n+\n+[^zip-0244]: [ZIP-244: Transaction Identifier Non-Malleability](https://zips.z.cash/zip-0244)\n+\n+[^zip-0245]: [ZIP-245: Transaction Identifier Digests & Signature Validation for Transparent Zcash Extensions](https://zips.z.cash/zip-0245)\n+\n+[^zip-0317]: [ZIP-317: Proportional Transfer Fee Mechanism](https://zips.z.cash/zip-0317)\n+\n+[^circle-stark-paper]: [Circle STARKs paper](https://eprint.iacr.org/2024/278)\n+\n+[^sn-stack]: [Starknet stack](https://www.starknet.io/sn-stack/)\n+\n+[^cairo-paper]: [Cairo – a Turing-complete STARK-friendly CPU architecture](https://eprint.iacr.org/2021/1063.pdf)\n+\n+[^stwo]: [Stwo proving system](https://starkware.co/blog/s-two-prover/)\n+\n+[^stwo-repo]: [Stwo prover and verifier implementation](https://github.com/starkware-libs/stwo)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-stark-verify-nu.md","title":"Circle STARK Verification as a Transparent Zcash Extension","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":2,"id":"abstract","text":"Abstract"},{"level":2,"id":"motivation","text":"Motivation"},{"level":2,"id":"requirements","text":"Requirements"},{"level":2,"id":"specification","text":"Specification"},{"level":3,"id":"tze-identification","text":"TZE identification"},{"level":3,"id":"precondition-prefixfree","text":"Precondition (prefix‑free)"},{"level":3,"id":"witness-prefixfree","text":"Witness (prefix‑free)"},{"level":3,"id":"verification","text":"Verification"},{"level":3,"id":"parameter-sets-param_id","text":"Parameter sets (param_id)"},{"level":3,"id":"bounds-consensus","text":"Bounds (consensus)"},{"level":3,"id":"fee-model","text":"Fee model"},{"level":2,"id":"rationale","text":"Rationale"},{"level":2,"id":"privacy-implications","text":"Privacy Implications"},{"level":2,"id":"backward-compatibility","text":"Backward Compatibility"},{"level":2,"id":"deployment","text":"Deployment"},{"level":2,"id":"security-considerations","text":"Security Considerations"},{"level":2,"id":"reference-implementation","text":"Reference Implementation"},{"level":2,"id":"test-vectors","text":"Test Vectors"},{"level":2,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":986,"title":"[#985] Create a ZIP Draft to document Regtest mode","author":"pacu","createdAt":"2025-02-26T23:02:13Z","updatedAt":"2026-02-23T22:23:32Z","url":"https://github.com/zcash/zips/pull/986","diffUrl":"https://github.com/zcash/zips/pull/986.diff","headRef":"regtest-zip-draft","headRepo":"pacu/zips","headSha":"8a137b5f2ab5feeffda93961cfe204869d3bfc89","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"closes #985","zipRefs":[],"draftRefs":["draft-pacu-regtest-local-consensus-definition"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":3,"deletions":0,"patch":"@@ -212,6 +212,7 @@ written.\n     <tr> <td>2005</td> <td class=\"left\"><a href=\"zips/zip-2005.md\">Quantum Recoverability</a></td> <td>Draft</td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1135\">zips#1135</a></td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n+    <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Something Short and To the Point}</a></td> <td>Draft</td> <td class=\"left\"></td>\n   </table></embed>\n \n Drafts without assigned ZIP numbers\n@@ -230,6 +231,7 @@ be deleted.\n     <tr> <td class=\"left\">draft-arya-deploy-nu7</td> <td class=\"left\"><a href=\"zips/draft-arya-deploy-nu7.md\">Deployment of the NU7 Network Upgrade</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/839\">zips#839</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/???\">zips#???</a></td>\n     <tr> <td class=\"left\">draft-ecc-onchain-accountable-voting</td> <td class=\"left\"><a href=\"zips/draft-ecc-onchain-accountable-voting.md\">On-chain Accountable Voting</a></td> <td class=\"left\"></td>\n+    <tr> <td class=\"left\">draft-pacu-regtest-local-consensus-definition</td> <td class=\"left\"><a href=\"zips/draft-pacu-regtest-local-consensus-definition.md\">Regtest: Definition of a Local Consensus Test Mode for Zcash Full Nodes</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-str4d-orchard-balance-proof</td> <td class=\"left\"><a href=\"zips/draft-str4d-orchard-balance-proof.md\">Air drops, Proof-of-Balance, and Stake-weighted Polling</a></td> <td class=\"left\"></td>\n   </table></embed>\n \n@@ -384,4 +386,5 @@ Index of ZIPs\n     <tr> <td>2005</td> <td class=\"left\"><a href=\"zips/zip-2005.md\">Quantum Recoverability</a></td> <td>Draft</td>\n     <tr> <td>guide-markdown</td> <td class=\"left\"><a href=\"zips/zip-guide-markdown.md\">{Something Short and To the Point}</a></td> <td>Draft</td>\n     <tr> <td>guide</td> <td class=\"left\"><a href=\"zips/zip-guide.rst\">{Something Short and To the Point}</a></td> <td>Draft</td>\n+    <tr> <td>template</td> <td class=\"left\"><a href=\"zips/zip-template.md\">{Something Short and To the Point}</a></td> <td>Draft</td>\n   </table></embed>"},{"filename":"zips/draft-pacu-regtest-local-consensus-definition.md","status":"added","additions":462,"deletions":0,"patch":"@@ -0,0 +1,462 @@\n+```\n+  ZIP: Unassigned\n+  Title: Regtest: Definition of a Local Consensus Test Mode for Zcash Full Nodes\n+  Owners: Francisco Gindre <pacu@zecdev.org>\n+  Original-Authors: Francisco Gindre <pacu@zecdev.org>\n+  Credits: Daira-Emma Hopwood\n+           Zancas (Zingo Labs)\n+           idky137 (Zingo Labs)\n+           Kris Nuttycombe\n+  Status: Draft\n+  Category: Informational\n+  Created: 2025-02-26\n+  License: MIT\n+  Pull-Request: <https://github.com/zcash/zips/pull/986>\n+```\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"MAY\",\n+\"REQUIRED\", \"SHALL\", \"SHALL NOT\", \"RECOMMENDED\", \"NOT RECOMMENDED\",\n+and \"OPTIONAL\" in this document are to be interpreted as described in\n+BCP 14 [^BCP14] when, and only when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted\n+as defined in the Zcash protocol specification [^protocol-networks].\n+\n+# Abstract\n+\n+Regtest is a testing mode inherited from the Bitcoin node. It provides a\n+way to have a consensus node on a local network with private state that a\n+developer can control in order to reproduce certain situations\n+deterministically for testing purposes. Regtest, as implemented in zcashd,\n+is similar to Testnet but without miners and remote peers.\n+\n+# Motivation\n+\n+It is necessary to define Regtest mode so that different implementations of\n+Zcash full nodes provide the same capabilities and so that testing\n+infrastructure can be interoperable. Regtest mode was inherited from Bitcoin\n+and never defined in a ZIP. Differences between Zebra and zcashd Regtest\n+implementations found by developers involved in zcashd deprecation motivated\n+this ZIP as a way to document existing assumptions, expectations and\n+functionalities of Regtest, and to document regtest-only features.\n+\n+This ZIP documents differences in behavior, values, expectations and\n+assumptions in comparison to Testnet. It uses the Zcash protocol\n+specification and existing ZIPs as guiding principles for the document\n+structure. It also documents pre-existing and previously *undocumented*\n+requirements, constants and values that are present in the zcashd 6.1.0\n+implementation and `zcash_protocol` crate 0.4.3 [^zcash_protocol].\n+\n+This ZIP is intended for full node implementors but also as a reference\n+for developers and QA testers who use Regtest functionality to ensure test\n+coverage of their codebases.\n+\n+# Requirements\n+\n+The following are the user-facing goals that Regtest mode is intended to\n+satisfy:\n+\n+1. Developers need to be able to efficiently generate deterministic\n+   sequences of blocks and transactions on a local node.\n+2. Consensus checks that would prevent efficient local block generation\n+   (such as proof-of-work difficulty) need to be relaxed or bypassed.\n+3. Developers need to be able to specify the activation heights of Network\n+   Upgrades at launch, via command-line parameters or a configuration file.\n+4. Regtest nodes need to operate in isolation from public networks and\n+   remote peers.\n+5. It should be possible to activate all Network Upgrades at a single\n+   height, while maintaining the invariant that a Network Upgrade is only\n+   active if all of its predecessors are also active.\n+6. Block generation should be available on demand, rather than on a\n+   mining schedule.\n+7. Address encodings should use distinct Human-Readable Parts (HRPs) to\n+   prevent confusion with Mainnet or Testnet addresses.\n+8. Regtest functionality should enable developers to achieve thorough\n+   test coverage of their codebases.\n+\n+# Non-requirements\n+\n+This ZIP does not attempt to address how Regtest mode should be\n+implemented. Details on full-node architecture, implementation details, and\n+software distribution are out of scope.\n+\n+# Specification\n+\n+## Configuration Parameters\n+\n+Regtest mode is activated by starting a node with a `regtest` network\n+identifier. Implementations MUST provide configuration mechanisms for at\n+least the following, which MUST only be accepted when the node is running\n+in Regtest mode:\n+\n+- **Network Upgrade activation heights.** The operator MUST be able to\n+  specify the activation height for each Network Upgrade. Upgrades that\n+  are not explicitly configured SHOULD default to inactive\n+  (`NO_ACTIVATION_HEIGHT`). Implementations SHOULD auto-activate\n+  predecessor upgrades when a later upgrade is configured (see\n+  [Global Effects on Network Upgrades](#global-effects-on-network-upgrades)).\n+\n+- **Funding stream parameters.** The operator MUST be able to configure\n+  funding stream recipients, height ranges, and numerators. Funding\n+  streams SHOULD be disabled by default.\n+\n+- **One-time lockbox disbursement parameters.** The operator MUST be able\n+  to configure lockbox disbursement recipients and heights.\n+\n+- **Coinbase shielding requirement.** The operator SHOULD be able to\n+  toggle whether coinbase outputs are required to be shielded. This\n+  SHOULD default to not required.\n+\n+Implementations MAY expose additional Regtest-only configuration, such as\n+proof-of-work parameter overrides or halving interval adjustments.\n+\n+In zcashd, these are configured via command-line flags: `-nuparams`,\n+`-fundingstream`, `-onetimelockboxdisbursement`, and\n+`-regtestshieldcoinbase`. In zebrad, they are configured via TOML\n+configuration fields under `[network.testnet_parameters]`, including\n+`activation_heights`, `funding_streams`, `lockbox_disbursements`, and the\n+`allow_unshielded_coinbase_spends` builder option.\n+\n+## Regtest Nodes and Network Connections\n+\n+Regtest nodes MUST be configured with no default seed nodes or fixed seeds.\n+Mining MUST NOT require peers. Regtest nodes MUST NOT connect to external\n+peers. Implementations MUST provide mechanisms that restrict connections to\n+localhost.\n+\n+Note: In zcashd, isolation is achieved by clearing `vFixedSeeds` and\n+`vSeeds` and setting `fMiningRequiresPeers = false`, rather than by\n+actively rejecting external connections.\n+\n+## Proof of Work and Block Generation\n+\n+Regtest mode SHOULD use reduced Equihash parameters (N=48, K=5) compared to\n+Mainnet and Testnet (N=200, K=9) to allow rapid block generation.\n+\n+The following proof-of-work settings apply in Regtest mode:\n+\n+- Block generation MUST be available on demand (e.g. via RPC calls such\n+  as `generate` or `generatetoaddress`).\n+- Difficulty retargeting MUST be disabled by default.\n+- Minimum-difficulty blocks MUST be allowed from genesis.\n+\n+Implementations MAY provide a mechanism for overriding proof-of-work\n+parameters at runtime for test scenarios that require specific difficulty\n+behavior. In zcashd, the `UpdateRegtestPow` function serves this purpose.\n+\n+## Subsidy and Halving\n+\n+The pre-Blossom subsidy halving interval for Regtest is 144 blocks\n+(compared to 840,000 on Mainnet), with no slow-start interval. Coinbase\n+outputs are not required to be shielded by default, but implementations\n+SHOULD provide a mechanism to enable this requirement (see\n+[Configuration Parameters](#configuration-parameters)).\n+\n+## Funding Streams\n+\n+Funding streams are not enabled by default on Regtest. They MAY be\n+configured at runtime (see\n+[Configuration Parameters](#configuration-parameters)). Implementations\n+MUST NOT accept funding stream configuration on networks other than\n+Regtest.\n+\n+## Global Effects on Network Upgrades\n+\n+Regtest mode MUST allow Network Upgrades to occur at configurable block\n+heights. One or more Network Upgrades MAY be activated at the same height.\n+\n+Network Upgrades MUST NOT be activated out of order. If a Network Upgrade\n+is active at a given height, all of its predecessors MUST also be active at\n+that height. The ordering of Network Upgrades is:\n+\n+> Sprout → Overwinter → Sapling → Blossom → Heartwood → Canopy → NU5 →\n+> NU6 → NU6.1\n+\n+In zcashd, when a Network Upgrade is explicitly configured via `-nuparams`,\n+all predecessor upgrades that have not been explicitly configured are\n+automatically activated at the same height. For example, specifying only\n+`-nuparams=CANOPY_BRANCH_ID:205 -nuparams=NU5_BRANCH_ID:210` will\n+automatically activate Overwinter, Sapling, Blossom, and Heartwood at\n+height 205. All Network Upgrades other than Sprout default to\n+`NO_ACTIVATION_HEIGHT` (disabled) if not configured.\n+\n+It MUST be possible to configure activation heights for not-yet-released\n+Network Upgrades in order to enable testing of new protocol features in\n+advance of Mainnet or Testnet activation. Client libraries SHOULD also\n+accommodate such flexibility.\n+\n+# Effects of Regtest Mode by ZIP\n+\n+The following table presents the ZIP catalog and a one-line summary of the\n+Regtest changes that apply.\n+\n+| ZIP  | Title | Regtest Behavior |\n+|------|-------|------------------|\n+| 32   | [Shielded Hierarchical Deterministic Wallets](https://zips.z.cash/zip-0032) | No change |\n+| 143  | [Transaction Signature Validation for Overwinter](https://zips.z.cash/zip-0143) | No change |\n+| [155](#behavior-for-zip-155)  | [addrv2 message](https://zips.z.cash/zip-0155) | Addresses MUST only point to localhost |\n+| 173  | [Bech32 Format](https://zips.z.cash/zip-0173) | No change |\n+| [200](#behavior-for-zip-200-network-upgrade-mechanism)  | [Network Upgrade Mechanism](https://zips.z.cash/zip-0200) | See section below |\n+| 201  | [Network Peer Management for Overwinter](https://zips.z.cash/zip-0201) | No change |\n+| 202  | [Version 3 Transaction Format for Overwinter](https://zips.z.cash/zip-0202) | No change |\n+| 203  | [Transaction Expiry](https://zips.z.cash/zip-0203) | No change |\n+| 205  | [Deployment of the Sapling Network Upgrade](https://zips.z.cash/zip-0205) | Configurable activation height |\n+| 206  | [Deployment of the Blossom Network Upgrade](https://zips.z.cash/zip-0206) | Configurable activation height |\n+| [207](#behavior-for-zip-207-funding-streams)  | [Funding Streams](https://zips.z.cash/zip-0207) | Disabled by default; configurable |\n+| [208](#behavior-for-zip-208-shorter-block-target-spacing)  | [Shorter Block Target Spacing](https://zips.z.cash/zip-0208) | See section below |\n+| 209  | [Prohibit Negative Shielded Chain Value Pool Balances](https://zips.z.cash/zip-0209) | Disabled by default; can be enabled via `SetRegTestZIP209Enabled` |\n+| 211  | [Disabling Addition of New Value to the Sprout Chain Value Pool](https://zips.z.cash/zip-0211) | No change |\n+| 212  | [Allow Recipient to Derive Ephemeral Secret from Note Plaintext](https://zips.z.cash/zip-0212) | No change |\n+| 213  | [Shielded Coinbase](https://zips.z.cash/zip-0213) | Coinbase shielding not required by default |\n+| 214  | [Consensus rules for a Zcash Development Fund](https://zips.z.cash/zip-0214) | No change |\n+| 215  | [Explicitly Defining and Modifying Ed25519 Validation Rules](https://zips.z.cash/zip-0215) | No change |\n+| 216  | [Require Canonical Jubjub Point Encodings](https://zips.z.cash/zip-0216) | No change |\n+| 221  | [FlyClient - Consensus-Layer Changes](https://zips.z.cash/zip-0221) | No change |\n+| 224  | [Orchard Shielded Protocol](https://zips.z.cash/zip-0224) | No change |\n+| 225  | [Version 5 Transaction Format](https://zips.z.cash/zip-0225) | No change |\n+| 236  | [Blocks should balance exactly](https://zips.z.cash/zip-0236) | No change |\n+| 239  | [Relay of Version 5 Transactions](https://zips.z.cash/zip-0239) | No change |\n+| 243  | [Transaction Signature Validation for Sapling](https://zips.z.cash/zip-0243) | No change |\n+| 244  | [Transaction Identifier Non-Malleability](https://zips.z.cash/zip-0244) | No change |\n+| 250  | [Deployment of the Heartwood Network Upgrade](https://zips.z.cash/zip-0250) | Configurable activation height |\n+| 251  | [Deployment of the Canopy Network Upgrade](https://zips.z.cash/zip-0251) | Configurable activation height |\n+| 252  | [Deployment of the NU5 Network Upgrade](https://zips.z.cash/zip-0252) | Configurable activation height |\n+| 253  | [Deployment of the NU6 Network Upgrade](https://zips.z.cash/zip-0253) | Configurable activation height |\n+| 300  | [Cross-chain Atomic Transactions](https://zips.z.cash/zip-0300) | No change |\n+| [301](#behavior-for-zip-301-zcash-stratum-protocol)  | [Zcash Stratum Protocol](https://zips.z.cash/zip-0301) | See section below |\n+| 308  | [Sprout to Sapling Migration](https://zips.z.cash/zip-0308) | No change |\n+| [316](#effects-on-zip-316)  | [Unified Addresses and Unified Viewing Keys](https://zips.z.cash/zip-0316) | [Revision 0] Changes to HRP of string encoding, [Revision 1] See section below |\n+| 317  | [Proportional Transfer Fee Mechanism](https://zips.z.cash/zip-0317) | No change |\n+| [320](#effects-on-zip-320-tex-addresses)  | [Defining an Address Type to which funds can only be sent from Transparent Addresses](https://zips.z.cash/zip-0320) | Changes to HRP of string encoding |\n+| 321  | [Payment Request URIs](https://zips.z.cash/zip-0321) | No change |\n+| 401  | [Addressing Mempool Denial-of-Service](https://zips.z.cash/zip-0401) | No change |\n+| [1014](#behavior-for-zip-1014-development-fund) | [Establishing a Dev Fund for ECC, ZF, and Major Grants](https://zips.z.cash/zip-1014) | Funding streams disabled by default; configurable |\n+| 1015 | [Block Subsidy Allocation for Non-Direct Development Funding](https://zips.z.cash/zip-1015) | No change |\n+| 2001 | [Lockbox Funding Streams](https://zips.z.cash/zip-2001) | No change |\n+\n+\n+## Behavior for ZIP-155\n+\n+Peer-to-peer communications on Regtest MUST be restricted to localhost\n+nodes. See [Regtest Nodes and Network Connections](#regtest-nodes-and-network-connections).\n+\n+## Behavior for ZIP-200 Network Upgrade Mechanism\n+\n+It MUST be possible to configure a Regtest node with a mapping between\n+Network Upgrade activation heights and consensus branch IDs. It MUST be\n+possible for such configuration to enable the development of functionality\n+for not-yet-released Network Upgrades, in order to enable testing of new\n+protocol features in advance of Mainnet or Testnet activation. Client\n+libraries SHOULD also accommodate such flexibility.\n+\n+## Behavior for ZIP-207 Funding Streams\n+\n+Funding streams are not enabled by default on Regtest. They MAY be\n+configured at runtime (see\n+[Configuration Parameters](#configuration-parameters)).\n+\n+## Behavior for ZIP-208 Shorter Block Target Spacing\n+\n+In Regtest mode, block generation is performed on demand. Difficulty\n+retargeting is disabled by default. Values referenced by ZIP-208 [^zip-0208]\n+such as `BlossomActivationHeight` MUST be populated with whatever value the\n+Regtest node was configured with.\n+\n+Regtest allows developers to set their own Network Upgrade activation\n+heights, so references to Blossom activation SHOULD use the value present\n+in the node configuration rather than a hardcoded constant.\n+\n+## Behavior for ZIP-301 Zcash Stratum Protocol\n+\n+Regtest nodes MUST NOT expose connections or connect to public networks\n+or other machines on a network. See\n+[Regtest Nodes and Network Connections](#regtest-nodes-and-network-connections).\n+\n+## Behavior for ZIP-1014 Development Fund\n+\n+Funding streams and development fund recipients are not configured by\n+default on Regtest. They MAY be configured at runtime (see\n+[Configuration Parameters](#configuration-parameters)).\n+\n+## Effects on ZIP-316\n+\n+### Revision 0\n+\n+The HRP for Regtest Unified Addresses is defined as `uregtest`. Receivers\n+included inside Regtest UAs MUST also be Regtest variants of their\n+respective type.\n+\n+Example:\n+```\n+uregtest1....\n+|\n+|-> Sapling  -> zregtestsapling1...\n+|\n+|-> Orchard  (encoded within the UA)\n+```\n+\n+### Revision 1\n+\n+From the [Address Expiration Metadata](https://zips.z.cash/zip-0316#address-expiration-metadata)\n+section:\n+\n+> When honoring an Address Expiry Time, the reason that a sender SHOULD\n+> choose a `nExpiryHeight` that is expected to occur within 24 hours of\n+> the time of transaction construction is to, when possible, ensure that\n+> the expiry time is respected to within a day.\n+\n+Time is compressed in Regtest mode because blocks can be generated at a\n+rate of one or more per second. This creates a conflict between expected\n+timespans for address expiration and the actual timestamps of transactions\n+in a Regtest-generated blockchain.\n+\n+This remains an open question. Possible approaches include:\n+\n+A. Regtest manages a separate clock where timestamps appear consistent\n+   with Mainnet's average block spacing (~75 seconds) for scenarios where\n+   time-based behavior is under test.\n+\n+B. Adjust ZIP-316 implementations on Regtest so that the expiration window\n+   can be adjusted according to Regtest block cadence.\n+\n+### Unified Incoming Viewing Keys Encoding\n+\n+UIVKs MUST be encoded with the `uivkregtest` HRP on Regtest.\n+\n+### Unified Full Viewing Keys Encoding\n+\n+UFVKs MUST be encoded with the `uviewregtest` HRP on Regtest.\n+\n+## Effects on ZIP-320 (TEX Addresses)\n+\n+ZIP-320 [^zip-0320] defines an encoding for transparent-source-only\n+addresses with a `tex` HRP. On Regtest, this value MUST be `texregtest`.\n+\n+# Effects of Regtest Mode on Protocol Definitions\n+\n+The following section describes how Regtest mode affects definitions present\n+in the Zcash protocol specification [^protocol].\n+\n+## Constants\n+\n+### Coin Type\n+\n+Regtest reuses Testnet's coin type:\n+\n+```\n+COIN_TYPE: 1\n+```\n+\n+### Sapling Payment Address Encoding\n+\n+From the Zcash protocol specification [^protocol-saplingpaymentaddrencoding]:\n+\n+> For addresses on Mainnet, the Human-Readable Part (as defined in\n+> ZIP 173) is \"zs\". For addresses on Testnet, the Human-Readable Part is\n+> \"ztestsapling\".\n+\n+On Regtest, this MUST be `zregtestsapling`.\n+\n+### Sapling Incoming Viewing Keys Encoding\n+\n+From the Zcash protocol specification [^protocol-saplinginviewingkeyencoding]:\n+\n+> For incoming viewing keys on Mainnet, the Human-Readable Part is\n+> \"zivks\". For incoming viewing keys on Testnet, the Human-Readable Part\n+> is \"zivktestsapling\".\n+\n+On Regtest, this MUST be `zivkregtestsapling`.\n+\n+### Sapling Full Viewing Keys Encoding\n+\n+From the Zcash protocol specification [^protocol-saplingfullviewingkeyencoding]:\n+\n+> For full viewing keys on Mainnet, the Human-Readable Part is \"zviews\".\n+> For full viewing keys on Testnet, the Human-Readable Part is\n+> \"zviewtestsapling\".\n+\n+On Regtest, this MUST be `zviewregtestsapling`.\n+\n+### Sapling Extended Spending Keys Encoding\n+\n+These keys are defined in ZIP 32 [^zip-0032]. On Regtest, the HRP\n+encoding MUST be `secret-extended-key-regtest`.\n+\n+### Sapling Extended Full Viewing Keys Encoding\n+\n+These keys are defined in ZIP 32 [^zip-0032]. On Regtest, the HRP\n+encoding MUST be `zxviewregtestsapling`.\n+\n+### Transparent Address Public Key Hash Base58 Prefix\n+\n+The prefix for a Base58Check-encoded Regtest transparent `PublicKeyHash`\n+MUST be the same as the Testnet prefix:\n+\n+```\n+B58_PUBKEY_ADDRESS_PREFIX: [0x1d, 0x25]\n+```\n+\n+### Transparent Address Script Hash Base58 Prefix\n+\n+The prefix for a Base58Check-encoded Regtest transparent `ScriptHash`\n+MUST be the same as the Testnet prefix:\n+\n+```\n+B58_SCRIPT_ADDRESS_PREFIX: [0x1c, 0xba]\n+```\n+\n+### Sprout Payment Address Encoding\n+\n+The Zcash protocol specification [^protocol-sproutpaymentaddrencoding]\n+defines:\n+\n+> Two bytes [0x16, 0x9A], indicating this version of the raw encoding of\n+> a Sprout shielded payment address on Mainnet. (Addresses on Testnet use\n+> [0x16, 0xB6] instead.)\n+\n+Regtest uses the same prefix as Testnet: `[0x16, 0xB6]`.\n+\n+# Rationale\n+\n+Regtest mode has existed since the early days of Zcash as inherited\n+Bitcoin functionality. It was never formally specified because its primary\n+consumers were internal developers and automated tests. As additional full\n+node implementations (such as Zebra) have emerged, the lack of a formal\n+specification has led to inconsistencies that affect developers who rely on\n+Regtest for testing wallets, libraries, and other ecosystem tools.\n+\n+The auto-fill behavior for predecessor Network Upgrades (where specifying a\n+later upgrade automatically activates all predecessors) is documented as a\n+convenience feature. Implementations MAY choose a different mechanism for\n+ensuring upgrade ordering, provided that the invariant that predecessor\n+upgrades are active whenever a successor upgrade is active is maintained.\n+\n+# Security and Privacy Considerations\n+\n+Regtest mode is intended exclusively for local testing. Because Regtest\n+nodes are intended to operate on isolated local networks, the security\n+properties of Mainnet and Testnet (such as resistance to Sybil attacks\n+or privacy guarantees of the peer-to-peer network) do not apply.\n+\n+Developers SHOULD NOT use Regtest mode for any purpose involving real\n+funds. Regtest uses reduced proof-of-work parameters (Equihash N=48, K=5)\n+that provide no meaningful security against block forgery.\n+\n+The address encodings defined in this ZIP (using `regtest`-prefixed HRPs)\n+are designed to prevent accidental cross-network address reuse between\n+Regtest and Mainnet.\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+[^protocol]: [Zcash Protocol Specification, Version 2024.5.1 or later](protocol/protocol.pdf)\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2024.5.1. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+[^protocol-saplingpaymentaddrencoding]: [Zcash Protocol Specification, Version 2024.5.1. Section 5.6.4: Sapling Payment Address](protocol/protocol.pdf#saplingpaymentaddrencoding)\n+[^protocol-saplinginviewingkeyencoding]: [Zcash Protocol Specification, Version 2024.5.1. Section 5.6.4.1: Sapling Incoming Viewing Key](protocol/protocol.pdf#saplinginviewingkeyencoding)\n+[^protocol-saplingfullviewingkeyencoding]: [Zcash Protocol Specification, Version 2024.5.1. Section 5.6.4.2: Sapling Full Viewing Key](protocol/protocol.pdf#saplingfullviewingkeyencoding)\n+[^protocol-sproutpaymentaddrencoding]: [Zcash Protocol Specification, Version 2024.5.1. Section 5.6.3: Sprout Payment Address](protocol/protocol.pdf#sproutpaymentaddrencoding)\n+[^zcash_protocol]: [zcash_protocol Rust crate 0.4.3](https://docs.rs/zcash_protocol/0.4.3)\n+[^zip-0032]: [ZIP 32: Shielded Hierarchical Deterministic Wallets](https://zips.z.cash/zip-0032)\n+[^zip-0320]: [ZIP 320: Defining an Address Type to which funds can only be sent from Transparent Addresses](https://zips.z.cash/zip-0320)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-pacu-regtest-local-consensus-definition.md","title":"[#985] Create a ZIP Draft to document Regtest mode","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"configuration-parameters","text":"Configuration Parameters"},{"level":2,"id":"regtest-nodes-and-network-connections","text":"Regtest Nodes and Network Connections"},{"level":2,"id":"proof-of-work-and-block-generation","text":"Proof of Work and Block Generation"},{"level":2,"id":"subsidy-and-halving","text":"Subsidy and Halving"},{"level":2,"id":"funding-streams","text":"Funding Streams"},{"level":2,"id":"global-effects-on-network-upgrades","text":"Global Effects on Network Upgrades"},{"level":1,"id":"effects-of-regtest-mode-by-zip","text":"Effects of Regtest Mode by ZIP"},{"level":2,"id":"behavior-for-zip-155","text":"Behavior for ZIP-155"},{"level":2,"id":"behavior-for-zip-200-network-upgrade-mechanism","text":"Behavior for ZIP-200 Network Upgrade Mechanism"},{"level":2,"id":"behavior-for-zip-207-funding-streams","text":"Behavior for ZIP-207 Funding Streams"},{"level":2,"id":"behavior-for-zip-208-shorter-block-target-spacing","text":"Behavior for ZIP-208 Shorter Block Target Spacing"},{"level":2,"id":"behavior-for-zip-301-zcash-stratum-protocol","text":"Behavior for ZIP-301 Zcash Stratum Protocol"},{"level":2,"id":"behavior-for-zip-1014-development-fund","text":"Behavior for ZIP-1014 Development Fund"},{"level":2,"id":"effects-on-zip-316","text":"Effects on ZIP-316"},{"level":3,"id":"revision-0","text":"Revision 0"},{"level":3,"id":"revision-1","text":"Revision 1"},{"level":3,"id":"unified-incoming-viewing-keys-encoding","text":"Unified Incoming Viewing Keys Encoding"},{"level":3,"id":"unified-full-viewing-keys-encoding","text":"Unified Full Viewing Keys Encoding"},{"level":2,"id":"effects-on-zip-320-tex-addresses","text":"Effects on ZIP-320 (TEX Addresses)"},{"level":1,"id":"effects-of-regtest-mode-on-protocol-definitions","text":"Effects of Regtest Mode on Protocol Definitions"},{"level":2,"id":"constants","text":"Constants"},{"level":3,"id":"coin-type","text":"Coin Type"},{"level":3,"id":"sapling-payment-address-encoding","text":"Sapling Payment Address Encoding"},{"level":3,"id":"sapling-incoming-viewing-keys-encoding","text":"Sapling Incoming Viewing Keys Encoding"},{"level":3,"id":"sapling-full-viewing-keys-encoding","text":"Sapling Full Viewing Keys Encoding"},{"level":3,"id":"sapling-extended-spending-keys-encoding","text":"Sapling Extended Spending Keys Encoding"},{"level":3,"id":"sapling-extended-full-viewing-keys-encoding","text":"Sapling Extended Full Viewing Keys Encoding"},{"level":3,"id":"transparent-address-public-key-hash-base58-prefix","text":"Transparent Address Public Key Hash Base58 Prefix"},{"level":3,"id":"transparent-address-script-hash-base58-prefix","text":"Transparent Address Script Hash Base58 Prefix"},{"level":3,"id":"sprout-payment-address-encoding","text":"Sprout Payment Address Encoding"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"security-and-privacy-considerations","text":"Security and Privacy Considerations"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":1187,"title":"offer userns=keep-id podman based build option","author":"zancas","createdAt":"2026-02-20T22:02:27Z","updatedAt":"2026-02-20T22:02:41Z","url":"https://github.com/zcash/zips/pull/1187","diffUrl":"https://github.com/zcash/zips/pull/1187.diff","headRef":"podman_option","headRepo":"zingolabs/zips","headSha":"e34ec2abab6e299932a499fb796dc6676ed06f3d","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Fixes: #1186","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":"README.template","status":"modified","additions":19,"deletions":0,"patch":"@@ -43,6 +43,25 @@ and double-check the generated ``rendered/draft-*.html`` file before filing a Pu\n See `here <protocol/README.rst>`__ for the project dependencies.\n \n \n+Building with Podman\n+~~~~~~~~~~~~~~~~~~~~\n+\n+To build the rendered HTML without installing dependencies locally, use\n+`Podman <https://podman.io/>`__ with the included ``Dockerfile``:\n+\n+.. code::\n+\n+   podman build -t zcash-zips-render .\n+   podman run --rm --userns=keep-id -v \"$(pwd):/zips\" zcash-zips-render\n+\n+The ``--userns=keep-id`` flag ensures that rendered files are owned by your\n+user rather than root. To render only the ZIPs (skipping protocol PDFs):\n+\n+.. code::\n+\n+   podman run --rm --userns=keep-id -v \"$(pwd):/zips\" zcash-zips-render all-zips\n+\n+\n Settled Mainnet Network Upgrade\n -------------------------------\n "},{"filename":"render-via-docker.sh","status":"modified","additions":2,"deletions":2,"patch":"@@ -3,5 +3,5 @@ set -efuxo pipefail\n \n TAG='zcash-zips-render'\n \n-docker build -t \"$TAG\" .\n-docker run -v \"$(pwd):/zips\" \"$TAG\"\n+podman build -t \"$TAG\" .\n+podman run --rm --userns=keep-id -v \"$(pwd):/zips\" \"$TAG\""}],"filesTruncated":false,"preview":null},{"number":1109,"title":"use URL references to protocol from README","author":"zancas","createdAt":"2025-10-16T21:01:13Z","updatedAt":"2026-02-10T21:39:26Z","url":"https://github.com/zcash/zips/pull/1109","diffUrl":"https://github.com/zcash/zips/pull/1109.diff","headRef":"URL_ref_protocol","headRepo":"zingolabs/zips","headSha":"9b4d5aafa0e5b54fb40bcc36ec0652aaa744f4d8","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":2,"deletions":2,"patch":"@@ -28,8 +28,8 @@ Participation in the Zcash project is subject to a `Code of\n Conduct <https://github.com/zcash/zcash/blob/master/code_of_conduct.md>`__.\n \n The Zcash protocol is documented in its\n-`Protocol Specification <rendered/protocol/protocol.pdf>`__\n-`(dark mode version) <rendered/protocol/protocol-dark.pdf>`__.\n+`Protocol Specification <https://zips.z.cash/protocol/protocol.pdf>`__\n+`(dark mode version) <https://zips.z.cash/protocol/protocol-dark.pdf>`__.\n \n To start contributing, first read `ZIP 0 <zips/zip-0000.rst>`__ which documents the ZIP process.\n Then clone `this repo <https://github.com/zcash/zips>`__ from GitHub, and start adding"}],"filesTruncated":false,"preview":null},{"number":1112,"title":"[ZIP TBD]: draft-coinholder-directed-grants.md","author":"aquietinvestor","createdAt":"2025-10-21T13:43:24Z","updatedAt":"2026-01-23T22:41:04Z","url":"https://github.com/zcash/zips/pull/1112","diffUrl":"https://github.com/zcash/zips/pull/1112.diff","headRef":"aquietinvestor-coinholder-grant-program","headRepo":"aquietinvestor/zips","headSha":"f795d42e9c39af62ff7215004e609936a2c70396","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This PR adds a draft process ZIP for the Coinholder-Directed Retroactive Grants Program","zipRefs":[],"draftRefs":["draft-aquietinvestor-coinholder-directed-grants"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -225,6 +225,7 @@ be deleted.\n \n   <embed><table>\n     <tr> <th>Draft name</th> <th>Title</th> <th>Discussions-To</th> </tr>\n+    <tr> <td class=\"left\">draft-aquietinvestor-coinholder-directed-grants</td> <td class=\"left\"><a href=\"zips/draft-aquietinvestor-coinholder-directed-grants.md\">Coinholder-Directed Retroactive Grants Program</a></td> <td class=\"left\"></td>\n     <tr> <td class=\"left\">draft-arya-dairaemma-disable-addition-of-transparent-chain-value</td> <td class=\"left\"><a href=\"zips/draft-arya-dairaemma-disable-addition-of-transparent-chain-value.md\">Disabling Addition of New Value to the Transparent Chain Value Pool</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/1115\">zips#1115</a></td>\n     <tr> <td class=\"left\">draft-arya-deploy-nu7</td> <td class=\"left\"><a href=\"zips/draft-arya-deploy-nu7.md\">Deployment of the NU7 Network Upgrade</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/839\">zips#839</a></td>\n     <tr> <td class=\"left\">draft-ecc-authenticated-reply-addrs</td> <td class=\"left\"><a href=\"zips/draft-ecc-authenticated-reply-addrs.md\">Authenticated Reply Addresses</a></td> <td class=\"left\"><a href=\"https://github.com/zcash/zips/issues/???\">zips#???</a></td>"},{"filename":"zips/draft-aquietinvestor-coinholder-directed-grants.md","status":"added","additions":359,"deletions":0,"patch":"@@ -0,0 +1,359 @@\n+    ZIP: Unassigned\n+    Title: Coinholder-Directed Retroactive Grants Program\n+    Owners: Jason McGee <jason@shieldedlabs.net>\n+            Mark Henderson <mark@shieldedlabs.net>\n+    Credits: Alex Bornstein\n+             Josh Swihart\n+             outgoing.doze\n+             Jack Grigg\n+             Daira-Emma Hopwood\n+             Kris Nuttycombe\n+    Status: Draft\n+    Category: Process / Standards\n+    Created: 2025-10-14\n+    License: MIT\n+    Pull Request: <https://github.com/zcash/zips/pull/1112>\n+\n+\n+# Terminology\n+\n+The key words \"MUST\", \"SHALL\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document\n+are to be interpreted as described in BCP 14 [^BCP14] when, and only when, they\n+appear in all capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol\n+Specification. [^protocol]\n+\n+The terms “Mainnet” and “Testnet” are to be interpreted as described in\n+§ 3.12 ‘Mainnet and Testnet’. [^protocol-networks]\n+\n+The terms “Electric Coin Company”, “Bootstrap Project”, and “Zcash Foundation”\n+in this document are to be interpreted as described in ZIP 1014 [^zip-1014].\n+\n+The terms “Zcash Community Grants” and “Deferred Dev Fund Lockbox” in this\n+document are to be interpreted as defined in ZIP 1015 [^zip-1015].\n+\n+The terms “Key-Holder Organizations” and “Shielded Labs” in this document are to\n+be interpreted as defined in ZIP 1016 [^zip-1016].\n+\n+“Coinholder Grants Program” refers to the 12% funding stream and multisig-controlled treasury defined in ZIP 1016.\n+\n+“Retroactive Grants Program” refers to the operational process defined in this ZIP.\n+\n+“Administrator” refers to an organization to be designated prior to activation of this ZIP, which SHALL administer the Coinholder Grants Program.\n+\n+“Voting Authorities” are operators of the BFT voting chain used to collect shielded ballots.\n+\n+“Incoming Viewing Key (IVK)” and “Full Viewing Key (FVK)” have the meanings used in the Orchard protocol. The IVK reveals incoming transaction values and memo data, but does not reveal any information about when outputs are spent.\n+\n+“ZEC” refers to the native currency of Zcash on Mainnet.\n+\n+“Cycle” refers to a single iteration of the quarterly process of grant submission,\n+voting, and grant disbursement.\n+\n+\n+# Abstract\n+\n+This ZIP defines how the Coinholder Grants Program established in ZIP 1016 [^zip-1016] operates. It specifies a retroactive funding model, Administrator responsibilities, shielded and transparent voting procedures, participation thresholds, funding prioritization, partial funding, and payment execution by Key-Holder Organizations.\n+\n+\n+# Motivation\n+\n+ZIP 1015 [^zip-1015] introduced a temporary, non-direct funding model that directed 12% of block rewards to a protocol lockbox and 8% to Zcash Community Grants.\n+\n+ZIP 1016 extends this by defining the Community and Coinholder Funding Model, allocating 12% of block rewards to a Coinholder Grants Program and 8% to Zcash Community Grants through Zcash’s third halving.\n+\n+While ZIP 1016 establishes a funding stream for coinholder-directed grants, it does not define how proposals are submitted, reviewed, voted, prioritized, or paid. This ZIP provides that missing operational process so that the Coinholder Grants Program can function as intended.\n+\n+\n+# Privacy Implications\n+\n+TBD\n+\n+\n+# Requirements\n+\n+The design of this ZIP is constrained to align with the coinholder decision-making mechanisms established in ZIP 1016.\n+\n+The process described in this ZIP should describe how proposals are submitted, how voting is performed, how voting outcomes are determined, and how disbursements are made.\n+\n+The roles of the Administrator, Key-Holder Organizations, Voting Authorities, and coinholders are expected to be clearly defined and distinguishable.\n+\n+This ZIP should describe Key-Holder responsibilities for custody, security, and legal compliance in a clear and understandable manner.\n+\n+The design is constrained to remain compatible with ZIP 1016 and with the NU 6.1 funding specification that governs key custody.\n+\n+The process described in this ZIP is intended to operate on a quarterly cycle, with recurring rounds for proposal submission, review, voting, and disbursement.\n+\n+This ZIP should not modify other Zcash funding programs or governance structures.\n+\n+This ZIP must not require any changes to Zcash consensus.\n+\n+\n+# Specification\n+\n+## Program Structure and Custody\n+\n+The Coinholder Grants Program SHALL be administered by an organization to be designated prior to activation, referred to as the Administrator.\n+\n+Key-Holder Organizations MUST execute disbursements according to ZIP 1016’s disbursment process [^zip-1016-disbursement-process] when grant proposals are approved by the process described in this ZIP.\n+\n+The Coinholder Grants Program MUST fund only grants for work that is already completed and has been publicly verified. Such a grant for completed work is referred to as a \"retroactive grant\".\n+\n+The Administrator MUST maintain publicly accessible resources including program rules, submission formats, timelines, voting instructions, and historical reports.\n+\n+## Administrator Duties\n+\n+The Administrator MUST operate the proposal submission workflow, perform eligibility screening, and publish qualified proposals.\n+\n+The Administrator MUST provide written guidance on voting processes, including registration windows, voting mechanics, ballot formats, deadlines, and audit instructions.\n+\n+The Administrator MUST publish proposal lists, voting parameters, tallies, participation metrics, and disbursement results for each cycle.\n+\n+The Administrator MUST retain records sufficient to reproduce vote tallies and payment reports.\n+\n+The Administrator MAY appoint a delegate to assist with executing any part of the quarterly process, including proposal administration, voting logistics, tally verification, and reporting.\n+\n+## KYC and Compliance\n+\n+The Administrator MUST perform KYC verification for any grant exceeding 50,000 USD (or ZEC equivalent at disbursement) prior to payment.\n+\n+The Administrator MUST comply with applicable legal, tax, and reporting requirements.\n+\n+## Voting Methods\n+\n+Two voting methods are supported: shielded voting and transparent voting.\n+\n+### Shielded Voting\n+\n+Coinholders should vote using the dedicated Coin Voting 2.0 [^zcash-vote-app] application, which uses Orchard addresses and the Halo2 proof system to verify balances and prevent double voting [^zcash-vote-doc]. In the future, the coin voting process should be rigorously specified, at which point any application that correctly implements that specification may be used to vote.\n+\n+#### Collecting Votes\n+\n+The shielded voting protocol has a registration window corresponding to a defined block range. Eligibility is determined by Orchard notes created within this block range.\n+\n+The Administrator MUST choose a registration window of at least 10 days.\n+\n+The Administrator MUST publish voting guidance to coinholders. This guidance SHOULD include advising participants to move funds out of any wallet used for voting before entering a seed phrase into the Coin Voting 2.0 application, in order to reduce risk if the third-party application is compromised.\n+\n+Votes MUST be submitted to a BFT voting chain operated by at least four independent Voting Authorities. The rationale for this threshold is that if at least two thirds of Voting Authorities are honest, vote suppression or manipulation becomes infeasible.\n+\n+#### Counting Votes\n+\n+The Administrator MUST collect and publish finalized tallies.\n+\n+TBD: specify how shielded voting is counted.\n+\n+#### Vote Auditing\n+\n+Independent auditors MAY verify the results using the published election data.\n+\n+TBD: specify how shielded voting is audited.\n+\n+### Transparent Voting\n+\n+\n+#### Signing protocol\n+\n+The signing protocol used for transparent voting is the \"legacy address signatures\" signature scheme implemented by Satoshi in Bitcoin Core. We specify it here in the absence of a formal specification anywhere else.\n+\n+Let $\\mathsf{SerializeString}(s) = \\mathsf{WriteCompactSize}(\\mathsf{length}(s)) \\,||\\, s$\n+\n+Let $\\mathsf{MessageHash}(\\mathsf{msg}) = \\textsf{SHA-256}(\\textsf{SHA-256}(\\mathsf{SerializeString}(\\texttt{“Zcash Signed Message:”} \\,||\\, [\\mathtt{0x0A}]) \\,||\\, \\mathsf{SerializeString}(\\mathsf{msg})))$\n+\n+Let $\\mathsf{Base64Encode}$ denote Base 64 Encoding as specified in [^base64], i.e. with the standard alphabet and '=' padding.\n+\n+$\\mathsf{LegacySignMessage}(\\mathsf{msg}, \\mathsf{addr})$ is the following algorithm:\n+- Let $\\mathsf{msg}$ be the message to be signed, encoded as US-ASCII.\n+- Let $\\mathsf{addr}$ be a transparent P2PKH address.\n+- Fetch the secp256k1 public key $\\mathsf{pk}$ corresponding to $\\mathsf{addr}$. (The wallet controlling $\\mathsf{addr}$ should be tracking sufficient information to trivially derive it.)\n+- Fetch the secp256k1 private key $\\mathsf{sk}$ corresponding to $\\mathsf{pk}$.\n+- Let $\\mathsf{msg\\_hash} = \\mathsf{MessageHash}(\\mathsf{msg})$\n+- Let $\\mathsf{sig} = \\mathsf{secp256k1\\_ecdsa\\_sign\\_recoverable}(\\mathsf{msg\\_hash}, \\mathsf{sk}, \\mathsf{secp256k1\\_nonce\\_function\\_rfc6979})$\n+- Let $(\\mathsf{recid}, \\mathsf{sig\\_bytes}) = \\mathsf{secp256k1\\_ecdsa\\_recoverable\\_signature\\_serialize\\_compact}(\\mathsf{sig})$\n+- Verify that $\\mathsf{recid}$ is set appropriately (TODO: be more precise).\n+- Return $\\mathsf{Base64Encode}([27 + \\mathsf{recid} + (\\mathsf{pk.is\\_compressed} \\;?\\; 4 : 0)] \\,||\\, \\mathsf{sig\\_bytes})$\n+   - (TODO: try and find where this encoding comes from, and reference it. SEC-1 perhaps? IEEE Std 1363-2000? This is a 3-bit flag added to $27 = \\mathtt{0b11011}$)\n+\n+$\\mathsf{LegacyVerifyMessage}(\\mathsf{msg}, \\mathsf{addr}, \\mathsf{signature})$ is the following algorithm:\n+- Let $\\mathsf{msg}$ be the message to be verified, encoded as US-ASCII.\n+- Let $\\mathsf{addr}$ be a transparent P2PKH address.\n+- If the signature is not a strictly valid Base 64 Encoding, fail verification; otherwise let $\\mathsf{sig\\_bytes} = \\mathsf{Base64Decode}(\\mathsf{signature})$.\n+- Check that $\\mathsf{length}(\\mathsf{sig\\_bytes}) = 65$, otherwise fail verification.\n+- Let $\\mathsf{msg\\_hash} = \\mathsf{MessageHash}(\\mathsf{msg})$\n+- Let $\\mathsf{recid} = (\\mathsf{sig\\_bytes}[0] - 27) \\;\\&\\; \\mathtt{0b011}$\n+- Let $\\mathsf{pk\\_is\\_compressed} = ((\\mathsf{sig\\_bytes}[0] - 27) \\;\\&\\; \\mathtt{0b100}) \\neq 0$\n+- Let $\\mathsf{sig} = \\mathsf{secp256k1\\_ecdsa\\_recoverable\\_signature\\_parse\\_compact}(\\mathsf{sig\\_bytes}[1..], \\mathsf{recid})$, and fail verification if this errors.\n+- Let $\\mathsf{raw\\_pk} = \\mathsf{secp256k1\\_ecdsa\\_recover}(\\mathsf{msg\\_hash}, \\mathsf{sig})$, and fail verification if this errors.\n+- Let $\\mathsf{pk} = \\mathsf{secp256k1\\_ec\\_pubkey\\_serialize}(\\mathsf{raw\\_pk}, \\mathsf{pk\\_is\\_compressed})$\n+- Pass verification if $\\mathsf{addr}$ is the transparent P2PKH address encoding of $\\textsf{RIPEMD-160}(\\textsf{SHA-256}(\\mathsf{pk}))$ as defined in [^protocol-transparentaddrencoding], otherwise fail verification.\n+\n+#### Collecting Votes\n+\n+Before accepting votes, the Administrator MUST publish an Orchard-only IVK designated for receiving vote messages and payments. The corresponding FVK MUST NOT be published.\n+\n+To create a transparent vote, Voters do the following:\n+\n+- Select a transparent P2PKH address $\\mathsf{addr}$ to use for voting.\n+- Ensure that $\\mathsf{addr}$ contains the intended balance of funds as of the end of the registration window.\n+- Construct a US-ASCII-encoded voting message $\\mathsf{msg}$, using the template provided by the Administrator. It MUST NOT be longer than $387$ bytes.\n+- Let $\\mathsf{sig} = \\mathsf{LegacySignMessage}(\\mathsf{msg}, \\mathsf{addr})$. This can be produced via the `zcashd` `signmessage` RPC call, or using a Trezor device following the procedure described in [^trezor-sign-and-verify].\n+- Construct a vote-casting text $\\mathsf{memo} = \\mathsf{msg} \\,||\\, [\\mathtt{0x0A}] \\,||\\, \\mathsf{addr} \\,||\\, [\\mathtt{0x0A}] || \\mathsf{sig}$\n+   - Previously this was ambiguous as to how many LF characters (or other whitespace) separated the fields, or whether additional LF characters were allowed.\n+- Send $\\mathsf{memo}$ along with a payment of at least 1 ZEC to the address associated with the published IVK.\n+\n+TBD: finish the specification, including but not limited to:\n+- The address derivation to be used.\n+- The structure of transaction to be created (what kinds of outputs, how many, to what recipients, with what values).\n+\n+#### Counting Votes\n+\n+TBD: specify how transparent votes are counted.\n+\n+The Administrator MUST ignore submissions:\n+\n+- with a payment threshold chosen by the Administrator.\n+- where the Voter moves the submitted funds before voting concludes, as specified in the Quarterly Timeline below.\n+\n+TBD: clarify specification of \"voting concludes\"; if you mean \"at the end of the Coinholder Vote on Proposals phase\" then say this instead of relying on the indirect definition.\n+\n+#### Vote Auditing\n+\n+The Administrator MUST publish IVK-based audit data and all received vote messages in order to allow verification.\n+\n+TBD: specify how transparent votes are verified.\n+\n+## Voting Process\n+\n+The Administrator MUST publish relevant parameters for each voting method for each cycle, and provide sufficient information for independent auditing.\n+\n+TBD: specify the rest of the overall voting process.\n+\n+## Participation and Voting Thresholds\n+\n+A proposal MUST have at least 420,000 ZEC participating to be eligible for approval, as specifed in ZIP 1016. [^zip-1016]\n+\n+A proposal is approved if a simple majority of participating votes are “Yes”.\n+\n+Each proposal vote is independent, and coins MAY be reused across proposals in the same cycle.\n+\n+## Proposal Funding and Disbursement\n+\n+After votes are complete, the Administrator MUST publish final voting results within seven days. The results MUST include total participation, per-proposal tallies, and data necessary for independent auditing.\n+\n+The Administrator MUST produce a ranked list of approved proposals based on the percentage of “Yes” votes.\n+\n+Funds MUST be allocated starting from the highest-ranked approved proposal until the treasury is exhausted.\n+\n+If insufficient funds remain to fully fund the next approved proposal, the Key-Holder Organizations MAY partially fund it up to the remaining balance.\n+\n+Approved proposals not funded or partially funded MAY be resubmitted in future quarters.\n+\n+Key-Holder Organizations MUST execute payments using the NU 6.1 multisignature scheme and MUST publish transaction identifiers.\n+\n+The Administrator MUST publish a disbursement report detailing allocated amounts, partial disbursements, and executed transactions.\n+\n+## Quarterly Timeline\n+\n+| Phase                        | Approximate Duration  | Description |\n+|------------------------------|-----------------------|-------------|\n+| Public Call for Proposals    | ~ 1 month             | The Administrator or its delegate announces an open call via community channels. Applicants prepare and submit completed-work proposals for consideration. |\n+| Review and Discussion Period | ≥ 30 days             | Proposals are publicly discussed and evaluated. The Administrator or its delegate reviews submissions for eligibility and publishes a final list of qualified proposals. |\n+| Registration Period          | ≥ 10 days             | Coinholders register their balances for voting by creating eligible Orchard notes within a defined block range (as described in Section 4.1). |\n+| Coinholder Vote on Proposals | ~ 2 weeks             | Coinholders vote on eligible proposals using the process defined in Section 4. The Administrator or its delegate verifies and tallies the results. |\n+| Vote Tallying                | After voting concludes | The Administrator or its delegate tallies all votes. For shielded voting, tallying is performed using the Coin Voting 2.0 Audit Tool [^zcash-vote-audit] to aggregate verified ballots submitted to the voting chain. For transparent voting, tallying is performed by verifying signed messages and corresponding transparent payments received at the published Orchard address using the IVK. Shielded and transparent results are combined into a unified tally prior to publication. |\n+| Funding Disbursements        | —                     | After final tallies are published, the Administrator or its delegate coordinates with Key-Holder Organizations to execute approved payments from the multisig wallet. |\n+\n+## Security Precautions\n+\n+Key-Holder Organizations MUST maintain custody according to the ZIP 271 funding specification and ZIP 1016.\n+\n+Any theft or compromise of funds or key material MUST be reported promptly after mitigation.\n+\n+\n+# Rationale\n+\n+TBD: add missing rationale for:\n+- Why the transparent voting FVK must not be published.\n+- The requirement for, and bound on, the payment amount for transparent voting.\n+- The requirement that transparent voters not move their funds until voting concludes.\n+\n+\n+# Historic Context: How the transparent voting process was run in the last vote\n+\n+Every election, Jason posts something like this:\n+\n+>Transparent ZEC\n+>\n+> If you want to vote ZEC held in a transparent address, follow the instructions and example outlined by @outgoing.doze:\n+>\n+>> To protect the anonymity of wallet holders, I suggest votes (signed messages) are sent to a shielded wallet where the viewing key is shared with the community so anyone can review the votes. The character limit being 512 if not mistaken, should be plenty. The format could be as simple as this:\n+>>\n+>>     $voteDetails\n+>>\n+>>     $address\n+>>\n+>>     $signature\n+>>\n+>> The extra line in between will make it easier easier to review visually.\n+>>\n+>> Example (ignore invalid signature):\n+>>\n+>>     1A; 2E Let’s make more Zcash Swag; 3Y\n+>>\n+>>     t1VydNnkjBzfL1iAMyUbwGKJAF7PgvuCfMY\n+>>\n+>>     ICrKSQjLORZP/aUTluyf2sZXXK+HuKtxdBLt2RRCn2j5CxgZlccNmiMC2K104JuhHnvHd5cXgSzdZtGh9vgWAYA=\n+>>\n+>> To protect from a Sybil attack, I suggest a payment of 0.1 ZEC is sent along with the vote. Anything less than 0.1 ZEC would therefore be ignored.\n+>>\n+>> The wallet would be generated and controlled by a trusted member of the community, same person that would share the viewing key with the community.\n+>\n+> Orchard Address:\n+>     ADDRESS\n+>\n+> View Key:\n+>     VK\n+>\n+> Birthday Height:\n+>     HEIGHT\n+>\n+> Note: Funds cannot be moved during the voting period, which runs until Tuesday, November 25, at 11:59 PM UTC.\n+\n+Jason then pulled up the transparent address received in each voting message on a block explorer (Blockchair) and looked at when the last activity was. If there was no activity since the defined registration window ended, then the vote was accepted, and he trusted that the balance in the address (as reported by the block explorer) was the voting amount corresponding to the received message.\n+\n+dismad verified signatures in zcashd with:\n+\n+> zcash-cli verifymessage \"t1VydNnkjBzfL1iAMyUbwGKJAF7PgvuCfMY\" \"IGobRVUtwsL92th70UFj0bGztv701evq/uHVuoTTceWsRjWeasSdvMkNUR43gSpMo393yixoFMiTQqnYh3y0b5U=\" \"Q1:N; Q2:N; Q3:Y (20250903)\" true\n+\n+and in Trezor following the instructions in [^trezor-sign-and-verify].\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^base64]: [RFC 4658: The Base16, Base32, and Base64 Data Encodings. Section 4: Base 64 Encoding](https://www.rfc-editor.org/rfc/rfc4648.html#section-4)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2025.6.1 [NU 6.1] or later](protocol/protocol.pdf)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2025.6.1 [NU 6.1]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^protocol-transparentaddrencoding]: [Zcash Protocol Specification, Version 2025.6.1 [NU 6.1]. Section 5.6.1.1: Transparent Addresses](protocol/protocol.pdf#transparentaddrencoding)\n+\n+[^zip-0271]: [ZIP 271: Deferred Dev Fund Lockbox Disbursement](zip-0271.md)\n+\n+[^zip-1014]: [ZIP 1014: Establishing a Dev Fund for ECC, ZF, and Major Grants](zip-1014.rst)\n+\n+[^zip-1015]: [ZIP 1015: Block Subsidy Allocation for Non-Direct Development Funding](zip-1015.rst)\n+\n+[^zip-1016]: [ZIP 1016: Community and Coinholder Funding Model](zip-1016.md)\n+\n+[^zip-1016-disbursement-process]: [ZIP 1016: Community and Coinholder Funding Model — Disbursement Process](zip-1016.md#disbursementprocess)\n+\n+[^zcash-vote-app]: [Coin Voting 2.0 Application (zcash-vote-app)](https://github.com/hhanh00/zcash-vote-app)\n+\n+[^zcash-vote-doc]: [Coin Voting 2.0 Technical Documentation (Audit Book)](https://hhanh00.github.io/coin-voting-book/)\n+\n+[^zcash-vote-audit]: [Coin Voting 2.0 Audit Tool](https://github.com/hhanh00/zcash-vote-audit)\n+\n+[^trezor-sign-and-verify]: [Trezor documentation: Sign & Verify messages in Trezor Suite](https://trezor.io/learn/supported-assets/bitcoin/sign-and-verify-messages-trezor-suite)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-aquietinvestor-coinholder-directed-grants.md","title":"Coinholder-Directed Retroactive Grants Program","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"privacy-implications","text":"Privacy Implications"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"program-structure-and-custody","text":"Program Structure and Custody"},{"level":2,"id":"administrator-duties","text":"Administrator Duties"},{"level":2,"id":"kyc-and-compliance","text":"KYC and Compliance"},{"level":2,"id":"voting-methods","text":"Voting Methods"},{"level":3,"id":"shielded-voting","text":"Shielded Voting"},{"level":4,"id":"collecting-votes","text":"Collecting Votes"},{"level":4,"id":"counting-votes","text":"Counting Votes"},{"level":4,"id":"vote-auditing","text":"Vote Auditing"},{"level":3,"id":"transparent-voting","text":"Transparent Voting"},{"level":4,"id":"signing-protocol","text":"Signing protocol"},{"level":4,"id":"collecting-votes-1","text":"Collecting Votes"},{"level":4,"id":"counting-votes-1","text":"Counting Votes"},{"level":4,"id":"vote-auditing-1","text":"Vote Auditing"},{"level":2,"id":"voting-process","text":"Voting Process"},{"level":2,"id":"participation-and-voting-thresholds","text":"Participation and Voting Thresholds"},{"level":2,"id":"proposal-funding-and-disbursement","text":"Proposal Funding and Disbursement"},{"level":2,"id":"quarterly-timeline","text":"Quarterly Timeline"},{"level":2,"id":"security-precautions","text":"Security Precautions"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"historic-context-how-the-transparent-voting-process-was-run-in-the-last-vote","text":"Historic Context: How the transparent voting process was run in the last vote"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":782,"title":"[draft-pacu-wallet-eol] Best Practices for Zcash Wallet Application End of Life","author":"pacu","createdAt":"2024-02-12T20:17:44Z","updatedAt":"2025-11-17T19:29:13Z","url":"https://github.com/zcash/zips/pull/782","diffUrl":"https://github.com/zcash/zips/pull/782.diff","headRef":"wallet-eos-eol-best-practices-zip-xxx","headRepo":"pacu/zips","headSha":"c21e6b52184aff08ee635e3d650b2bcaf7e57806","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"would eventually close https://github.com/zcash/zips/issues/773","zipRefs":[],"draftRefs":["draft-zip-xxx-wallet-eos-eol"],"mapped":true,"claimed":false,"files":[{"filename":"draft-zip-xxx-wallet-eos-eol.rst","status":"added","additions":377,"deletions":0,"patch":"@@ -0,0 +1,377 @@\n+::\n+\n+  ZIP: Unassigned {numbers are assigned by ZIP editors}\n+  Title: Zcash Wallet Application End of Life / End Of Service Best practices\n+  and recommendations\n+  Owners: Francisco Gindre <codebuffet@proton.me>\n+          \n+  Credits: Kris Nuttycombe\n+           ...\n+  Status: Draft\n+  Category: Wallet\n+  Created: 2024-02-03\n+  License: MIT\n+  Pull-Request: TBD\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"REQUIRED\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this\n+document are to be interpreted as described in BCP 14 [#BCP14]_ when, and\n+only when, they appear in all capitals.\n+\n+Maintainer\n+  The individual, group or organization responsible for the \n+  operation, maintenance, development or support of a Zcash wallet software\n+  application.\n+\n+End-user / User\n+  The final user of the wallet. An individual, group or organization \n+  that makes use of the wallet application. Terms end-user and user are treated\n+  as synonyms for the purposes of this ZIP.\n+\n+End of Life / End of Service\n+  refers to a point in time where the wallet\n+  software will no longer be actively maintained or developed, therefore its\n+  operation cannot be guaranteed anymore or terminated indefinitely.\n+\n+Self-custody wallets\n+  Wallet applications that require end-users to maintain a sovereign custody of their private\n+  keys and/or the input they originate from (E.g: entropy bytes or BIP-39 style mnemonic seed\n+  phrases)\n+\n+\n+Abstract\n+========\n+\n+Zcash Wallet Applications might no longer be maintained, supported or serviced by\n+their maintainter from a point in time onwards. This ZIP defines a set of best practices\n+and recommendations to minimize the impact of the End of Service of a Zcash wallet \n+application onto its current or potential users.\n+\n+\n+Motivation\n+==========\n+\n+Cryptocurrency peer-to-peer networks are designed to be long-lived decentralized systems\n+that should tolerate the pass of time. While it is considered that the network implementing \n+these decentralized protocols would live indefinitely, wallet applications might be subject to\n+shorter lifecycles. There are many reasons for them to reach a point in time where they will\n+longer able to keep functioning. Whatever the reason might be, this document considers that \n+the result for end-users is indistinct: they will not be able to carry on their usual activities\n+on the application once the EOL/EOS day comes. To that end, this ZIP will discuss the topic of\n+sunsetting end-user applications gracefully by anticipating the moment as an invariant of any\n+product's lifecycle. \n+\n+\n+Requirements\n+============\n+\n+This document aims to ensure the ability for user to exercise complete sovereign self-\n+custody of their keys and funds related to them. Although it will refer specifically to \n+non-custodian wallets (or similar applications that handle Zcash keys), it can also apply to\n+custodian applications reaching EOS/EOL. The goal of this ZIP is to ensure that the user is in \n+control of its funds (or access to them) at all times, and that they can be provided ways to\n+keep that access beyond a specific product EOS/EOL in a \"hassle-free\" and user-friendly way.\n+Maintainers SHOULD always be eagerly looking to provide well-tested, dependable and mission\n+critical use cases that produce an application feature that can be made available to users in \n+the case of an EOS/EOL scenario.\n+\n+\n+Non-requirements\n+================\n+\n+Wallets that are considered to be \"in testing\", \"alpha\", \"beta\" or \"developer preview\"\n+programs and openly adversiting that to their users, therefore, not considered to be deployed \n+to an open audience as a finished product that is intended for end-users MAY opt not to follow \n+this practices. Althought, they SHOULD consider implementing them as part of their process to \n+delivering the application into production.\n+\n+\n+Specification\n+=============\n+\n+What is an EOS/EOL event?\n+-------------------------\n+End of Life / End of Service: refers to a point in time where the wallet\n+software will no longer be actively maintained or developed, therefore its\n+operation cannot be guaranteed anymore or terminated indefinitely.\n+\n+\n+Scope of an EOS/EOL event\n+-------------------------\n+\n+The breath of an application userbase and ecosystem may vary depending of the number of Operating\n+Systems, Platforms and/or architectures it supports. \n+\n+Complete EOS/EOL for 100% of the userbase\n+'''''''''''''''''''''''''''''''''''''''''\n+It might be the case that the whole product ceases to exist for all application versions on all target\n+Operating Systems or Platforms. This would be the more  drastic scenario for an end-user application. \n+After EOL/EOS no user will be able to use the application as normally.\n+\n+Partial EOS/EOL for a portion of the userbase\n+'''''''''''''''''''''''''''''''''''''''''''''\n+A subset of the userbase becomes unsupported. For example, a range of versions for a given target OS\n+will become unsupported at a given date (regardless the reason). Maintainers MUST scope and assess \n+the impact on their userbase to determine if there is a portion of which that will not be able to \n+continue to use their application with their current host device. If it is the case that 1 to N users \n+would eventually be unable to continue operating the application as usual after the EOS/EOL date, \n+Maintainers MUST treat those users as if they were going through \"Complete EOS/EOL for 100% of the\n+userbase\". \n+\n+The subsections below illustrate some of the scenarios that were taken into consideration when\n+creating this ZIP. We grouped them into two broad categories: \"Initiated by Maintainers\" or \n+\"caused by Contigency or third party\". \n+\n+\n+EOL / EOS initiated by Maintainer(s)\n+------------------------------------\n+\n+Unilateral End of Support\n+'''''''''''''''''''''''''\n+Maintainers will no longer continue to provide support to all or some of the aplications versions.\n+Casting a complete or a partial EOS/EOL event onto their userbase. \n+\n+\n+EOL / EOS caused by continency or a third party\n+-----------------------------------------------\n+\n+Programmed obsolescense\n+'''''''''''''''''''''''\n+\n+Programmed Obsolescense is imposed by hardware and software manufacturers. They may \n+unilaterally force users of otherwise perfectly operational products to cease to use them \n+by limiting or entirely disabling their capabilities, leaving the users with no other choice than\n+acquiring a newer version or migrating to a different product. \n+\n+Example: \n+After 19 Sep 2017, iOS 10 users won't be able to get any updates for their OS. Maintainers \n+might be not able to provide updates to their applications either. They decide to end support\n+for iOS 10. Users whose devices can't upgrade will have to be treated as EOS/EOL'd users. \n+\n+\n+Sanctions, Embargo, Ban, Closure or Prosecution\n+'''''''''''''''''''''''''''''''''''''''''''''''\n+\n+Wallet Maintainers might be based in jurisdictions subject to geopolitical Sanctions, Embargos\n+and be threatened or forced to cease their operations. Maintainers might also be legally bound\n+to cease and desist of their operations because of regulations on their jurisdictions, as well\n+as ceasing to provide support to certain subset of their whole userbase. Maintainers SHOULD\n+be aware of their jurisdictional risks and act accordingly to provided support over possible\n+imposed EOS/EOL scenario.\n+\n+**Example:** \n+\n+Country A forbids the use of cryptocurrency. \"SomeWallet\" is a very popular mobile crypto wallet\n+in country A. Developers of \"SomeWallet\" are informed that their application won't be available\n+on official distribution platforms (like App Store or Google Play) for the region of Country A\n+from date YYYY-mm-dd onwards. Developers of \"SomeWallet\" have to follow this ZIP's practices to\n+treat this event as a Partial EOS/EOL event. \n+\n+\n+Best Practices for handling EOS/EOL events on wallet applications\n+-----------------------------------------------------------------\n+\n+Make no assumptions on user expectations and knowledge\n+''''''''''''''''''''''''''''''''''''''''''''''''''''''\n+\n+Maintainers MUST NOT assume that end-users have any other technical knowledge of their application or its\n+underlying technology than the information or education their application provides with its intended use. \n+\n+Maintainers MAY assume that users could have knowledge of use cases and usability patterns inherited from \n+the Operating System that hosts the wallet application. \n+\n+Maintainers SHOULD NOT make assumptions on what their users will expect from the outcome of an EOS/EOL\n+event. Expectations SHOULD be set to users so that they can learn about the effects (main and side) of the \n+event itself. Users MUST be able to access information on how it will affect them, what will happen,\n+which actions are required from them and when those actions should be performed and what are the \n+possible outcomes of performing them or refusing to perform them. \n+\n+\n+Anticipating to the event\n+'''''''''''''''''''''''''\n+\"Unhappy\" paths are better than the absense of a path. Wallet developers SHOULD consider that eventually,\n+users will have to move out from their application. Either by their own choice or by force, users of\n+a Zcash wallet will have to go through the process of ceasing to use this application in favor of \n+another one. It could also be the case that users stay within the same application but switch to a \n+different device. **Building these use cases** into the Business as Usual (BAU) use cases, will give\n+developers a better understanding of what will eventually be needed in the case of having to enforce an\n+EOS/EOL event. \n+\n+Maintain an accurate inventory or mind-map of the User Data that the application handles\n+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\n+\n+Wallet maintainters SHOULD account for the kind of information that is locally stored on the user's device. \n+Although a great amount of information could be restored and/or derived from the blockchain with the \n+help of the user's private and public keys, maintainters SHOULD keep a record of which information\n+can be restored from the blockchain and which one is product of regular use and interaction and would\n+still be relevant for the user when an EOS/EOL event forces users to move to another version or wallet\n+application. \n+\n+Example of recoverable information:\n+\n+- On-chain information that can be recreated with user's viewing keys.\n+\n+Example of potentially non-recoverable information:\n+- A local address book that the wallet maintained for the user.\n+- local user-preferences of the user like, default currency conversion, locale, account pet-names, \n+- imported viewing keys not related to the user's seed phrase.\n+- internal datastructures that the wallet application uses to enhance the User Experience\n+\n+Educate the user on the information that can or cannot be backed up or exported from the wallet\n+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\n+\n+Users make use of their applications without thinking of the moment they'll have to move out their\n+data either to a new device, a different OS or application version. Operating Systems might offer\n+tools to migrate user data from one device to another. Users also rely on cloud backups to aid\n+on this kind of use cases. Users might expect that they can move all of their data with ease across\n+devices. Any deviations to default expectations set by the host platform, SHOULD be notified to\n+the user. \n+\n+**Example:**\n+\n+SomeWallet App will be deprecated soon and its community will run its own version that maintainers are\n+supportive of. Maintainers prepare a version of SomeWallet that will point users to the new community-\n+led version as their last official release of SomeWallet (See `Tombstone Releases`_).\n+As part of that process SomeWallet App lets users back up their keys so that they can restore their\n+wallet in the community-led version. When doing so, some users start to complain on the SomeWallet \n+community forums that they have lost contact list information they've gathered over the years.\n+\n+SomeWallet Maintainers SHOULD have set expectations properly to their users and explain the rationale\n+behind this data \"lost in translation\". If SomeWallet maintainers had followed the\n+`Make no assumptions on user expectations and knowledge`_ practice, they would have not assumed that\n+their users would have known of the contact list data not being preserved when restoring users' keys\n+into the community-led version of the SomeWallet app. This could indicate that they might not have\n+been `Anticipating to the event`_ enough to make time on their application design to \n+`Maintain an accurate inventory or mind-map of the User Data that the application handles`_ and\n+realizing that it would have been better to work around this contact list loss either developing\n+a way to migrate the information over, or at minimum, letting users know beforehand that their\n+contact list would have been lost so that they could actually taken some action to record that\n+information on their own.\n+\n+Use familiar building blocks\n+''''''''''''''''''''''''''''\n+Wallet maintainers SHOULD reuse the same UI and UX components that the user is familiar with from \n+everyday usage to handle EOS/EOL events. Additional UI elements MAY be used to convey a sense of \n+urgency, call user's attention or stress out critical aspects that cannot be ignored by the user.\n+\n+**Example:**\n+If the maintainers determine that to handle an EOS/EOL event, the user should backup its keys and \n+later restore it into another application or a subsequent version of the current application, the\n+application should reuse an existing backup flow (if available) that the user is already familiar\n+with or that it could remembrance from when it first signed into the application. \n+\n+\n+Support Offline / disconnected types of use modes\n+'''''''''''''''''''''''''''''''''''''''''''''''''\n+As stated in `Anticipating to the event`_, EOL/EOS events might involve network infrastructure\n+being shut down and becoming unavailable. A good way to handling netwokr contingencies is to\n+provide \"offline\" or \"disconnected\" modes on the wallet application, so that there is a subset\n+of functionality that is still available to the user regardless of network services the app\n+mostly relies on. This ties up to `Use familiar building blocks`_ point, where handling offline\n+or disconnected states could be a foundamental building block for handling an EOS/EOL event\n+on your application.\n+\n+Inform, request feedback, communicate\n+''''''''''''''''''''''''''''''''''''''\n+Maintainers SHOULD keep a dynamic and flowing of dialog with their users when possible. Zcash\n+wallet maintainers SHOULD consider establishing communication channels that statisfy the privacy\n+expectations of a shielded ZEC user. Maintainers SHOULD not impose or imply deanonymization to \n+users in order to maintain fluid two-way dialogue with them. While announcing an EOS/EOL event\n+MAY be done publicly, maintainers SHOULD provide privacy-preserving communication channels for\n+the affected users to gather feedback, concerns and support requests. \n+\n+When planning how to communicate an EOS/EOL event, maintainers SHOULD be aware that proactive\n+ecosystem outreach campaigns might be a good opportunity for adversaries to attack users (see \n+`Beware of adversaries`_)\n+\n+\n+Beware of adversaries\n+'''''''''''''''''''''\n+Maintainers MUST establishing inequivocable, verifiable, accessible and reliable communication\n+channels with their users. Users of wallet applications MUST be able to properly identify their\n+wallet's official communcation channels. Maintainers SHOULD provide more than one channel to\n+not only have a wider outreach range, but also redundancy so that compromised channels can be\n+disposed and discarded. \n+\n+Maintainers MUST account for EOS/EOL events in their security threat models. Adversaries MAY\n+user such events as attack vectors.\n+\n+**Example:** \n+\n+SomeWallet's maintainers communicate throught their official channels that their app will be\n+discontinued in 30 days. Maintainers have anticipated to the event (see `Anticipating to the event`_),\n+and created a new version that will include educational material on how to move to the \"community\n+maintained\" version of SomeWallet. This version will also provide a workflow to back up and\n+migrate the user's information to the \"community maintained\" version of SomeWallet if users wish to\n+because they have followed `Maintain an accurate inventory or mind-map of the User Data that the\n+application handles`_ recommendations. Also, they have redundant open channels to `Inform, request \n+feedback, communicate`_ with their userbase.\n+\n+Several users started to report that a Twitter/X bot is targeting SomeWallet's official account's \n+followers by impersonating as SomeWallet maintainers and trickying into following fake troubleshooting\n+steps to migrate their wallets, resulting on users mistakenly giving out their confidential information\n+and having their funds stolen by these fake support agents. \n+\n+Although SomeWallet maintainers appeared to have followed this ZIP \"by the book\". They overlooked the\n+possibility of adversaries taking advantage of a disruptive episode such as an EOS/EOS event. They\n+SHOULD have informed users that SomeWallet **would never** contact them directly requesting any kind of\n+information no matter how harmless it might be. \n+\n+Tombstone Releases\n+''''''''''''''''''\n+The final release of a wallet application.  \n+\n+Given that it has been scoped that there will be an EOS/EOL event affecting its users (see `Scope\n+of an EOS/EOL event`_), and determined that (a portion of) its users will not be capable of running\n+the wallet application any longer, maintainers following `Anticipating to the event`_ practices,\n+MAY prepare and schedule a **Tombstone Release**. \n+\n+As its name indicates, a tombstone release of a wallet application will be (ideally) **the last**\n+release maintainters will do of such application. The main goal of this release is to adapt the \n+application to the fact that will no longer be supported, actively maintained and that the network\n+infrastructure it may depend on will no longer be available. \n+\n+Tombstone releases MUST be inside the version update path of the application (or versions) to be\n+deprecated/terminated. \n+\n+Maintainers MUST perform all actions at theird disposal to avoid surprising their userbase with the\n+appearance of a tombstone version. Maintainters MUST account for automatic update mechanism that the\n+host OS might provide to users, therefore they SHOULD prepare affected users of the EOS/EOL event so\n+that they can be aware of its imminence. \n+\n+For the case that users SHALL perform actions to migrate from the affected application, maintainers\n+SHOULD anticipate them and provide the tools to perform those actions in versions **prior** to the\n+planned and announced tombstone release.\n+\n+Maintainers SHOULD anticipate EOS/EOL events when possible and proactively communicate with their user-\n+base. \n+\n+\n+\n+Open questions and TODOs\n+========================\n+\n+- Shall the ZIP recommend that EOS/EOL features be bundled in and hidden from the public\n+regardless?\n+- [TODO] EOS/EOL type flow chart \n+- [TODO] Add References\n+\n+Reference implementation\n+========================\n+\n+{This section is entirely optional; if present, it usually gives links to zcashd or\n+zebrad PRs.}\n+\n+\n+References\n+==========\n+\n+.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2022.3.8 or later <protocol/protocol.pdf>`_\n+.. [#protocol-introduction] `Zcash Protocol Specification, Version 2022.3.8. Section 1: Introduction <protocol/protocol.pdf#introduction>`_\n+.. [#protocol-blockchain] `Zcash Protocol Specification, Version 2022.3.8. Section 3.3: The Block Chain <protocol/protocol.pdf#blockchain>`_\n+.. [#protocol-networks] `Zcash Protocol Specification, Version 2022.3.8. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#katex] `KaTeX - The fastest math typesetting library for the web <https://katex.org/>`_\n+.. [#zip-0000] `ZIP 0: ZIP Process <zip-0000.rst>`_"}],"filesTruncated":false,"preview":{"filename":"draft-zip-xxx-wallet-eos-eol.rst","title":"Zcash Wallet Application End of Life / End Of Service Best practices","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"what-is-an-eos-eol-event","text":"What is an EOS/EOL event?"},{"level":2,"id":"scope-of-an-eos-eol-event","text":"Scope of an EOS/EOL event"},{"level":3,"id":"complete-eos-eol-for-100-of-the-userbase","text":"Complete EOS/EOL for 100% of the userbase"},{"level":3,"id":"partial-eos-eol-for-a-portion-of-the-userbase","text":"Partial EOS/EOL for a portion of the userbase"},{"level":2,"id":"eol-eos-initiated-by-maintainer-s","text":"EOL / EOS initiated by Maintainer(s)"},{"level":3,"id":"unilateral-end-of-support","text":"Unilateral End of Support"},{"level":2,"id":"eol-eos-caused-by-continency-or-a-third-party","text":"EOL / EOS caused by continency or a third party"},{"level":3,"id":"programmed-obsolescense","text":"Programmed obsolescense"},{"level":3,"id":"sanctions-embargo-ban-closure-or-prosecution","text":"Sanctions, Embargo, Ban, Closure or Prosecution"},{"level":2,"id":"best-practices-for-handling-eos-eol-events-on-wallet-applications","text":"Best Practices for handling EOS/EOL events on wallet applications"},{"level":3,"id":"make-no-assumptions-on-user-expectations-and-knowledge","text":"Make no assumptions on user expectations and knowledge"},{"level":3,"id":"anticipating-to-the-event","text":"Anticipating to the event"},{"level":3,"id":"maintain-an-accurate-inventory-or-mind-map-of-the-user-data-that-the-application-handles","text":"Maintain an accurate inventory or mind-map of the User Data that the application handles"},{"level":3,"id":"educate-the-user-on-the-information-that-can-or-cannot-be-backed-up-or-exported-from-the-wallet","text":"Educate the user on the information that can or cannot be backed up or exported from the wallet"},{"level":3,"id":"use-familiar-building-blocks","text":"Use familiar building blocks"},{"level":3,"id":"support-offline-disconnected-types-of-use-modes","text":"Support Offline / disconnected types of use modes"},{"level":3,"id":"inform-request-feedback-communicate","text":"Inform, request feedback, communicate"},{"level":3,"id":"beware-of-adversaries","text":"Beware of adversaries"},{"level":3,"id":"tombstone-releases","text":"Tombstone Releases"},{"level":1,"id":"open-questions-and-todos","text":"Open questions and TODOs"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":1124,"title":"[WIP] Support macros in ZIPs","author":"daira","createdAt":"2025-11-07T17:01:42Z","updatedAt":"2025-11-07T17:01:42Z","url":"https://github.com/zcash/zips/pull/1124","diffUrl":"https://github.com/zcash/zips/pull/1124.diff","headRef":"support-macros-in-zips","headRepo":"daira/zips","headSha":"9e8e676cfd744441cd0284b9fd2ab9f7b343d2ff","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[],"draftRefs":["draft-ecc-quantum-recoverability"],"mapped":true,"claimed":false,"files":[{"filename":"Makefile","status":"modified","additions":5,"deletions":4,"patch":"@@ -37,17 +37,18 @@ discard:\n \trm -r rendered\n \tgit checkout -- 'README.rst'\n \n-.Makefile.uptodate: Makefile render.sh\n+.Makefile.uptodate: Makefile render.sh expand_macros.py\n \t$(MAKE) clean\n+\t./expand_macros.py --test\n \ttouch .Makefile.uptodate\n \n-rendered/index.html: README.rst render.sh\n+rendered/index.html: README.rst render.sh expand_macros.py\n \t./render.sh --rst $< $@\n \n-rendered/%.html: zips/%.rst render.sh\n+rendered/%.html: zips/%.rst render.sh expand_macros.py\n \t./render.sh --rst $< $@\n \n-rendered/%.html: zips/%.md render.sh\n+rendered/%.html: zips/%.md render.sh expand_macros.py\n \t./render.sh $(MARKDOWN_OPTION) $< $@\n \n README.rst: .zipfilelist.current .draftfilelist.current makeindex.sh README.template $(wildcard zips/zip-*.rst) $(wildcard zips/zip-*.md) $(wildcard zips/draft-*.rst) $(wildcard zips/draft-*.md)"},{"filename":"expand_macros.py","status":"added","additions":332,"deletions":0,"patch":"@@ -0,0 +1,332 @@\n+#!/usr/bin/env python3\n+\n+import sys\n+import re\n+from io import StringIO\n+from pathlib import Path\n+from collections import namedtuple, deque\n+\n+\n+def main(args):\n+    if args[1:] == [\"--test\"]:\n+        return self_test()\n+\n+    if len(args) != 3:\n+        print(\"Usage: ./expand_macros.py <macrofile> <targetfile>\\n\"\n+              \"\\n\"\n+              \"Expand <targetfile> using the macros defined in <macrofile>, writing the\\n\"\n+              \"output on stdout. If <targetfile> is \\\"-\\\", read from stdin.\\n\"\n+              \"\\n\"\n+              \"The contents of <macrofile> should be a list of file inclusions and\\n\"\n+              \"macro definitions, one per line. Comments (from the rightmost '%' to the\\n\"\n+              \"end of the line), whitespace, and blank lines will be ignored.\\n\"\n+              \"\\n\"\n+              \"A file inclusion has the form\\n\"\n+              \"\\n\"\n+              \"\\\\includeonly{filename}\\n\"\n+              \"\\n\"\n+              \"and causes another macro file 'filename.tex' in the same directory as\\n\"\n+              \"<macrofile> to be included.\\n\"\n+              \"\\n\"\n+              \"A macro definition has the form\\n\"\n+              \"\\n\"\n+              \"\\\\newcommand{\\\\macroname}[n]{replacement}\\n\"\n+              \"\\n\"\n+              \"where n is the number of arguments. If n is zero then the '[0]' can be\\n\"\n+              \"omitted. The replacement text may include '#i' to substitute the i'th\\n\"\n+              \"argument, counting from 1.\\n\"\n+              \"\\n\"\n+              \"For example, given a macro\\n\"\n+              \"\\n\"\n+              \"\\\\newcommand{\\\\journey}[3]{from {#1} to {#3} via {#2}}\\n\"\n+              \"\\n\"\n+              \"the invocation '\\\\journey{Manchester}{Crewe}{London}' will expand to\\n\"\n+              \"'from {Manchester} to {London} via {Crewe}'. (It is usual, but not\\n\"\n+              \"required, to use '{}' around subsitutions to compensate for lack of\\n\"\n+              \"syntactic hygiene in LaTeX.)\\n\"\n+              \"\\n\"\n+              \"Note that this intentionally covers only the most commonly used subset\\n\"\n+              \"of LaTeX macro definition syntax.\\n\"\n+              \"\\n\"\n+              \"A very simple approximation of LaTeX's tokenization is used in order to\\n\"\n+              \"ensure that substitutions are not applied incorrectly in the case of\\n\"\n+              \"no-argument macros. Specifically, a macro named '\\\\foo' will match only\\n\"\n+              \"instances of the string '\\\\foo' followed by a non-letter or the end of\\n\"\n+              \"a line, i.e. it will match on '\\\\foo0' (giving the replacement text\\n\"\n+              \"followed by '0'), but not on '\\\\foonly'. As in LaTeX, a space after a\\n\"\n+              \"no-argument macro is consumed, i.e. '\\\\foo ' behaves like '\\\\foo'.\\n\"\n+              \"\\n\"\n+              \"Unlike LaTeX, macros with arguments will only match if each argument is\\n\"\n+              \"surrounded by '{}'. Nesting of '{}' within macro arguments works, i.e.\\n\"\n+              \"in the example above, '\\\\journey{{a}}{b}{c}' will expand to\\n\"\n+              \"'from {{a}} to {c} via {b}'. The '{' and '}' characters can be escaped\\n\"\n+              \"as '\\\\{' and '\\\\}'.\\n\"\n+              \"\\n\"\n+              \"A token effort (pun intended) is made to ensure that macro expansion\\n\"\n+              \"does not lead to tokenization inconsistent with LaTeX: if a replacement\\n\"\n+              \"starts with a letter that would continue a control word, then a space\\n\"\n+              \"is inserted before the replacement so that the tokenization of the\\n\"\n+              \"preceding control word is preserved. (In combination with the stricter\\n\"\n+              \"syntax for macro arguments, this means that certain patterns in which a\\n\"\n+              \"macro expansion provides arguments to another macro are not supported.)\\n\"\n+              \"Similarly, if a replacement ends with a control word that would be\\n\"\n+              \"extended by the following text, then a space is inserted after the\\n\"\n+              \"replacement to prevent that.\\n\"\n+              \"\\n\"\n+              \"Doubling a '\\\\' character can be used to escape it, i.e. an even number\\n\"\n+              \"of '\\\\' characters will be left as-is and will not be considered as the\\n\"\n+              \"start of a potential macro invocation. To write a literal '%' character\\n\"\n+              \"in a macro definition, include a comment after the definition.\\n\"\n+              \"\\n\"\n+              \"Unlike LaTeX, macro arguments cannot span lines; the expansion of each\\n\"\n+              \"line is independent.\\n\"\n+              \"\\n\"\n+              \"Exit code: 0 on success, 1 on failure.\\n\"\n+              \"\\n\"\n+              \"To run a self-test, use './expand_macros.py --test'. This follows the\\n\"\n+              \"same exit code convention.\\n\")\n+        return 1\n+\n+    macroset = parse_macros_from_file(Path(args[1]))\n+\n+    targetfile = args[2]\n+    if targetfile == \"-\":\n+        expand_stream(macroset, sys.stdin, \"<stdin>\")\n+    else:\n+        with open(targetfile, \"r\", encoding=\"utf-8\") as targetstream:\n+            expand_stream(macroset, targetstream, targetfile)\n+\n+\n+\n+class MacroDefinitionError(Exception):\n+    pass\n+\n+class MacroSubsitutionError(Exception):\n+    pass\n+\n+Macro = namedtuple(\"Macro\", \"argcount replacement\")\n+MacroSet = namedtuple(\"MacroSet\", \"mapping regex\")\n+\n+def make_macroset(macros):\n+    regexes = deque()\n+    for name, macro in macros.items():\n+        assert re.escape(name) == name\n+        if macro.argcount == 0:\n+            regexes.append(r\"(?:(?:^|[^\\\\])(\\\\\\\\)*\\\\\" + name + r\" ?)\")\n+        else:\n+            regexes.append(r\"(?:(?:^|[^\\\\])(\\\\\\\\)*\\\\\" + name + r\"\\{)\")\n+\n+    return MacroSet(macros, re.compile(\"|\".join(regexes)))\n+\n+def parse_macros_from_file(filepath):\n+    filepath = filepath.resolve()\n+    basepath = filepath.parent\n+    macros = {}\n+    inclusions = set()\n+\n+    def opener(includename, location):\n+        if includename in inclusions:\n+            raise MacroDefinitionError(f\"Re-inclusion of '{includename}' at {location}\")\n+        inclusions.add(includename)\n+\n+        with open(basepath / includename, \"r\", encoding=\"utf-8\") as macrostream:\n+            parse_macros_into(macros, opener, macrostream, includename)\n+\n+    opener(filepath.name, \"<top-level>\")\n+    return make_macroset(macros)\n+\n+def parse_macros_from_string(s, name):\n+    macros = {}\n+    def opener(includename, location):\n+        raise MacroDefinitionError(\"Cannot include {includename} from a string at {location}\")\n+\n+    parse_macros_into(macros, opener, StringIO(s), name)\n+    return make_macroset(macros)\n+\n+\n+# The filename pattern cannot access other directories.\n+INCLUDEONLY = re.compile(r\"\\\\includeonly\\{([a-zA-Z][-.a-zA-Z]*)\\}\")\n+\n+NEWCOMMAND_START = re.compile(r\"\\\\newcommand\\{\\\\([a-zA-Z]+)\\}(?:\\[([0-9])\\])?\")\n+\n+def parse_macros_into(macros, opener, macrostream, filename):\n+    for n, line in enumerate(macrostream):\n+        line = line.partition('%')[0].strip(' \\t\\n')\n+        if line == \"\": continue\n+\n+        include = INCLUDEONLY.match(line)\n+        if include:\n+            opener(include.group(1) + \".tex\", f\"{filename} line {n+1}\")\n+            continue\n+\n+        start = NEWCOMMAND_START.match(line)\n+        if not start:\n+            raise MacroDefinitionError(f\"Non-macro found\\n  At {filename} line {n+1}: <{line}>\")\n+\n+        name = start.group(1)\n+        argcount = int(start.group(2) or 0)\n+        (replacement, rest) = munch(line[start.end():])\n+        if replacement is None or rest != \"\":\n+            raise MacroDefinitionError(f\"Incomplete macro definition\\n  At {filename} line {n+1}: <{line}>\\n  Still to parse: <{rest}>\")\n+\n+        macros[name] = Macro(argcount, replacement)\n+\n+def munch(s):\n+    \"\"\"Parse a balanced {} group. Return (group, rest) if found, or (None, s) otherwise.\"\"\"\n+    if s[0] != '{':\n+        return (None, s)\n+\n+    depth = 0\n+    unescaped = 1\n+    for i in range(len(s)):\n+        c = s[i]\n+        match c:\n+          case '{' : depth += unescaped\n+          case '}' : depth -= unescaped\n+\n+        unescaped = (1 - unescaped) if c == '\\\\' else 1\n+\n+        if depth == 0:\n+            return (s[1:i], s[i+1:])\n+\n+    return (None, s)\n+\n+def expand_stream(macroset, targetstream, filename):\n+    for n, line in enumerate(targetstream):\n+        print(expand_line(macroset, line.rstrip(\"\\n\"), f\"{filename} line {n+1}\"))\n+\n+# 0-based\n+SUBST = [re.compile(r\"(?:^|[^\\\\])(\\\\\\\\)*#\" + str(i)) for i in range(1, 10)]\n+\n+ENDS_IN_CONTROL_WORD = re.compile(r\"(?:^|[^\\\\])(\\\\\\\\)*\\\\[a-zA-Z]+$\")\n+ASCII_LETTER = re.compile(r\"[a-zA-Z]\")\n+\n+def would_extend_control_word(before, after):\n+    return (ASCII_LETTER.match(after) is not None) and (ENDS_IN_CONTROL_WORD.search(before) is not None)\n+\n+def expand_line(macroset, line, location):\n+    result = \"\"\n+    rest = line\n+    runaway = 0\n+    while found := macroset.regex.search(rest):\n+        name = found.group().rstrip('{')\n+        end = found.start() + len(name)\n+        name = name.rpartition('\\\\')[2]\n+        assert name is not None, f\"name: {name}, rest: <{rest}>, found: {found}\"\n+        start = end - len(name)\n+        assert start > 0, f\"name: {name}, rest: <{rest}>, found: {found}\"\n+        name = name.rstrip(' ')\n+        macro = macroset.mapping.get(name)\n+        assert macro is not None, f\"name: {name}, rest: <{rest}>, found: {found}, macroset: {macroset}\"\n+        replacement = macro.replacement\n+\n+        # Append the target string up to the macro invocation including any escaped '\\'s.\n+        result += rest[:start-1]\n+        rest = rest[end:]\n+\n+        for subst in SUBST[:macro.argcount]:\n+            (arg, rest) = munch(rest)\n+            if arg is None:\n+                raise MacroSubsitutionError(f\"Too few arguments to macro '\\\\{name}'\\n  At {location}: <{line}>\\n  Still to parse: <{rest}>\")\n+            replacement = subst.sub(lambda m: m.group()[:-2] + arg, replacement)\n+\n+        # If a replacement starts with a letter that would continue a control word, then a\n+        # space is inserted before the replacement so that the tokenization of the preceding\n+        # control word is preserved.\n+        if would_extend_control_word(result, replacement + rest):\n+            result += ' '\n+\n+        # If a replacement ends with a control word that would be extended by the following\n+        # text, then a space is inserted after the replacement to prevent that.\n+        if would_extend_control_word(result + replacement, rest):\n+            replacement += ' '\n+\n+        rest = replacement + rest\n+        runaway += 1\n+        if runaway == 100:\n+            raise MacroSubsitutionError(f\"Runaway macro expansion\\n  At {location}: <{line}>\\n  Result: <{result}>\\n  Still to parse: <{rest}>\")\n+\n+    return result + rest\n+\n+def self_test():\n+    successes = 0\n+    failures = 0\n+    for testname, (macrostr, expect_mapping, cases) in TESTS.items():\n+        try:\n+            macroset = parse_macros_from_string(macrostr, f\"test {testname}\")\n+            assert macroset.mapping == expect_mapping, f\"{testname} definitions\\n    actual: {macroset.mapping}\\n  expected: {expect_mapping}\"\n+            for i, (target, expect_replacement) in enumerate(cases):\n+                location = f\"test {testname} case {i+1}\"\n+                try:\n+                    replacement = expand_line(macroset, target, location)\n+                except MacroSubsitutionError as e:\n+                    replacement = None\n+                    if expect_replacement is not None: raise\n+                assert replacement == expect_replacement, f\"{location}\\n    actual: {replacement}\\n  expected: {expect_replacement}\"\n+\n+            successes += 1\n+        except (MacroDefinitionError, AssertionError) as e:\n+            print(f\"Failed testcase: {testname}\\n  {e.__class__.__name__}: {e}\")\n+            failures += 1\n+\n+    if failures > 0:\n+        print(f\"\\nTests failed: {failures} failures, {successes} successes.\\n\")\n+        return 1\n+    else:\n+        print(f\"Tests passed: no failures, {successes} successes.\\n\")\n+        return 0\n+\n+\n+TESTS = {\n+    \"journey\": (\"\\\\newcommand{\\\\journey}[3]{from {#1} to {#3} via {#2}}\",\n+        {'journey': Macro(3, 'from {#1} to {#3} via {#2}')}, [\n+            (\"\\\\journey{Manchester}{Crewe}{London}\", \"from {Manchester} to {London} via {Crewe}\"),\n+            (\"\\\\journey{{a}}{b}{c}\", \"from {{a}} to {c} via {b}\"),\n+            (\"\\\\journey{{a}}{\\\\b}{\\\\\\\\c}\", \"from {{a}} to {\\\\\\\\c} via {\\\\b}\"),\n+            (\"\\\\journey{{a}}{\\\\}\\\\{b}{c}\", \"from {{a}} to {c} via {\\\\}\\\\{b}\"),\n+            (\"\\\\journey{{a}}{\\\\}\\\\\\\\\\\\{b}{\\\\\\\\c}\", \"from {{a}} to {\\\\\\\\c} via {\\\\}\\\\\\\\\\\\{b}\"),\n+            (\"\\\\journey{{a}{b}{c}\", None),\n+        ]),\n+    \"noarg\": (\" \\\\newcommand{\\\\foo}{bar}\",\n+        {'foo': Macro(0, 'bar')}, [\n+            (\"rhu\\\\foo b\", \"rhubarb\"),\n+            (\"rhu\\\\foo{}b\", \"rhubar{}b\"),\n+        ]),\n+    \"unclosedarg\": (\"\\t\\\\newcommand{\\\\foo}[1]{bar}\",\n+        {'foo': Macro(1, 'bar')}, [\n+            (\"rhu\\\\foo{\", None),\n+        ]),\n+    \"unusedarg\": (\"\\\\newcommand{\\\\foo}[1]{bar} \",\n+        {'foo': Macro(1, 'bar')}, [\n+            (\"rhu\\\\foo{}b\", \"rhubarb\"),\n+        ]),\n+    \"recursive\": (\"\\\\newcommand{\\\\journey}[3]{from {#1} to {#3} via {#2}}\\t\\n\\\\newcommand{\\\\foo}{bar}\",\n+        {'journey': Macro(3, 'from {#1} to {#3} via {#2}'), 'foo': Macro(0, 'bar')}, [\n+            (\"\\\\journey{cabbage}{rhu\\\\foo b}{cauliflower}\", \"from {cabbage} to {cauliflower} via {rhubarb}\"),\n+        ]),\n+    \"comment\": (\"% abc\\n\\n \\t\\\\newcommand{\\\\foo}{bar}\\t % def\",\n+        {'foo': Macro(0, 'bar')}, [\n+            (\"rhu\\\\foo b\", \"rhubarb\"),\n+        ]),\n+    \"noextbefore\": (\"\\\\newcommand{\\\\foo}{o}\",\n+        {'foo': Macro(0, 'o')}, [\n+            (\"\\\\fo\\\\foo\", \"\\\\fo o\"),\n+        ]),\n+    \"noextafter\": (\"\\\\newcommand{\\\\foo}{\\\\bar}\",\n+        {'foo': Macro(0, '\\\\bar')}, [\n+            (\"rhu\\\\foo b\", \"rhu\\\\bar b\"),\n+            (\"rhu\\\\\\\\foo b\", \"rhu\\\\\\\\foo b\"),\n+            (\"rhu\\\\\\\\\\\\foo b\", \"rhu\\\\\\\\\\\\bar b\"),\n+        ]),\n+    \"escape\": (\"\\\\newcommand{\\\\foo}{bar}\",\n+        {'foo': Macro(0, 'bar')}, [\n+            (\"rhu\\\\\\\\\\\\foo b\", \"rhu\\\\\\\\barb\"),\n+        ]),\n+    \"runaway\": (\"\\\\newcommand{\\\\foo}{\\\\foo}\",\n+        {'foo': Macro(0, '\\\\foo')}, [\n+            (\"\\\\foo\", None),\n+        ]),\n+}\n+\n+if __name__ == '__main__':\n+    sys.exit(main(sys.argv))"},{"filename":"render.sh","status":"modified","additions":21,"deletions":5,"patch":"@@ -7,7 +7,7 @@ set -euo pipefail\n \n if ! ( ( [ \"x$1\" = \"x--rst\" ] || [ \"x$1\" = \"x--pandoc\" ] || [ \"x$1\" = \"x--mmd\" ] ) && [ $# -eq 3 ] ); then\n     cat - <<EndOfUsage\n-Usage: render.sh --rst|--pandoc|--mmd <inputfile> <htmlfile> <title>\n+Usage: render.sh --rst|--pandoc|--mmd <inputfile> <htmlfile>\n \n --rst     render reStructuredText using rst2html5\n --pandoc  render Markdown using pandoc\n@@ -29,7 +29,8 @@ if [ \"x$1\" = \"x--rst\" ]; then\n else\n     filetype='.md'\n fi\n-title=\"$(basename -s ${filetype} ${inputfile} | sed -E 's|zip-0{0,3}|ZIP |; s|draft-|Draft |')$(grep -E '^(\\.\\.)?\\s*Title: ' ${inputfile} |sed -E 's|.*Title||')\"\n+basefile=\"$(basename -s ${filetype} ${inputfile})\"\n+title=\"$(echo ${basefile} | sed -E 's|zip-0{0,3}|ZIP |; s|draft-|Draft |')$(grep -E '^(\\.\\.)?\\s*Title: ' ${inputfile} |sed -E 's|.*Title||')\"\n echo \"    ${title}\"\n \n Math1='<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.16.25/dist/katex.min.css\" integrity=\"sha384-WcoG4HRXMzYzfCgiyfrySxx90XSl2rxY5mnVY5TwtWE6KLrArNKn0T/mOgNL0Mmi\" crossorigin=\"anonymous\">'\n@@ -40,13 +41,19 @@ Mermaid='<script defer src=\"https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/me\n \n ViewAndStyle='<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"stylesheet\" href=\"css/style.css\">'\n \n+macrofile=\"$(dirname ${inputfile})/${basefile}.macros.tex\"\n+\n cat <(\n     if [ \"x$1\" = \"x--rst\" ]; then\n         # These are basic regexps so \\+ is needed, not +.\n         # We use the Unicode 💲 character to move an escaped $ out of the way,\n         # which is much easier than trying to handle escapes within a capture.\n \n-        cat \"${inputfile}\" \\\n+        (if [ -f \"${macrofile}\" ]; then\n+            ./expand_macros.py \"${macrofile}\" \"${inputfile}\"\n+        else\n+            cat \"${inputfile}\"\n+        fi) \\\n         | sed 's|[\\][$]|💲|g;\n                s|[$]\\([^$]\\+\\)[$]\\([.,:;!?)-]\\)|:math:`\\1\\\\!`\\2|g;\n                s|[$]\\([^$]\\+\\)[$]|:math:`\\1`|g;\n@@ -55,12 +62,21 @@ cat <(\n         | sed \"s|<script src=\\\"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\\\"></script>|${Math1}\\n    ${Math2}\\n    ${Math3}|;\n                s|</head>|${ViewAndStyle}</head>|\"\n     else\n+        delete_expandedfile=0\n+        if [ -f \"${macrofile}\" ]; then\n+            expandedfile=\"$(dirname ${inputfile})/${basefile}.expanded${filetype})\"\n+            ./expand_macros.py \"${macrofile}\" \"${inputfile}\" >\"${expandedfile}\"\n+            delete_expandedfile=1\n+        else\n+            expandedfile=\"${inputfile}\"\n+        fi\n         if [ \"x$1\" = \"x--pandoc\" ]; then\n             # Not actually MathJax. KaTeX is compatible if we use the right headers.\n-            pandoc --mathjax --from=markdown --to=html \"${inputfile}\" --output=\"${outputfile}.temp\"\n+            pandoc --mathjax --from=markdown --to=html \"${expandedfile}\" --output=\"${outputfile}.temp\"\n         else\n-            multimarkdown ${inputfile} -o \"${outputfile}.temp\"\n+            multimarkdown ${expandedfile} -o \"${outputfile}.temp\"\n         fi\n+        if [ ${delete_expandedfile} -eq 1 ]; then rm -f \"${expandedfile}\"; fi\n \n         # Both pandoc and multimarkdown just output the HTML body.\n         echo \"<!DOCTYPE html>\""},{"filename":"zips/common-macros.tex","status":"added","additions":49,"deletions":0,"patch":"@@ -0,0 +1,49 @@\n+% Common macros\n+\n+% To use macros from this file, write `\\includeonly{common-macros}` in the\n+% `zip-xxxx.macros.tex` file.\n+\n+\\newcommand{\\bytes}[1]{\\underline{#1}}\n+\\newcommand{\\zeros}[1]{[0]^{#1}}\n+\\newcommand{\\zerobytes}[1]{[\\hexint{00}]^{#1}}\n+\\newcommand{\\ones}[1]{[1]^{#1}}\n+\\newcommand{\\bit}{\\mathbb{B}}\n+\\newcommand{\\byte}{\\mathbb{B}^{\\kern-0.1em\\tiny\\mathbb{Y}}}\n+\\newcommand{\\Nat}{\\mathbb{N}}\n+\\newcommand{\\PosInt}{\\mathbb{N}^+}\n+\\newcommand{\\Int}{\\mathbb{Z}}\n+\\newcommand{\\Rat}{\\mathbb{Q}}\n+\\newcommand{\\GF}[1]{\\mathbb{F}_{\\!#1}}\n+\\newcommand{\\GFstar}[1]{\\mathbb{F}^\\ast_{#1}}\n+\\newcommand{\\typeexp}[2]{{#1}\\vphantom{)}^{[{#2}]}}\n+\\newcommand{\\bitseq}[1]{\\typeexp{\\bit}{#1}}\n+\\newcommand{\\bitseqs}{\\bitseq{\\Nat}}\n+\\newcommand{\\byteseq}[1]{\\mathbb{B}^{{\\kern-0.1em\\tiny\\mathbb{Y}}[{#1}]}}\n+\\newcommand{\\byteseqs}{\\byteseq{\\Nat}}\n+\\newcommand{\\concatbits}{\\mathsf{concat}_\\bit}\n+\\newcommand{\\bconcat}{\\,||\\,}\n+\\newcommand{\\listcomp}[1]{[~{#1}~]}\n+\\newcommand{\\biglistcomp}[1]{\\bigg[{#1}\\bigg]}\n+\\newcommand{\\fun}[2]{{#1} \\mapsto {#2}}\n+\\newcommand{\\exclusivefun}[3]{{#1} \\mapsto_{\\not\\in\\kern 0.05em{#3}} {#2}}\n+\n+\\newcommand{\\squash}{\\!\\!\\!}\n+\\newcommand{\\caseif}{\\squash\\text{if }}\n+\\newcommand{\\caseotherwise}{\\squash\\text{otherwise}}\n+\n+\\newcommand{\\length}{\\mathsf{length}}\n+\\newcommand{\\truncate}[1]{\\mathsf{truncate}_{#1}}\n+\\newcommand{\\mean}{\\mathsf{mean}}\n+\\newcommand{\\median}{\\mathsf{median}}\n+\n+\\newcommand{\\hexint}[1]{\\mathtt{0x#1}}\n+\\newcommand{\\ascii}[1]{\\textbf{``\\texttt{#1}''}}\n+\\newcommand{\\setof}[1]{\\{{#1}\\}}\n+\\newcommand{\\bigsetof}[1]{\\big\\{{#1}\\big\\}}\n+\\newcommand{\\powerset}[1]{\\mathscr{P}\\big({#1}\\big)}\n+\\newcommand{\\barerange}[2]{{{#1}\\,..\\,{#2}}}\n+\\newcommand{\\range}[2]{\\setof{\\barerange{#1}{#2}}}\n+\\newcommand{\\bigrange}[2]{\\bigsetof{\\barerange{#1}{#2}}}\n+\\newcommand{\\rangenozero}[2]{\\range{#1}{#2} \\setminus \\setof{0}}\n+\\newcommand{\\bigrangenozero}[2]{\\bigrange{#1}{#2} \\setminus \\setof{0}}\n+\\newcommand{\\binaryrange}[1]{\\range{0}{2^{#1}-1}}"},{"filename":"zips/draft-ecc-quantum-recoverability.macros.tex","status":"added","additions":1,"deletions":0,"patch":"@@ -0,0 +1 @@\n+\\includeonly{common-macros}"},{"filename":"zips/draft-ecc-quantum-recoverability.md","status":"modified","additions":52,"deletions":52,"patch":"@@ -163,7 +163,7 @@ This subsection and the flow diagram below are non-normative.\n \n In order to support ZSAs [^zip-0226] [^zip-0227] and memo bundles\n [^zip-0231], v6 transactions require in any case a new note plaintext\n-format, with lead byte $\\mathtt{0x03}.$ This gives us an opportunity\n+format, with lead byte $\\hexint{03}.$ This gives us an opportunity\n to change the way that the $\\mathsf{pre\\_rcm}$ value is computed for\n this new format, by including all of the note fields in $\\mathsf{pre\\_rcm}$.\n The resulting $\\mathsf{rcm}$ is essentially a random function of the\n@@ -450,10 +450,10 @@ in the Orchard key components diagram, with the following note:\n Replace the paragraph\n \n > The field $\\mathsf{leadByte}$ indicates the version of the encoding of\n-> a Sapling or Orchard note plaintext. For Sapling it is $\\mathtt{0x01}$\n-> before activation of the Canopy network upgrade and $\\mathtt{0x02}$\n+> a Sapling or Orchard note plaintext. For Sapling it is $\\hexint{01}$\n+> before activation of the Canopy network upgrade and $\\hexint{02}$\n > afterward, as specified in [ZIP-212]. For Orchard note plaintexts it is\n-> always $\\mathtt{0x02}$.\n+> always $\\hexint{02}$.\n \n with\n \n@@ -472,10 +472,10 @@ with\n >\n > Define $\\mathsf{allowedLeadBytes^{protocol}}(\\mathsf{height}, \\mathsf{txVersion}) =$\n > $\\hspace{2em} \\begin{cases}\n->   \\{ \\mathtt{0x01} \\},&\\!\\!\\!\\text{if } \\mathsf{height} < \\mathsf{CanopyActivationHeight} \\\\\n->   \\{ \\mathtt{0x01}, \\mathtt{0x02} \\},&\\!\\!\\!\\text{if } \\mathsf{CanopyActivationHeight} \\leq \\mathsf{height} < \\mathsf{CanopyActivationHeight} + \\mathsf{ZIP212GracePeriod} \\\\\n->   \\{ \\mathtt{0x02} \\},&\\!\\!\\!\\text{if } \\mathsf{CanopyActivationHeight} + \\mathsf{ZIP212GracePeriod} \\leq \\mathsf{height} \\text{ and } \\mathsf{txVersion} < 6 \\\\\n->   \\{ \\mathtt{0x03} \\},&\\!\\!\\!\\text{otherwise.}\n+>   \\setof{\\hexint{01}},&\\caseif \\mathsf{height} < \\mathsf{CanopyActivationHeight} \\\\\n+>   \\setof{\\hexint{01}, \\hexint{02}},&\\caseif \\mathsf{CanopyActivationHeight} \\leq \\mathsf{height} < \\mathsf{CanopyActivationHeight} + \\mathsf{ZIP212GracePeriod} \\\\\n+>   \\setof{\\hexint{02}},&\\caseif \\mathsf{CanopyActivationHeight} + \\mathsf{ZIP212GracePeriod} \\leq \\mathsf{height} \\text{ and } \\mathsf{txVersion} < 6 \\\\\n+>   \\setof{\\hexint{03}},&\\!\\!\\!\\text{otherwise.}\n > \\end{cases}$\n >\n > The $\\mathsf{leadByte}$ of a Sapling or Orchard note MUST satisfy\n@@ -497,7 +497,7 @@ In the list of places where $\\mathsf{PRF^{expand}}$ is used:\n Replace\n \n > * [**NU5** onward] in § 4.2.3 ‘Orchard Key Components’, with inputs\n->   $[6]$, $[7]$, $[8]$, and with first byte $\\mathtt{0x82}$ (the last of\n+>   $[6]$, $[7]$, $[8]$, and with first byte $\\hexint{82}$ (the last of\n >   these is also specified in [[ZIP-32]](https://zips.z.cash/zip-0032));\n > * in the processes of sending (§ 4.7.2 ‘Sending Notes (Sapling)’ and\n >   § 4.7.3 ‘Sending Notes (Orchard)’) and of receiving\n@@ -508,23 +508,23 @@ Replace\n with\n \n > * [**NU5** onward] in § 4.2.3 ‘Orchard Key Components’, with inputs\n->   $[\\mathtt{0x06}]$, $[\\mathtt{0x07}]$, $[\\mathtt{0x08}]$, with first\n->   byte in $\\{ \\mathtt{0x0C}, \\mathtt{0x0D} \\}$ (also specified in\n->   {{ reference to this ZIP }}), and with first byte $\\mathtt{0x82}$\n+>   $[\\hexint{06}]$, $[\\hexint{07}]$, $[\\hexint{08}]$, with first\n+>   byte in $\\setof{\\hexint{0C}, \\hexint{0D}}$ (also specified in\n+>   {{ reference to this ZIP }}), and with first byte $\\hexint{82}$\n >   (also specified in [[ZIP-32]](https://zips.z.cash/zip-0032));\n > * in the processes of sending (§ 4.7.2 ‘Sending Notes (Sapling)’ and\n >   § 4.7.3 ‘Sending Notes (Orchard)’) and of receiving\n >   (§ 4.20 ‘In-band secret distribution (Sapling and Orchard)’) notes,\n->   for Sapling with inputs $[\\mathtt{0x04}]$ and $[\\mathtt{0x05}]$,\n+>   for Sapling with inputs $[\\hexint{04}]$ and $[\\hexint{05}]$,\n >   and for Orchard with first byte in\n->   $\\{ \\mathtt{0x05}, \\mathtt{0x04}, \\mathtt{0x09}, \\mathtt{0x0A}, \\mathtt{0x0B} \\}$\n->   ($\\mathtt{0x0A}$ and $\\mathtt{0x0B}$ are also specified in\n+>   $\\setof{\\hexint{05}, \\hexint{04}, \\hexint{09}, \\hexint{0A}, \\hexint{0B}}$\n+>   ($\\hexint{0A}$ and $\\hexint{0B}$ are also specified in\n >   {{ reference to this ZIP }});\n \n Add\n \n > * in {{ reference to this ZIP }}, with first byte in\n->   $\\{ \\mathtt{0x0A}, \\mathtt{0x0B}, \\mathtt{0x0C}, \\mathtt{0x0D} \\}$.\n+>   $\\setof{\\hexint{0A}, \\hexint{0B}, \\hexint{0C}, \\hexint{0D}}$.\n \n Also change the remaining decimal constants to hex for consistency.\n \n@@ -535,7 +535,7 @@ from § 5.3 ‘Constants’.\n \n Insert after the definition of $\\mathsf{ToScalar^{Orchard}}$:\n \n-> Define $\\mathsf{H}^{\\mathsf{rivk\\_ext}}_{\\mathsf{qk}}(\\mathsf{ak}, \\mathsf{nk}) = \\mathsf{ToScalar^{Orchard}}(\\mathsf{PRF^{expand}_{qk}}([\\mathtt{0x0D}] \\,||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{ak})$\n+> Define $\\mathsf{H}^{\\mathsf{rivk\\_ext}}_{\\mathsf{qk}}(\\mathsf{ak}, \\mathsf{nk}) = \\mathsf{ToScalar^{Orchard}}(\\mathsf{PRF^{expand}_{qk}}([\\hexint{0D}] \\,||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{ak})$\n > $\\hspace{23.9em} ||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{nk})))$.\n \n Replace from \"From this spending key\" up to and including the line\n@@ -567,10 +567,10 @@ in the algorithm with:\n > needs to be set to false.)\n >\n > Define:\n-> * $\\mathsf{H^{ask}}(\\mathsf{sk}) = \\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{sk}}([\\mathtt{0x06}])\\kern-0.1em\\big)$\n-> * $\\mathsf{H^{nk}}(\\mathsf{sk}) = \\mathsf{ToBase^{Orchard}}\\big(\\mathsf{PRF^{expand}_{sk}}([\\mathtt{0x07}])\\kern-0.1em\\big)$\n-> * $\\mathsf{H^{rivk}}(\\mathsf{sk}) = \\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{sk}}([\\mathtt{0x08}])\\kern-0.1em\\big)$\n-> * $\\mathsf{H^{qsk}}(\\mathsf{sk}) = \\mathsf{truncate}_{32}\\big(\\mathsf{PRF^{expand}_{sk}}([\\mathtt{0x0C}])\\kern-0.1em\\big)$\n+> * $\\mathsf{H^{ask}}(\\mathsf{sk}) = \\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{sk}}([\\hexint{06}])\\kern-0.1em\\big)$\n+> * $\\mathsf{H^{nk}}(\\mathsf{sk}) = \\mathsf{ToBase^{Orchard}}\\big(\\mathsf{PRF^{expand}_{sk}}([\\hexint{07}])\\kern-0.1em\\big)$\n+> * $\\mathsf{H^{rivk}}(\\mathsf{sk}) = \\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{sk}}([\\hexint{08}])\\kern-0.1em\\big)$\n+> * $\\mathsf{H^{qsk}}(\\mathsf{sk}) = \\mathsf{truncate}_{32}\\big(\\mathsf{PRF^{expand}_{sk}}([\\hexint{0C}])\\kern-0.1em\\big)$\n > * $\\mathsf{H^{qk}}(\\mathsf{qsk}) = \\textsf{BLAKE2s\\kern0.1em-256}(\\texttt{“Zcash\\_qk”}, \\mathsf{qsk})$.\n > \n > $\\mathsf{ask} \\;{\\small ⦂}\\; \\mathbb{F}^{*}_{r_{\\mathbb{P}}}$,\n@@ -633,8 +633,8 @@ Replace\n > Let $\\mathsf{CanopyActivationHeight}$ be as defined in § 5.3 ‘Constants’.\n >\n > Let $\\mathsf{leadByte}$ be the note plaintext lead byte.\n-> This MUST be $\\mathsf{0x01}$ if for the next block,\n-> $\\mathsf{height} < \\mathsf{CanopyActivationHeight}$, or $\\mathtt{0x02}$\n+> This MUST be $\\hexint{01}$ if for the next block,\n+> $\\mathsf{height} < \\mathsf{CanopyActivationHeight}$, or $\\hexint{02}$\n > if $\\mathsf{height} \\geq \\mathsf{CanopyActivationHeight}$.\n \n with\n@@ -643,9 +643,9 @@ with\n > according to § 3.2.1 ‘Note Plaintexts and Memo Fields’ with\n > $\\mathsf{protocol} = \\mathsf{Sapling}$.\n >\n-> Define $\\mathsf{H^{rcm,Sapling}_{rseed}}(\\_, \\_) = \\mathsf{ToScalar^{Sapling}}\\big(\\mathsf{PRF^{expand}_{rseed}}([\\mathtt{0x04}])\\kern-0.1em\\big)$\n+> Define $\\mathsf{H^{rcm,Sapling}_{rseed}}(\\_, \\_) = \\mathsf{ToScalar^{Sapling}}\\big(\\mathsf{PRF^{expand}_{rseed}}([\\hexint{04}])\\kern-0.1em\\big)$\n >\n-> Define $\\mathsf{H^{esk,Sapling}_{rseed}}(\\_, \\_) = \\mathsf{ToScalar^{Sapling}}\\big(\\mathsf{PRF^{expand}_{rseed}}([\\mathtt{0x05}])\\kern-0.1em\\big)$.\n+> Define $\\mathsf{H^{esk,Sapling}_{rseed}}(\\_, \\_) = \\mathsf{ToScalar^{Sapling}}\\big(\\mathsf{PRF^{expand}_{rseed}}([\\hexint{05}])\\kern-0.1em\\big)$.\n >\n > ($\\mathsf{H^{rcm,Sapling}}$ and $\\mathsf{H^{esk,Sapling}}$ intentionally\n > take arguments that are unused.)\n@@ -673,18 +673,18 @@ with\n >   $\\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed}}(\\mathsf{pre\\_rcm})\\kern-0.1em\\big)$\n >\n > where $\\mathsf{pre\\_rcm} = \\begin{cases}\n->   [\\mathtt{0x05}] \\,||\\, \\underline{\\text{ρ}},&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x02} \\\\\n->   [\\mathtt{0x0B}, \\mathsf{leadByte}] \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{g}\\star_{\\mathsf{d}}) \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{pk}\\star_{\\mathsf{d}}) \\\\\n->   \\hphantom{[\\mathtt{0x0B}, \\mathsf{leadByte}]} \\,||\\, \\mathsf{I2LEOSP}_{64}(\\mathsf{v}) \\,||\\, \\underline{\\text{ρ}} \\,||\\, \\mathsf{I2LEOSP}_{256}(\\text{ψ}) \\\\\n->   \\hphantom{[\\mathtt{0x0B}, \\mathsf{leadByte}]}\\,[\\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{AssetBase}\\kern0.08em\\star)],&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x03}\n+>   [\\hexint{05}] \\,||\\, \\underline{\\text{ρ}},&\\caseif \\mathsf{leadByte} = \\hexint{02} \\\\\n+>   [\\hexint{0B}, \\mathsf{leadByte}] \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{g}\\star_{\\mathsf{d}}) \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{pk}\\star_{\\mathsf{d}}) \\\\\n+>   \\hphantom{[\\hexint{0B}, \\mathsf{leadByte}]} \\,||\\, \\mathsf{I2LEOSP}_{64}(\\mathsf{v}) \\,||\\, \\underline{\\text{ρ}} \\,||\\, \\mathsf{I2LEOSP}_{256}(\\text{ψ}) \\\\\n+>   \\hphantom{[\\hexint{0B}, \\mathsf{leadByte}]}\\,[\\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{AssetBase}\\kern0.08em\\star)],&\\caseif \\mathsf{leadByte} = \\hexint{03}\n > \\end{cases}$\n >\n-> Define $\\mathsf{H^{esk,Orchard}_{rseed}}(\\underline{\\text{ρ}}) = \\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed}}([\\mathtt{0x04}] \\,||\\, \\underline{\\text{ρ}})\\kern-0.1em\\big)$.\n+> Define $\\mathsf{H^{esk,Orchard}_{rseed}}(\\underline{\\text{ρ}}) = \\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed}}([\\hexint{04}] \\,||\\, \\underline{\\text{ρ}})\\kern-0.1em\\big)$.\n >\n > Define $\\mathsf{H^{\\text{ψ},Orchard}_{rseed}}(\\underline{\\text{ρ}}, \\mathsf{split\\_flag}) = \\mathsf{ToBase^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed}}([\\mathsf{split\\_domain}] \\,||\\, \\underline{\\text{ρ}})\\kern-0.1em\\big)$.\n > where $\\mathsf{split\\_domain} = \\begin{cases}\n->   \\mathtt{0x09}&\\text{if } \\mathsf{split\\_flag} = 0 \\\\\n->   \\mathtt{0x0A}&\\text{if } \\mathsf{split\\_flag} = 1\\text{.}\n+>   \\hexint{09}&\\text{if } \\mathsf{split\\_flag} = 0 \\\\\n+>   \\hexint{0A}&\\text{if } \\mathsf{split\\_flag} = 1\\text{.}\n > \\end{cases}$\n \n Insert before the derivation of $\\mathsf{esk}$:\n@@ -805,11 +805,11 @@ with\n For § 4.20.2, replace\n \n > $\\hspace{1.0em}$ [**Canopy** onward] let $\\underline{\\mathsf{rcm}} = \\begin{cases}\n->                    \\mathsf{rseed},&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x01} \\\\\n+>                    \\mathsf{rseed},&\\!\\!\\!\\text{if } \\mathsf{leadByte} = hexint{01} \\\\\n >                    \\mathsf{ToScalar^{protocol}}(\\mathsf{PRF^{expand}_{rseed}}(\\mathsf{pre\\_rcm})),&\\!\\!\\!\\text{otherwise}\n >                  \\end{cases}$ <br>\n > $\\hspace{1.0em}$ let $\\mathsf{rcm} = \\mathsf{LEOS2IP}_{256}(\\underline{\\mathsf{rcm}})$ and $\\mathsf{g_d} = \\mathsf{DiversifyHash}(\\mathsf{d})$. if $\\mathsf{rcm} \\geq r_{\\mathbb{G}}$ or (for Sapling) $\\mathsf{g_d} = \\bot$, return $\\bot$ <br>\n-> $\\hspace{1.0em}$ [**Canopy** onward] if $\\mathsf{leadByte} \\neq \\mathtt{0x01}$: <br>\n+> $\\hspace{1.0em}$ [**Canopy** onward] if $\\mathsf{leadByte} \\neq hexint{01}$: <br>\n > $\\hspace{2.5em}$     $\\mathsf{esk} = \\mathsf{ToScalar^{protocol}}(\\mathsf{PRF^{expand}_{rseed}}(\\mathsf{pre\\_esk}))$ <br>\n > $\\hspace{2.5em}$     if $\\mathsf{repr}_{\\mathbb{G}}(\\mathsf{KA.DerivePublic}(\\mathsf{esk}, \\mathsf{g_d})) \\neq \\mathtt{ephemeralKey}$, return $\\bot$\n > \n@@ -818,15 +818,15 @@ For § 4.20.2, replace\n with\n \n > $\\hspace{1.0em}$ let $\\mathsf{g_d} = \\mathsf{DiversifyHash}(\\mathsf{d})$; for Sapling, if $\\mathsf{g_d} = \\bot$, return $\\bot$ <br>\n-> $\\hspace{1.0em}$ [**Canopy** onward] if $\\mathsf{leadByte} \\neq \\mathtt{0x01}$: <br>\n+> $\\hspace{1.0em}$ [**Canopy** onward] if $\\mathsf{leadByte} \\neq hexint{01}$: <br>\n > $\\hspace{2.5em}$     let $\\mathsf{esk} = \\mathsf{H^{esk,protocol}_{rseed}}(\\underline{\\text{ρ}})$ <br>\n > $\\hspace{2.5em}$     if $\\mathsf{repr}_{\\mathbb{G}}(\\mathsf{KA.DerivePublic}(\\mathsf{esk}, \\mathsf{g_d})) \\neq \\mathtt{ephemeralKey}$, return $\\bot$ <br>\n > $\\hspace{2.5em}$     let $\\text{ψ} = \\mathsf{H^{\\text{ψ},Orchard}_{rseed}}(\\underline{\\text{ρ}}, 0)$ for Orchard or $\\bot$ for Sapling\n > \n > $\\hspace{1.0em}$ let $\\mathsf{pk_d} = \\mathsf{KA.DerivePublic}(\\mathsf{ivk}, \\mathsf{g_d})$ <br>\n > $\\hspace{1.0em}$ let $\\mathsf{g}\\star_{\\mathsf{d}} = \\mathsf{repr}_{\\mathbb{P}}(\\mathsf{g_d})$, $\\mathsf{pk}\\star_{\\mathsf{d}} = \\mathsf{repr}_{\\mathbb{P}}(\\mathsf{pk_d}) [$, and $\\mathsf{AssetBase}\\kern0.08em\\star = \\mathsf{repr}_{\\mathbb{P}}(\\mathsf{AssetBase})]$ <br>\n > $\\hspace{1.0em}$ let $\\mathsf{rcm} = \\begin{cases}\n->                    \\mathsf{LEOS2IP}_{256}(\\mathsf{rseed}),&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x01} \\\\\n+>                    \\mathsf{LEOS2IP}_{256}(\\mathsf{rseed}),&\\caseif \\mathsf{leadByte} = \\hexint{01} \\\\\n >                    \\mathsf{H^{rcm,protocol}_{rseed}}(\\mathsf{leadByte}, (\\mathsf{g}\\star_{\\mathsf{d}}, \\mathsf{pk}\\star_{\\mathsf{d}}, \\mathsf{v}, \\underline{\\text{ρ}}, \\text{ψ}[, \\mathsf{AssetBase}\\kern0.08em\\star])),&\\!\\!\\!\\text{otherwise}\n >                  \\end{cases}$ <br>\n > $\\hspace{1.0em}$ if $\\mathsf{rcm} \\geq r_{\\mathbb{G}}$, return $\\bot$\n@@ -837,7 +837,7 @@ $\\mathsf{rcm}$ can depend on $\\mathsf{g_d}$ and $\\mathsf{pk_d}$.)\n For § 4.20.3, replace\n \n > $\\hspace{1.0em}$ [**Canopy** onward] let $\\underline{\\mathsf{rcm}} = \\begin{cases}\n->                    \\mathsf{rseed},&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x01} \\\\\n+>                    \\mathsf{rseed},&\\!\\!\\!\\text{if } \\mathsf{leadByte} = hexint{01} \\\\\n >                    \\mathsf{ToScalar}(\\mathsf{PRF^{expand}_{rseed}}(\\mathsf{pre\\_rcm})),&\\!\\!\\!\\text{otherwise}\n >                  \\end{cases}$ <br>\n > $\\hspace{1.0em}$ let $\\mathsf{rcm} = \\mathsf{LEOS2IP}_{256}(\\underline{\\mathsf{rcm}})$ and $\\mathsf{g_d} = \\mathsf{DiversifyHash}(\\mathsf{d})$ <br>\n@@ -849,7 +849,7 @@ with\n > $\\hspace{1.0em}$ let $\\mathsf{g}\\star_{\\mathsf{d}} = \\mathsf{repr}_{\\mathbb{P}}(\\mathsf{g_d}) [$ and $\\mathsf{AssetBase}\\kern0.08em\\star = \\mathsf{repr}_{\\mathbb{P}}(\\mathsf{AssetBase})]$ <br>\n > $\\hspace{1.0em}$ if $\\mathsf{leadByte} \\neq \\mathtt{0x01}$, let $\\text{ψ} = \\mathsf{H^{\\text{ψ},Orchard}_{rseed}}(\\underline{\\text{ρ}}, 0)$ for Orchard or $\\bot$ for Sapling <br>\n > $\\hspace{1.0em}$ let $\\mathsf{rcm} = \\begin{cases}\n->                    \\mathsf{LEOS2IP}_{256}(\\mathsf{rseed}),&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x01} \\\\\n+>                    \\mathsf{LEOS2IP}_{256}(\\mathsf{rseed}),&\\caseif \\mathsf{leadByte} = \\hexint{01} \\\\\n >                    \\mathsf{H^{rcm,protocol}_{rseed}}(\\mathsf{leadByte}, (\\mathsf{g}\\star_{\\mathsf{d}}, \\mathsf{pk}\\star_{\\mathsf{d}}, \\mathsf{v}, \\underline{\\text{ρ}}, \\text{ψ}[, \\mathsf{AssetBase}\\kern0.08em\\star])),&\\!\\!\\!\\text{otherwise}\n >                  \\end{cases}$ <br>\n > $\\hspace{1.0em}$ if $\\mathsf{rcm} \\geq r_{\\mathbb{G}}$, return $\\bot$\n@@ -900,7 +900,7 @@ Add the following to the section [Note Structure & Commitment](https://zips.z.ca\n > When § 4.7.3 ‘Sending Notes (Orchard)’ or § 4.8.3 ‘Dummy Notes (Orchard)’\n > are invoked directly or indirectly in the computation of $\\text{ρ}$ and\n > $\\text{ψ}$ for an OrchardZSA note, $\\mathsf{leadByte}$ MUST be set to\n-> $\\mathtt{0x03}$.\n+> $\\hexint{03}$.\n \n In section [Split Notes](https://zips.z.cash/zip-0226#split-notes), change:\n \n@@ -910,7 +910,7 @@ In section [Split Notes](https://zips.z.cash/zip-0226#split-notes), change:\n to\n \n > where $\\text{ψ}^{\\mathsf{nf}}$ is computed as\n-> $\\mathsf{H^{\\text{ψ},Orchard}_{rseed\\_nf}}(\\underline{\\text{ρ}}, 1) = \\mathsf{ToBase^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed\\_nf}}([\\mathtt{0x0A}] \\,||\\, \\underline{\\text{ρ}})\\kern-0.1em\\big)$\n+> $\\mathsf{H^{\\text{ψ},Orchard}_{rseed\\_nf}}(\\underline{\\text{ρ}}, 1) = \\mathsf{ToBase^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed\\_nf}}([\\hexint{0A}] \\,||\\, \\underline{\\text{ρ}})\\kern-0.1em\\big)$\n > for $\\mathsf{rseed\\_nf}$ sampled uniformly at random on $\\mathbb{B}^{{\\kern-0.1em\\tiny\\mathbb{Y}}[32]}$, ...\n \n \n@@ -949,22 +949,22 @@ Import this definition from § 4.7.3 ‘Sending Notes (Orchard)’:\n > $\\mathsf{ToScalar^{Orchard}}\\big(\\mathsf{PRF^{expand}_{rseed}}(\\mathsf{pre\\_rcm})\\kern-0.1em\\big)$\n >\n > where $\\mathsf{pre\\_rcm} = \\begin{cases}\n->   [\\mathtt{0x05}] \\,||\\, \\underline{\\text{ρ}},&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x02} \\\\\n->   [\\mathtt{0x0B}, \\mathsf{leadByte}] \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{g}\\star_{\\mathsf{d}}) \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{pk}\\star_{\\mathsf{d}}) \\\\\n->   \\hphantom{[\\mathtt{0x0B}, \\mathsf{leadByte}]} \\,||\\, \\mathsf{I2LEOSP}_{64}(\\mathsf{v}) \\,||\\, \\underline{\\text{ρ}} \\,||\\, \\mathsf{I2LEOSP}_{256}(\\text{ψ}) \\\\\n->   \\hphantom{[\\mathtt{0x0B}, \\mathsf{leadByte}]}\\,[\\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{AssetBase}\\kern0.08em\\star)],&\\!\\!\\!\\text{if } \\mathsf{leadByte} = \\mathtt{0x03}\\text{.} \\\\\n+>   [\\hexint{05}] \\,||\\, \\underline{\\text{ρ}},&\\caseif \\mathsf{leadByte} = \\hexint{02} \\\\\n+>   [\\hexint{0B}, \\mathsf{leadByte}] \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{g}\\star_{\\mathsf{d}}) \\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{pk}\\star_{\\mathsf{d}}) \\\\\n+>   \\hphantom{[\\hexint{0B}, \\mathsf{leadByte}]} \\,||\\, \\mathsf{I2LEOSP}_{64}(\\mathsf{v}) \\,||\\, \\underline{\\text{ρ}} \\,||\\, \\mathsf{I2LEOSP}_{256}(\\text{ψ}) \\\\\n+>   \\hphantom{[\\hexint{0B}, \\mathsf{leadByte}]}\\,[\\,||\\, \\mathsf{LEBS2OSP}_{256}(\\mathsf{AssetBase}\\kern0.08em\\star)],&\\caseif \\mathsf{leadByte} = \\hexint{03}\\text{.} \\\\\n > \\end{cases}$\n \n Define:\n \n * $\\mathsf{H}^{\\text{ψ},\\mathsf{Orchard}}_{\\mathsf{r}\\text{ψ}}(\\underline{\\text{ρ}}, \\mathsf{split\\_flag}) = \\mathsf{ToBase^{Orchard}}(\\mathsf{PRF}^{\\mathsf{expand}}_{\\mathsf{r}\\text{ψ}}([\\mathsf{split\\_domain}] \\,||\\, \\underline{\\text{ρ}}))$ <br>\n   where $\\mathsf{split\\_domain} = \\begin{cases}\n-    \\mathtt{0x09},&\\!\\!\\!\\text{if } \\mathsf{split\\_flag} = 0 \\\\\n-    \\mathtt{0x0A},&\\!\\!\\!\\text{if } \\mathsf{split\\_flag} = 1\\text{.}\n+    \\hexint{09},&\\caseif \\mathsf{split\\_flag} = 0 \\\\\n+    \\hexint{0A},&\\caseif \\mathsf{split\\_flag} = 1\\text{.}\n   \\end{cases}$\n-* $\\mathsf{H^{rivk\\_int}_{rivk\\_ext}}(\\mathsf{ak}, \\mathsf{nk}) = \\mathsf{ToScalar^{Orchard}}(\\mathsf{PRF^{expand}_{rivk\\_ext}}([\\mathtt{0x83}] \\,||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{ak})$\n+* $\\mathsf{H^{rivk\\_int}_{rivk\\_ext}}(\\mathsf{ak}, \\mathsf{nk}) = \\mathsf{ToScalar^{Orchard}}(\\mathsf{PRF^{expand}_{rivk\\_ext}}([\\hexint{83}] \\,||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{ak})$\n   $\\hspace{21.58em} ||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{nk})))$\n-* $\\mathsf{H}^{\\mathsf{rivk\\_ext}}_{\\mathsf{qk}}(\\mathsf{ak}, \\mathsf{nk}) = \\mathsf{ToScalar^{Orchard}}(\\mathsf{PRF^{expand}_{\\rlap{qk}{\\hphantom{rivk\\_ext}}}}([\\mathtt{0x84}] \\,||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{ak})$\n+* $\\mathsf{H}^{\\mathsf{rivk\\_ext}}_{\\mathsf{qk}}(\\mathsf{ak}, \\mathsf{nk}) = \\mathsf{ToScalar^{Orchard}}(\\mathsf{PRF^{expand}_{\\rlap{qk}{\\hphantom{rivk\\_ext}}}}([\\hexint{84}] \\,||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{ak})$\n   $\\hspace{21.58em} ||\\, \\mathsf{I2LEOSP}_{256}(\\mathsf{nk})))$\n * $\\mathcal{G}^{\\mathsf{Orchard}} = \\mathsf{GroupHash}^{\\mathbb{P}}(\\texttt{“z.cash:Orchard”}, \\texttt{“G”})$\n \n@@ -1023,7 +1023,7 @@ $\\begin{array}{l}\n \\wedge\\; \\text{let } \\mathsf{pk_d} = [\\mathsf{ivk}]\\, \\mathsf{g_d} \\\\\n \\wedge\\; \\text{let } \\text{ψ} = \\mathsf{H}^{\\text{ψ}}_{\\mathsf{r}\\text{ψ}}(\\underline{\\text{ρ}}, \\mathsf{split\\_flag}) \\\\\n \\wedge\\; \\text{let } \\mathsf{note\\_repr} = \\big(\\mathsf{repr}_{\\mathbb{P}}(\\mathsf{g_d}), \\mathsf{repr}_{\\mathbb{P}}(\\mathsf{pk_d}), \\mathsf{v}, \\underline{\\text{ρ}}, \\text{ψ}[, \\mathsf{AssetBase}\\kern0.08em\\star]\\big) \\\\\n-\\wedge\\; \\text{let } \\mathsf{rcm} = \\mathsf{H^{rcm,Orchard}_{rseed}}\\big(\\mathtt{0x03}, \\mathsf{note\\_repr}\\big) \\\\\n+\\wedge\\; \\text{let } \\mathsf{rcm} = \\mathsf{H^{rcm,Orchard}_{rseed}}\\big(\\hexint{03}, \\mathsf{note\\_repr}\\big) \\\\\n \\wedge\\; \\text{let } \\mathsf{cm} = \\mathsf{NoteCommit^{Orchard}_{rcm}}(\\mathsf{note\\_repr}) \\\\\n \\wedge\\; \\mathsf{cm} \\neq \\bot \\\\\n \\wedge\\; \\text{let } \\mathsf{cm}_x = \\mathsf{Extract}_{\\mathbb{P}}(\\mathsf{cm}) \\\\\n@@ -1121,11 +1121,11 @@ We prefer to fix this without changing $\\mathsf{NoteCommit^{Orchard}}$ itself.\n Instead we change how $\\mathsf{rcm}$ is computed to be a hash of $\\mathsf{rseed}$ and\n $\\mathsf{noterepr} = (\\mathsf{g}\\star_{\\mathsf{d}}, \\mathsf{pk}\\star_{\\mathsf{d}}, \\mathsf{v}, \\underline{\\text{ρ}}, \\text{ψ}[, \\mathsf{AssetBase}\\kern0.08em\\star])$,\n as detailed in the [Specification] section.\n-Specifically, when $\\mathsf{leadByte} = \\mathtt{0x03}$ we have:\n+Specifically, when $\\mathsf{leadByte} = \\hexint{03}$ we have:\n \n $\\mathsf{rcm} = \\mathsf{H^{rcm,Orchard}_{rseed}}(\\mathsf{leadByte}, \\mathsf{noterepr}) = \\mathsf{ToScalar^{Orchard}}(\\mathsf{PRF^{expand}_{rseed}}(\\mathsf{pre\\_rcm}))$\n \n-$\\text{where } \\mathsf{pre\\_rcm} = [\\mathtt{0x0B}, \\mathsf{leadByte}] \\,||\\, \\mathsf{encode}(\\mathsf{noterepr})$\n+$\\text{where } \\mathsf{pre\\_rcm} = [\\hexint{0B}, \\mathsf{leadByte}] \\,||\\, \\mathsf{encode}(\\mathsf{noterepr})$\n \n Then we view the output of\n $\\mathsf{NoteCommit^{Orchard}_{rcm}}(\\mathsf{noterepr})$\n@@ -1405,7 +1405,7 @@ TBD: explain that such attacks can break Balance and Spendability, including\n Spendability for transactions after switching to the Recovery Protocol.\n \n Note that we can identify the precise set of note commitments for\n-recoverable (lead byte $\\mathtt{0x03}$) Orchard notes, since they are\n+recoverable (lead byte $\\hexint{03}$) Orchard notes, since they are\n the commitments for Orchard outputs of v6 transactions.\n However, we cannot identify the precise set of nullifiers for\n recoverable notes: an Orchard action in a v6 transaction could be"},{"filename":"zips/zip-guide-markdown.md","status":"modified","additions":29,"deletions":0,"patch":"@@ -154,6 +154,35 @@ In general the conventions in the Zcash protocol specification SHOULD be followe\n If you find this difficult, don't worry too much about it in initial drafts; the\n ZIP editors will catch any inconsistencies in review.\n \n+## Macros\n+\n+$\\LaTeX$-style macros are supported in ZIPs. The macros for `zips/<name>.{rst,md}`\n+must be defined in `zips/<name>.macros.tex`. Macros will only be processed for a\n+given document if this file exists.\n+\n+There is a common set of useful macro definitions in `zips/common-macros.tex`,\n+but they will not be used by default. To use them, write this line in\n+`zips/<name>.macros.tex`::\n+\n+    \\includeonly{common-macros}\n+\n+The syntax of macro invocations follows LaTeX and is the same for ZIPs written in\n+reStructuredText and Markdown.\n+\n+For example, the following macro definition:\n+\n+    \\newcommand{\\hello}[1]{Hello, #1!}\n+\n+causes `\\hello{world}` to be replaced with `Hello, world!`. Note that it is more\n+typical to surround argument subsitutions with braces, e.g. `{#1}`, in the case\n+of macros intended for use in LaTeX.\n+\n+Run `./expand_macros.py --help` for more details, including discussion of escaping\n+and corner cases.\n+\n+Macros can be used anywhere in a document for which they are enabled; their use\n+is not restricted to embedded LaTeX.\n+\n ## Notes and warnings\n \n <div class=\"note\"></div>"},{"filename":"zips/zip-guide.rst","status":"modified","additions":41,"deletions":0,"patch":"@@ -163,6 +163,47 @@ In general the conventions in the Zcash protocol specification SHOULD be followe\n If you find this difficult, don't worry too much about it in initial drafts; the\n ZIP editors will catch any inconsistencies in review.\n \n+Macros\n+------\n+\n+$\\LaTeX$-style macros are supported in ZIPs. The macros for ``zips/<name>.{rst,md}``\n+must be defined in a file called ``zips/<name>.macros.tex``. Macros will only be\n+processed for a given document if this file exists.\n+\n+There is a common set of useful macro definitions in ``zips/common-macros.tex``,\n+but they will not be used by default. To use them, write this line in\n+``zips/<name>.macros.tex``::\n+\n+    \\includeonly{common-macros}\n+\n+The syntax of macro invocations follows LaTeX and is the same for ZIPs written in\n+reStructuredText and Markdown.\n+\n+For example, the following macro definition::\n+\n+    \\newcommand{\\hello}[1]{Hello, #1!}\n+\n+causes ``\\hello{world}`` to be replaced with ``Hello, world!``. Note that it is more\n+typical to surround argument subsitutions with braces, e.g. ``{#1}``, in the case\n+of macros intended for use in LaTeX.\n+\n+Run ``./expand_macros.py --help`` for more details, including discussion of escaping\n+and corner cases.\n+\n+Macros can be used anywhere in a document for which they are enabled; their use\n+is not restricted to embedded LaTeX.\n+\n+then ``\\hello{world}`` will be replaced with ``Hello, world!``. Note that it\n+is more typical to surround argument subsitutions with braces, e.g. ``{#1}``, in\n+the case of macros intended for use in LaTeX.\n+\n+To escape the ``\\`` character, use ``\\\\``.\n+\n+Run ``./expand_macros.py --help`` for more details and discussion of corner cases.\n+\n+Macros can be used anywhere in a reStructuredText or Markdown file; their use\n+is not restricted to embedded LaTeX.\n+\n Notes and warnings\n ------------------\n "}],"filesTruncated":false,"preview":{"filename":"zips/draft-ecc-quantum-recoverability.md","title":"Quantum recoverability","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"non-requirements","text":"Non-requirements"},{"level":1,"id":"high-level-summary-of-changes","text":"High-level summary of changes"},{"level":2,"id":"flow-diagram-for-the-orchard-and-orchardzsa-protocols","text":"Flow diagram for the Orchard and OrchardZSA protocols"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"usage-with-frost","text":"Usage with FROST"},{"level":2,"id":"usage-with-hardware-wallets","text":"Usage with hardware wallets"},{"level":2,"id":"specification-updates","text":"Specification Updates"},{"level":3,"id":"changes-to-the-protocol-specification","text":"Changes to the Protocol Specification"},{"level":4,"id":"-31-payment-addresses-and-keys","text":"§ 3.1 ‘Payment Addresses and Keys’"},{"level":4,"id":"-321-note-plaintexts-and-memo-fields","text":"§ 3.2.1 ‘Note Plaintexts and Memo Fields’"},{"level":4,"id":"-412-pseudo-random-functions","text":"§ 4.1.2 ‘Pseudo Random Functions’"},{"level":4,"id":"-423-orchard-key-components","text":"§ 4.2.3 ‘Orchard Key Components’"},{"level":4,"id":"-472-sending-notes-sapling","text":"§ 4.7.2 ‘Sending Notes (Sapling)’"},{"level":4,"id":"-473-sending-notes-orchard","text":"§ 4.7.3 ‘Sending Notes (Orchard)’"},{"level":4,"id":"-482-dummy-notes-sapling","text":"§ 4.8.2 ‘Dummy Notes (Sapling)’"},{"level":4,"id":"-483-dummy-notes-orchard","text":"§ 4.8.3 ‘Dummy Notes (Orchard)’"},{"level":4,"id":"-4202-and--4203-decryption-using-an-incomingfull-viewing-key-sapling-and-orchard","text":"§ 4.20.2 and § 4.20.3 ‘Decryption using an Incoming/Full Viewing Key (Sapling and Orchard)’"},{"level":4,"id":"-53-constants","text":"§ 5.3 ‘Constants’"},{"level":3,"id":"changes-to-zips","text":"Changes to ZIPs"},{"level":4,"id":"zip-32","text":"ZIP 32"},{"level":4,"id":"zip-212","text":"ZIP 212"},{"level":4,"id":"zip-226","text":"ZIP 226"},{"level":1,"id":"rationale","text":"Rationale"},{"level":2,"id":"cryptographic-background","text":"Cryptographic background"},{"level":2,"id":"proposed-recovery-protocol","text":"Proposed Recovery Protocol"},{"level":3,"id":"cost","text":"Cost"},{"level":2,"id":"security-analysis","text":"Security analysis"},{"level":3,"id":"repairing-the-note-commitment-merkle-tree","text":"Repairing the note commitment Merkle tree"},{"level":3,"id":"attacks-against-binding-of-note-commitments","text":"Attacks against binding of note commitments"},{"level":3,"id":"repairing-note-commitments","text":"Repairing note commitments"},{"level":3,"id":"attacks-against-binding-of-ivk","text":"Attacks against binding of ivk"},{"level":2,"id":"informal-security-argument","text":"Informal Security Argument"},{"level":2,"id":"security-argument-for-spendability","text":"Security argument for Spendability"},{"level":2,"id":"effects-of-discrete-log-breaking-attacks-before-the-switch-to-the-recovery-protocol","text":"Effects of discrete-log-breaking attacks before the switch to the Recovery Protocol"},{"level":1,"id":"deployment","text":"Deployment"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":980,"title":"ZIP draft: Tx acceptance / User controls ","author":"PaulLaux","createdAt":"2025-02-18T17:09:46Z","updatedAt":"2025-07-08T08:52:21Z","url":"https://github.com/zcash/zips/pull/980","diffUrl":"https://github.com/zcash/zips/pull/980.diff","headRef":"zip-approval","headRepo":"QED-it/zips","headSha":"034378623ada1fa050feb0189a635d396308f453","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This PR introduces the TX approval / user controls ZIP. ## Motivation In the current version of Zcash, fund transfers occur without the explicit consent of recipients. While this simplicity offers convenience, it creates significant challenges for users of the network (e.g. individuals or businesses","zipRefs":[],"draftRefs":["draft-qedit-tx-user-controls"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-qedit-tx-user-controls.rst","status":"added","additions":211,"deletions":0,"patch":"@@ -0,0 +1,211 @@\n+::\n+\n+  ZIP: Unassigned\n+  Title: Transaction User Controls\n+  Owners: Antoine Rondelet <antoine@qed-it.com>\n+          Pablo Kogan <pablo@qed-it.com>\n+  Status: Draft\n+  Category: Consensus\n+  Created: 2024-12-22\n+  License: MIT\n+  Discussions-To: <https://forum.zcashcommunity.com/t/introducing-transaction-controls-in-zcash/49640>\n+  Pull-Request: TBD\n+\n+\n+Terminology\n+===========\n+\n+The key word \"MUST\" in this document is to be interpreted as described in BCP 14 [#BCP14]_ when, and only when, it appears in all capitals.\n+\n+The term \"network upgrade\" in this document is to be interpreted as described in ZIP 200 [#zip-0200]_.\n+\n+The terms \"Orchard\" and \"Action\" in this document are to be interpreted as described in ZIP 224 [#zip-0224]_.\n+\n+The terms \"Asset\" and \"ZSA\" in this document are to be interpreted as described in ZIP 227 [#zip-0227]_.\n+\n+Abstract\n+========\n+\n+This ZIP specifies user controls for transactions - a mechanism allowing recipients of shielded funds to actively participate in the transfer of Assets by approving or rejecting incoming transactions.\n+\n+Motivation\n+==========\n+\n+In the current version of Zcash, fund transfers occur without the explicit consent of recipients.\n+While this simplicity offers convenience, it creates significant challenges for users of the network (e.g. individuals or businesses).\n+The goal of this ZIP is to design a mechanism where the recipient of shielded funds on Zcash (for any type of ZSA) can confirm (or ‘approve’) the receipt of the funds on chain.\n+This enhancement addresses crucial needs in the Zcash ecosystem, particularly at a time when privacy-preserving assets face increased scrutiny from major cryptocurrency exchanges.\n+The proposed controls offer robust safeguarding solutions while maintaining Zcash’s core privacy features.\n+\n+Overview\n+========\n+\n+In the current Zcash network, users identify each other through addresses. When two parties want to transact, the sender must first obtain the recipient's address, typically through off-chain communications like email or forums. Recipients can control incoming payments by selectively sharing their addresses, effectively \"authorizing\" specific users to send them funds.\n+\n+While some users or organizations (e.g. charities like Turing Trust) may choose to publicly share their addresses, others prefer to restrict address sharing to specific parties. However, this address-based authorization system has several fundamental limitations:\n+\n+1. No Revocation Mechanism: Once an address is shared, recipients cannot revoke a sender's ability to transfer funds. While generating new addresses is possible, the sender can still send funds to old addresses, potentially leading to lost funds.\n+2. Lack of Fine-grained Control: Sharing an address grants unrestricted payment authorization. Recipients cannot set conditions on incoming payments, such as specific amounts or timing restrictions (e.g., for tax purposes).\n+3. Uncontrolled Transitivity: Address sharing is transitive - if recipient Alice shares her address with Bob, who then shares it with Charlie, Charlie gains the same payment capabilities as Bob. This means recipients lose control over who can ultimately send them funds, as their address may be shared (maliciously or through compromise) with unintended parties.\n+\n+Currently, any sender with a recipient's address can unilaterally initiate transfers. This capability creates several risks:\n+\n+- Establishing unwanted links between recipients and a specific Asset\n+- Associating recipients with tainted funds (e.g. for blackmail or reputational damage)\n+- Creating undesired connections between recipients and other users or transactions\n+\n+This unilateral transfer capability presents practical challenges across the ecosystem. Centralized exchanges (CEXs) cannot reject fraudulent deposits, leading some to delist privacy-preserving assets like Zcash [#zcash-delist]_. These delistings impact the entire Zcash community by reducing asset liquidity and accessibility.\n+\n+Furthermore, since recipients are not actively involved in transfers, the sender bears sole responsibility for transaction accuracy. Common mistakes like address typos regularly result in lost funds. While traditional payment systems (e.g. for CHAPS or Faster Payments in the UK) have developed safeguards against such errors (e.g., Confirmation of Payee [#confirmation-of-payee]_), cryptocurrency transfers lack similar protections.\n+\n+This ZIP introduces an interactive protocol requiring recipient approval for asset transfers. The recipient generates an approval signature for the Orchard Action and returns it to the sender, who then submits the complete transaction to the network. The network verifies the approval by checking that the signature was generated by the intended recipient.\n+\n+This double opt-in protocol provides two key benefits:\n+\n+1. Prevention of accidental fund loss (i.e. safeguarding)\n+2. Recipient control over incoming transfers\n+\n+Specification\n+=============\n+\n+Most of the protocol is kept the same as the Orchard protocol released with NU5, except for the following.\n+\n+Approval Signature\n+------------------\n+\n+Let $(\\mathsf{d}, \\mathsf{pk_d})$ be the Orchard shielded payment address of the recipient of the output note of an Orchard Action [#protocol-raw-address]_. Let $\\mathsf{g_d}$ be derived from the diversifier $\\mathsf{d}$ as in §5.4.1.6 of the protocol specification [#protocol-diversify-hash]_. The approval signature is derived as follows:\n+\n+1. The sender sends the $\\mathtt{OrchardActionDescription}$ (the preimage of the message to be signed, as per [#protocol-actions]_) for the recipient to sign.\n+2. The recipient executes the following steps:\n+    - $m \\gets H(\\mathtt{OrchardActionDescription})$\n+    - $r \\overset{R}{\\leftarrow} \\mathbb{Z}_{r_{\\mathbb{P}}}$, where $\\mathbb{Z}_{r_{\\mathbb{P}}}$ is the scalar field of Pallas [#protocol-pallas-vesta]_, and where $\\overset{R}{\\leftarrow}$ denotes a variable assignment uniformly at random from a given set [#protocol-notation]_.\n+    - $u \\gets [r] \\mathsf{g_d}$\n+    - $C \\gets H(g_d || pk_d || u || m) \\mod r_{\\mathbb{P}}$, an element of Pallas' scalar field, and where $H$ is a secure hash function (e.g. SHA256 or Blake2)\n+    - $s \\gets r + C \\cdot \\mathsf{ivk} \\mod r_{\\mathbb{P}}$, an element of Pallas' scalar field\n+    - $\\sigma_{approval} \\gets (u, s)$\n+\n+, and sends $\\sigma_{approval}$ to the sender (off-chain).\n+\n+$\\sigma_{approval}$ is a tuple made of one Pallas point and one element of Pallas' scalar field. Hence, the size of $\\sigma_{approval}$ is 96 bytes.\n+\n+Rationale for Approval Signature\n+````````````````````````````````\n+\n+To prove that the correct recipient of the output notes of an Orchard Action approves (the transfer of funds represented by) the Action, we want to show that the approval signature has been generated with a signing key that is derived from the spending key of the recipient of the output notes of the Action.\n+In other words, we want to prove that the approval signature is generated by the network user who \"knows\" the spending key of the output notes of the Action.\n+Doing so means that only the recipient of the note created in the Orchard Action can approve the payment.\n+\n+To achieve this, we look into the key structure of Zcash Orchard.\n+We know that the Orchard address is of the form: $(\\mathsf{d}, \\mathsf{pk_d})$.\n+These 2 fields, the diversifier and the diversified address, are used by the sender when sending notes.\n+\n+Looking at the Orchard key components derivations, we know that $\\mathsf{pk_d}$ is derived as:\n+$\\mathsf{pk_d} =mathsf{KAOrchard.DerivePublic}(\\mathsf{ivk}, \\mathsf{g_d}) = [\\mathsf{ivk}]\\mathsf{g_d}$ [#protocol-orchard-keys]_.\n+\n+Given that $\\mathsf{ivk}$ is derived from the spending key of the recipient of the funds, we can prove that the recipient of the funds in an Orchard Action is approving the receipt of the funds, by using a proof of knowledge of $\\mathsf{ivk}$.\n+Such proof of knowledge of $\\mathsf{ivk}$ can be obtained by using the Non-Interactive Schnorr Protocol. \n+\n+In fact, such proof of knowledge of $\\mathsf{ivk}$ can be obtained by using a Schnorr Signature on the Action (the message) with $\\mathsf{ivk}$ as signing/secret key and $\\mathsf{g_d}$ as group generator.\n+\n+**Note:** Zcash Orchard already uses a Schnorr-based signature scheme instantiated with the Pallas curve, $\\mathsf{RedPallas}$ [#protocol-redpallas]_.\n+As of NU6, $\\mathsf{RedPallas}$ is used to instantiate $\\mathsf{SpendAuthSig^{Orchard}}$ and $\\mathsf{BindingSig^{Orchard}}$.\n+\n+Modifications to the Orchard Statement/Circuit\n+----------------------------------------------\n+\n+The following steps are added to the Orchard Action statement:\n+\n+Instance:\n+\n+- $\\sigma_{approval}$\n+- $\\mathtt{OrchardActionDescription}$\n+\n+Witness:\n+\n+- $g_d$\n+- $pk_d$\n+\n+Circuit:\n+\n+- $C’ \\gets H(g_d || pk_d || \\sigma_{approval}.u || H(\\mathtt{OrchardActionDescription}))$\n+- $LHS \\gets [\\sigma_{approval}.s]g_d$\n+- $RHS \\gets \\sigma_{approval}.u + [C']pk_d$\n+- $LHS - RHS = 0$\n+\n+\n+**Note:** It is also possible to move these steps into a separate circuit.\n+In this case, the transaction structure needs to be modified to account for an extra proof per action.\n+\n+Rationale for the modifications to the Orchard Statement/Circuit\n+````````````````````````````````````````````````````````````````\n+\n+Upon receipt of the approval signature by the recipient of the funds, the sender could include $\\sigma_{approval}$ along with $g_d$ and $pk_d$ in the transaction to be sent on chain.\n+Indeed, both $g_d$ and $pk_d$ of the recipient are needed by the Zcash validators/miners to verify the approval Schnorr signature on chain.\n+\n+In this case, the Zcash miners could verify the recipient's approval by doing (for each Action in the transaction):\n+\n+1. $C’ \\gets H(g_d || pk_d || \\sigma_{approval}.u || H(\\mathtt{OrchardActionDescription}))$\n+2. $LHS \\gets [\\sigma_{approval}.sigma]g_d$\n+3. $RHS \\gets \\sigma_{approval}.u + [C']pk_d$\n+4. $LHS \\stackrel{?}{=} RHS$. If not, reject transaction.\n+\n+If the signature was generated correctly, $LHS = [r + C * ivk]g_d$ and $RHS =[r]g_d + [C]pk_d$, since a well derived $pk_d$ equals $[ivk]g_d$ we get $RHS = [r]g_d + [C][ivk]g_d \\implies RHS = [r + C * ivk]g_d$.\n+So if all steps are followed properly, $LHS = RHS$ and the signature verification succeeds.\n+\n+However, to verify the signature, Zcash miners need to know which $g_d$ and $pk_d$ to use to verify the approval signatures on each Actions.\n+Disclosing these values leaks \"which Orchard address\" is the recipient of the output notes of an Action.\n+So, unlinkability is affected.\n+\n+Here, the sender needs to include the Orchard address of the recipient for the miners to check approval from the recipient.\n+To fix this, we included the Schnorr signature verification in the Orchard Action circuit directly. This keeps the recipient's $g_d$ and $pk_d$ privy to the transacting parties (i.e. the values remain part of the witness - as currently done in the NU5 protocol).\n+The Zcash miners, just need to verify the Orchard Action proof to make sure the approval signature was:\n+\n+- Properly generated by the recipient of the notes in the Orchard Actions\n+- Properly verified by the sender of the funds\n+\n+Modifications to the Transaction Format\n+---------------------------------------\n+\n+In order to support this ZIP, the transaction format, as specified in [#protocol-tx-encoding]_, must be extended to add the approval signatures, as follows:\n+\n+======================= ================ ============================ ================================================================\n+Bytes                   Name             Data Type                    Description\n+======================= ================ ============================ ================================================================\n+96 * nActionsOrchard    vApprovalSigs    byte[96][nActionsOrchard]    Approval signatures for each Orchard Action\n+======================= ================ ============================ ================================================================ \n+\n+Other Considerations\n+====================\n+\n+Transaction Fees\n+----------------\n+\n+Given the modification of the transaction structure (and the additional bytes), it might be necessary to slightly increase the default transaction fees on Zcash if this ZIP gets implemented.\n+\n+Malicious Recipients\n+--------------------\n+\n+By empowering recipients to approve (or not) incoming transactions, we also give them the ability to withhold their approval.\n+This could be done maliciously to, for instance:\n+\n+1. Block a payment and deny to have received the \"approval request\", then accuse the sender to have failed to settle a contractual obligation.\n+2. Gain information: By receiving the $\\mathtt{OrchardActionDescription}$ to approve, recipients gets to see the nullifier of the input note of the Orchard Action before the rest of the network.\n+\n+References\n+==========\n+\n+.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n+.. [#zip-0200] `ZIP 200: Network Upgrade Mechanism <zip-0200.html>`_\n+.. [#zip-0209] `ZIP 209: Prohibit Negative Shielded Chain Value Pool Balances <zip-0209.html>`_\n+.. [#zip-0224] `ZIP 224: Orchard <zip-0224.html>`_\n+.. [#zip-0227] `ZIP 227: Issuance of Zcash Shielded Assets <zip-0227.html>`_\n+.. [#confirmation-of-payee] `Confirmation of Payee` <https://www.wearepay.uk/what-we-do/overlay-services/confirmation-of-payee/>\n+.. [#zcash-delist] `Important: Potential Binance Delisting` <https://forum.zcashcommunity.com/t/important-potential-binance-delisting/45954>\n+.. [#protocol-actions] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. Section 7.5: Action Description Encoding and Consensus` <https://zips.z.cash/protocol/protocol.pdf#actionencodingandconsensus>\n+.. [#protocol-raw-address] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. 5.6.4.2 Orchard Raw Payment Addresses` <https://zips.z.cash/protocol/protocol.pdf#orchardpaymentaddrencoding>\n+.. [#protocol-diversify-hash] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. 5.4.1.6 DiversifyHashSapling and DiversifyHashOrchard Hash Functions` <https://zips.z.cash/protocol/protocol.pdf#concretediversifyhash>\n+.. [#protocol-pallas-vesta] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. 5.4.9.6 Pallas and Vesta` <https://zips.z.cash/protocol/protocol.pdf#pallasandvesta>\n+.. [#protocol-orchard-keys] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. 4.2.3 Orchard Key Components` <https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents>\n+.. [#protocol-key-agreement] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. 5.4.5.5 Orchard Key Agreement` <https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement>\n+.. [#protocol-tx-encoding] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. 7.1 Transaction Encoding and Consensus` <https://zips.z.cash/protocol/protocol.pdf#txnencoding>\n+.. [#protocol-redpallas] `Zcash Protocol Specification, Version 2024.5.1 [NU6]. 5.4.7 RedDSA, RedJubjub, and RedPallas` <https://zips.z.cash/protocol/protocol.pdf#concretereddsa>\n\\ No newline at end of file"}],"filesTruncated":false,"preview":{"filename":"zips/draft-qedit-tx-user-controls.rst","title":"Transaction User Controls","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"overview","text":"Overview"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"approval-signature","text":"Approval Signature"},{"level":3,"id":"rationale-for-approval-signature","text":"Rationale for Approval Signature"},{"level":2,"id":"modifications-to-the-orchard-statement-circuit","text":"Modifications to the Orchard Statement/Circuit"},{"level":3,"id":"rationale-for-the-modifications-to-the-orchard-statement-circuit","text":"Rationale for the modifications to the Orchard Statement/Circuit"},{"level":2,"id":"modifications-to-the-transaction-format","text":"Modifications to the Transaction Format"},{"level":1,"id":"other-considerations","text":"Other Considerations"},{"level":2,"id":"transaction-fees","text":"Transaction Fees"},{"level":2,"id":"malicious-recipients","text":"Malicious Recipients"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":992,"title":"Support multi-paragraph notes and warnings in Markdown","author":"daira","createdAt":"2025-03-18T19:35:30Z","updatedAt":"2025-06-10T21:22:47Z","url":"https://github.com/zcash/zips/pull/992","diffUrl":"https://github.com/zcash/zips/pull/992.diff","headRef":"md-div-across-paragraphs","headRepo":"daira/zips","headSha":"34ceb8f532404001ac2eb08027871b8b499f92f7","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Also fix the syntax for Markdown references in the ZIP guide.","zipRefs":[325],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"rendered/css/style.css","status":"modified","additions":35,"deletions":12,"patch":"@@ -122,6 +122,10 @@\n     background-color: #6060e0;\n     color: #ffffff;\n   }\n+  div.note-cont + p {\n+    background-color: #6060e0;\n+    color: #ffffff;\n+  }\n   aside.note::before {\n     background-color: #3030e0;\n   }\n@@ -138,6 +142,11 @@\n     color: #ffffff;\n     font-weight: 700;\n   }\n+  div.warning-cont + p {\n+    background-color: #e08060;\n+    color: #ffffff;\n+    font-weight: 700;\n+  }\n   aside.warning::before {\n     background-color: #e05030;\n     font-weight: 800;\n@@ -152,9 +161,15 @@\n   div.note + p > a, a:visited {\n     color: #50e7f4;\n   }\n+  div.note-cont + p > a, a:visited {\n+    color: #50e7f4;\n+  }\n   div.warning + p > a, a:visited {\n     color: #50e7f4;\n   }\n+  div.warning-cont + p > a, a:visited {\n+    color: #50e7f4;\n+  }\n   aside > p > a:hover {\n     color: #90ffff;\n   }\n@@ -181,16 +196,32 @@ aside {\n }\n div.note + p {\n   padding-top: 4.4rem;\n-  padding-left: 0.15rem;\n-  padding-right: 0.15rem;\n+  padding-left: 1rem;\n+  padding-right: 1rem;\n+  padding-bottom: 1.4rem;\n+  margin-top: -2.25rem;\n+  margin-bottom: 1.5rem;\n+}\n+div.note-cont + p {\n+  padding-top: 1rem;\n+  padding-left: 1rem;\n+  padding-right: 1rem;\n   padding-bottom: 1.4rem;\n   margin-top: -2.25rem;\n   margin-bottom: 1.5rem;\n }\n div.warning + p {\n   padding-top: 4.4rem;\n-  padding-left: 0.15rem;\n-  padding-right: 0.15rem;\n+  padding-left: 1rem;\n+  padding-right: 1rem;\n+  padding-bottom: 1.4rem;\n+  margin-top: -2.25rem;\n+  margin-bottom: 1.5rem;\n+}\n+div.warning-cont + p {\n+  padding-top: 1rem;\n+  padding-left: 1rem;\n+  padding-right: 1rem;\n   padding-bottom: 1.4rem;\n   margin-top: -2.25rem;\n   margin-bottom: 1.5rem;\n@@ -245,14 +276,6 @@ aside > p {\n   padding-left: 1rem;\n   padding-right: 1rem;\n }\n-div.note + p {\n-  padding-left: 1rem;\n-  padding-right: 1rem;\n-}\n-div.warning + p {\n-  padding-left: 1rem;\n-  padding-right: 1rem;\n-}\n \n @font-face {\n   font-family: robotolight;"},{"filename":"rendered/zip-0325.html","status":"modified","additions":6,"deletions":3,"patch":"@@ -202,13 +202,16 @@ <h2 id=\"private-usemetadatakeys\"><span class=\"section-heading\">Private-use metad\n <li>Return <span class=\"math\">\\(\\mathsf{CKDreg}(K, \\mathtt{0x7FFFFFFF}, \\mathsf{PrivateUseSubject})\\)</span></li>\n </ul>\n \n-<div class=warning></div>\n+<div class=\"warning\"></div>\n \n <p>It is the responsibility of wallet developers to ensure that they do not use\n colliding <span class=\"math\">\\(\\mathsf{PrivateUseSubject}\\)</span> values, and to analyse their private use for\n any security risks related to potential cross-protocol attacks (in the event that\n-two wallet developers happen to select a colliding <span class=\"math\">\\(\\mathsf{PrivateUseSubject}\\)</span>).\n-Wallet developers that are unwilling to accept these risks SHOULD propose new\n+two wallet developers happen to select a colliding <span class=\"math\">\\(\\mathsf{PrivateUseSubject}\\)</span>).</p>\n+\n+<div class=\"warning-cont\"></div>\n+\n+<p>Wallet developers that are unwilling to accept these risks SHOULD propose new\n standardised metadata protocols instead, to benefit from ecosystem coordination\n and review.</p>\n "},{"filename":"rendered/zip-guide-markdown.html","status":"modified","additions":17,"deletions":9,"patch":"@@ -168,21 +168,29 @@ <h2 id=\"notesandwarnings\"><span class=\"section-heading\">Notes and warnings</span\n \n <div class=\"note\"></div>\n \n-<p>&#8220;<code>.. note::</code>&#8221; in reStructuredText or &#8220;<code>&lt;div class=&quot;note&quot;&gt;&lt;/div&gt;</code>&#8221; in Markdown\n-(followed by a blank line in either case), can be used for an aside from the main\n-text. The rendering of notes is colourful and may be distracting, so they should\n+<p>&#8220;<code>.. note::</code>&#8221; in reStructuredText, or &#8220;<code>&lt;div class=&quot;note&quot;&gt;&lt;/div&gt;</code>&#8221; followed by a\n+blank line in Markdown, can be used for an aside from the main text.</p>\n+\n+<div class=\"note-cont\"></div>\n+\n+<p>The rendering of notes is colourful and may be distracting, so they should\n only be used for important points.</p>\n \n <div class=\"warning\"></div>\n \n-<p>&#8220;<code>.. warning::</code>&#8221; in reStructuredText or &#8220;<code>&lt;div class=&quot;warning&quot;&gt;&lt;/div&gt;</code>&#8221; in\n-Markdown (followed by a blank line in either case), can be used for warnings.\n-Warnings should be used very sparingly — for example to signal that a\n+<p>&#8220;<code>.. warning::</code>&#8221; in reStructuredText, or &#8220;<code>&lt;div class=&quot;warning&quot;&gt;&lt;/div&gt;</code>&#8221; followed\n+by a blank line in Markdown, can be used for warnings.</p>\n+\n+<div class=\"warning-cont\"></div>\n+\n+<p>Warnings should be used very sparingly — for example to signal that a\n entire specification, or part of it, may be inapplicable or could cause\n significant interoperability or security problems. In most cases, a &#8220;MUST&#8221;\n or &#8220;SHOULD&#8221; conformance requirement is more appropriate.</p>\n \n-<p>In Markdown, notes and warnings can currently only be a single paragraph.</p>\n+<p>In Markdown, notes and warnings can be extended across multiple paragraphs by\n+using &#8220;<code>&lt;div class=&quot;note-cont&quot;&gt;&lt;/div&gt;</code>&#8221; or &#8220;<code>&lt;div class=&quot;warning-cont&quot;&gt;&lt;/div&gt;</code>&#8221;\n+(preceded and followed by a blank line) between paragraphs.</p>\n \n <h2 id=\"validmarkup\"><span class=\"section-heading\">Valid markup</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#validmarkup\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n \n@@ -219,9 +227,9 @@ <h2 id=\"citationsandreferences\"><span class=\"section-heading\">Citations and refe\n <pre><code class=\"rst\">.. [#snark] `The Hunting of the Snark &lt;<a href=\"https://www.gutenberg.org/files/29888/29888-h/29888-h.htm\">https://www.gutenberg.org/files/29888/29888-h/29888-h.htm</a>&gt;_. Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n </code></pre>\n \n-<p>or like this in Markdown::</p>\n+<p>or like this in Markdown:</p>\n \n-<pre><code class=\"markdown\">[^snark] [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n+<pre><code class=\"markdown\">[^snark]: [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n </code></pre>\n \n <p>Note that each entry must be on a single line regardless of how long that makes the"},{"filename":"rendered/zip-guide.html","status":"modified","additions":4,"deletions":4,"patch":"@@ -91,14 +91,14 @@\n             </section>\n             <section id=\"notes-and-warnings\"><h3><span class=\"section-heading\">Notes and warnings</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#notes-and-warnings\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <aside class=\"note\">\n-                    <p>\"<code>.. note::</code>\" in reStructuredText or \"<code>&lt;div class=\"note\"&gt;&lt;/div&gt;</code>\" in Markdown (followed by a blank line in either case), can be used for an aside from the main text.</p>\n+                    <p>\"<code>.. note::</code>\" in reStructuredText, or \"<code>&lt;div class=\"note\"&gt;&lt;/div&gt;</code>\" followed by a blank line in Markdown, can be used for an aside from the main text.</p>\n                     <p>The rendering of notes is colourful and may be distracting, so they should only be used for important points.</p>\n                 </aside>\n                 <aside class=\"warning\">\n-                    <p>\"<code>.. warning::</code>\" in reStructuredText, or \"<code>&lt;div class=\"warning\"&gt;&lt;/div&gt;</code>\" in Markdown (followed by a blank line in either case), can be used for warnings.</p>\n+                    <p>\"<code>.. warning::</code>\" in reStructuredText, or \"<code>&lt;div class=\"warning\"&gt;&lt;/div&gt;</code>\" followed by a blank line in Markdown, can be used for warnings.</p>\n                     <p>Warnings should be used very sparingly — for example to signal that a entire specification, or part of it, may be inapplicable or could cause significant interoperability or security problems. In most cases, a \"MUST\" or \"SHOULD\" conformance requirement is more appropriate.</p>\n                 </aside>\n-                <p>In Markdown, notes and warnings can currently only be a single paragraph.</p>\n+                <p>In Markdown, notes and warnings can be extended across multiple paragraphs by using \"<code>&lt;div class=\"note-cont\"&gt;&lt;/div&gt;</code>\" or \"<code>&lt;div class=\"warning-cont\"&gt;&lt;/div&gt;</code>\" (preceded and followed by a blank line) between paragraphs.</p>\n             </section>\n             <section id=\"valid-markup\"><h3><span class=\"section-heading\">Valid markup</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#valid-markup\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>This is optional before publishing a PR, but to check whether a document is valid reStructuredText or Markdown, first install <code>docutils</code> and <code>rst2html5</code>, and build <code>MultiMarkdown-6</code>. E.g. on Debian-based distros:</p>\n@@ -119,7 +119,7 @@\n                 <p>The corresponding entry in the <a href=\"#references\">References</a> section should look like this in reStructuredText:</p>\n                 <pre>.. [#snark] `The Hunting of the Snark &lt;<a href=\"https://www.gutenberg.org/files/29888/29888-h/29888-h.htm\">https://www.gutenberg.org/files/29888/29888-h/29888-h.htm</a>&gt;`_. Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.</pre>\n                 <p>or like this in Markdown:</p>\n-                <pre>[^snark] [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.</pre>\n+                <pre>[^snark]: [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.</pre>\n                 <p>Note that each entry must be on a single line regardless of how long that makes the line. In Markdown there must be a blank line between entries.</p>\n                 <p>The current rendering of a Markdown ZIP reorders the references according to their first use; the rendering of a reStructuredText ZIP keeps them in the same order as in the References section.</p>\n                 <p>To link to another section of the same ZIP, use \"<code>`Section title`_</code>\" in reStructuredText, or \"<code>[Section title]</code>\" in Markdown.</p>"},{"filename":"zips/zip-0325.md","status":"modified","additions":4,"deletions":1,"patch":"@@ -171,12 +171,15 @@ valid hardened child index) within its key tree for this purpose.\n   most 252 bytes that identifies the desired private-use context.\n - Return $\\mathsf{CKDreg}(K, \\mathtt{0x7FFFFFFF}, \\mathsf{PrivateUseSubject})$\n \n-<div class=warning></div>\n+<div class=\"warning\"></div>\n \n It is the responsibility of wallet developers to ensure that they do not use\n colliding $\\mathsf{PrivateUseSubject}$ values, and to analyse their private use for\n any security risks related to potential cross-protocol attacks (in the event that\n two wallet developers happen to select a colliding $\\mathsf{PrivateUseSubject}$).\n+\n+<div class=\"warning-cont\"></div>\n+\n Wallet developers that are unwilling to accept these risks SHOULD propose new\n standardised metadata protocols instead, to benefit from ecosystem coordination\n and review."},{"filename":"zips/zip-guide-markdown.md","status":"modified","additions":16,"deletions":8,"patch":"@@ -158,21 +158,29 @@ ZIP editors will catch any inconsistencies in review.\n \n <div class=\"note\"></div>\n \n-\"`.. note::`\" in reStructuredText or \"`<div class=\"note\"></div>`\" in Markdown\n-(followed by a blank line in either case), can be used for an aside from the main\n-text. The rendering of notes is colourful and may be distracting, so they should\n+\"`.. note::`\" in reStructuredText, or \"`<div class=\"note\"></div>`\" followed by a\n+blank line in Markdown, can be used for an aside from the main text.\n+\n+<div class=\"note-cont\"></div>\n+\n+The rendering of notes is colourful and may be distracting, so they should\n only be used for important points.\n \n <div class=\"warning\"></div>\n \n-\"`.. warning::`\" in reStructuredText or \"`<div class=\"warning\"></div>`\" in\n-Markdown (followed by a blank line in either case), can be used for warnings.\n+\"`.. warning::`\" in reStructuredText, or \"`<div class=\"warning\"></div>`\" followed\n+by a blank line in Markdown, can be used for warnings.\n+\n+<div class=\"warning-cont\"></div>\n+\n Warnings should be used very sparingly — for example to signal that a\n entire specification, or part of it, may be inapplicable or could cause\n significant interoperability or security problems. In most cases, a \"MUST\"\n or \"SHOULD\" conformance requirement is more appropriate.\n \n-In Markdown, notes and warnings can currently only be a single paragraph.\n+In Markdown, notes and warnings can be extended across multiple paragraphs by\n+using \"`<div class=\"note-cont\"></div>`\" or \"`<div class=\"warning-cont\"></div>`\"\n+(preceded and followed by a blank line) between paragraphs.\n \n ## Valid markup\n \n@@ -207,9 +215,9 @@ reStructuredText:\n .. [#snark] `The Hunting of the Snark <https://www.gutenberg.org/files/29888/29888-h/29888-h.htm>_. Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n ```\n \n-or like this in Markdown::\n+or like this in Markdown:\n ```markdown\n-[^snark] [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n+[^snark]: [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n ```\n \n Note that each entry must be on a single line regardless of how long that makes the"},{"filename":"zips/zip-guide.rst","status":"modified","additions":7,"deletions":6,"patch":"@@ -167,23 +167,24 @@ Notes and warnings\n ------------------\n \n .. note::\n-    \"``.. note::``\" in reStructuredText or \"``<div class=\"note\"></div>``\" in\n-    Markdown (followed by a blank line in either case), can be used for an aside\n-    from the main text.\n+    \"``.. note::``\" in reStructuredText, or \"``<div class=\"note\"></div>``\" followed\n+    by a blank line in Markdown, can be used for an aside from the main text.\n \n     The rendering of notes is colourful and may be distracting, so they should\n     only be used for important points.\n \n .. warning::\n     \"``.. warning::``\" in reStructuredText, or \"``<div class=\"warning\"></div>``\"\n-    in Markdown (followed by a blank line in either case), can be used for warnings.\n+    followed by a blank line in Markdown, can be used for warnings.\n \n     Warnings should be used very sparingly — for example to signal that a\n     entire specification, or part of it, may be inapplicable or could cause\n     significant interoperability or security problems. In most cases, a \"MUST\"\n     or \"SHOULD\" conformance requirement is more appropriate.\n \n-In Markdown, notes and warnings can currently only be a single paragraph.\n+In Markdown, notes and warnings can be extended across multiple paragraphs by\n+using \"``<div class=\"note-cont\"></div>``\" or \"``<div class=\"warning-cont\"></div>``\"\n+(preceded and followed by a blank line) between paragraphs.\n \n Valid markup\n ------------\n@@ -221,7 +222,7 @@ reStructuredText::\n \n or like this in Markdown::\n \n-  [^snark] [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n+  [^snark]: [The Hunting of the Snark](https://www.gutenberg.org/files/29888/29888-h/29888-h.htm). Lewis Carroll, with illustrations by Henry Holiday. MacMillan and Co. London. March 29, 1876.\n \n Note that each entry must be on a single line regardless of how long that makes the\n line. In Markdown there must be a blank line between entries."}],"filesTruncated":false,"preview":{"filename":"zips/zip-0325.md","title":"Account Metadata Keys","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"metadata-key-tree","text":"Metadata key tree"},{"level":3,"id":"account-metadata-key","text":"Account Metadata Key"},{"level":3,"id":"inherent-metadata-keys","text":"Inherent metadata keys"},{"level":3,"id":"external-metadata-keys","text":"External metadata keys"},{"level":2,"id":"standardised-metadata-protocols","text":"Standardised metadata protocols"},{"level":2,"id":"private-use-metadata-keys","text":"Private-use metadata keys"},{"level":1,"id":"reference-implementation","text":"Reference implementation"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":968,"title":"ZIP 400: Update wallet schema ","author":"dorianvp","createdAt":"2024-12-19T23:20:57Z","updatedAt":"2025-05-18T18:23:48Z","url":"https://github.com/zcash/zips/pull/968","diffUrl":"https://github.com/zcash/zips/pull/968.diff","headRef":"zip400-update","headRepo":"dorianvp/zips","headSha":"fc1e0e9b9df20cde0f42f24e19429d970ab2a242","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Ref: #964. This PR updates the wallet schema to incorporate all changes across different `zcashd` versions.","zipRefs":[400],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-0400.rst","status":"modified","additions":49,"deletions":44,"patch":"@@ -9,12 +9,6 @@\n   License: MIT\n \n \n-Terminology\n-===========\n-\n-The key words \"MUST\" and \"MAY\" in this document are to be interpreted as described in\n-BCP 14 [#BCP14]_ when, and only when, they appear in all capitals.\n-\n Abstract\n ========\n \n@@ -43,46 +37,57 @@ Here ``zkey`` is the property name located at the first position of the database\n Schema\n ------\n \n-According to Zcash v3.0.0-rc1 the following key-values can be found: the property names in **bold** mean only one instance of this type can exist in the entire database, while the others, suffixed by '\\*' can have multiple instances. Keys and Values columns of the table contain the types that the stored data is representing. Included also there are the variable names hoping it will add some clarity to what the stored data is representing.\n+The following key-values can be found: the property names in **bold** mean only one instance of this type can \n+exist in the entire database, while the others, suffixed by '\\*' can have multiple instances. Keys and Values columns of the table\n+contain the types that the stored data is representing. The Version column indicates the zcashd version(s) that create databases containing this key.\n+Included also there are the variable names hoping it will add some clarity to what the stored data is representing.\n \n .. csv-table::\n-   :header: \"Name\", \"Description\", \"Keys\", \"Values\"\n-\n-   \"acc*\", \"Account data\", \"1. ``string strAccount``\", \"1. ``CAccount account``\"\n-   \"acentry*\", \"Track internal transfers between accounts in the same wallet\", \"1. ``string strAccount``\n-   2. ``uint64_t nNumber``\", \"1. ``CAccountingEntry acentry``\"\n-   \"**bestblock**\", \"The current best block of the blockchain.\", \"\", \"1. ``CBlockLocator locator``\"\n-   \"**chdseed**\", \"Encrypted HD seed\", \"1. ``uint256 seedFp``\", \"1. ``vector<unsigned char> vchCryptedSecret``\"\n-   \"ckey*\", \"Encrypted transparent pubkey and private key.\", \"1. ``vector<unsigned char> vchPubKey``\", \"1. ``vector<unsigned char> vchPrivKey``\"\n-   \"csapzkey*\", \"Encrypted Sapling pubkey and private key.\", \"1. ``libzcash::SaplingIncomingViewingKey ivk``\", \"1. ``libzcash::SaplingExtendedFullViewingKey extfvk`` \n+   :header: \"Version\", \"Name\", \"Description\", \"Keys\", \"Values\"\n+   :align: left\n+\n+   \"3..6\", \"**bestblock**\", \"The current best block of the blockchain.\", \"\", \"1. ``CBlockLocator locator``\"\n+   \"3..4\", \"**chdseed**\", \"Encrypted HD seed. Replaced by ``cmnemonicphrase`` in later versions.\", \"1. ``uint256 seedFp``\", \"1. ``vector<unsigned char> vchCryptedSecret``\"\n+   \"3..6\", \"ckey*\", \"Encrypted transparent pubkey and private key.\", \"1. ``vector<unsigned char> vchPubKey``\", \"1. ``vector<unsigned char> vchPrivKey``\"\n+   \"3..6\", \"csapzkey*\", \"Encrypted Sapling pubkey and private key.\", \"1. ``libzcash::SaplingIncomingViewingKey ivk``\", \"1. ``libzcash::SaplingExtendedFullViewingKey extfvk`` \n    2. ``vector<unsigned char> vchCryptedSecret``\"\n-   \"**cscript**\", \"Serialized script, used inside transaction inputs and outputs\", \"1. ``uint160 hash``\", \"1. ``CScript script``\"\n-   \"czkey*\", \"Encrypted Sprout pubkey and private key.\", \"1. ``libzcash::SproutPaymentAddress addr``\", \"1. ``uint256 rkValue`` \n+   \"3..6\", \"cscript\\*\", \"Serialized redeem script, used inside transaction inputs and outputs\", \"1. ``uint160 hash``\", \"1. ``CScript script``\"\n+   \"3..6\", \"czkey*\", \"Encrypted Sprout pubkey and private key.\", \"1. ``libzcash::SproutPaymentAddress addr``\", \"1. ``uint256 rkValue`` \n    2. ``vector<unsigned char> vchCryptedSecret``\"\n-   \"**defaultkey**\", \"Default Transparent key.\", \"\", \"1. ``CPubKey CWallet::vchDefaultKey``\"\n-   \"destdata*\", \"Adds a destination data tuple to the store.\", \"1. ``std::string strAddress``\n+   \"3..6\", \"**defaultkey**\", \"Default Transparent key.\", \"\", \"1. ``CPubKey CWallet::vchDefaultKey``\"\n+   \"3..6\", \"destdata*\", \"Adds a destination data tuple to the store.\", \"1. ``std::string strAddress``\n    2. ``std::string strKey``\", \"1. ``std::string strValue``\"\n-   \"**hdchain**\", \"Hierarchical Deterministic chain code, derived from   seed.\", \"\", \"1. ``CHDChain chain``\"\n-   \"hdseed*\", \"Hierarchical Deterministic seed. [#zip-0032]_\", \"1. ``uint256 seedFp``\", \"1. ``RawHDSeed rawSeed``\"\n-   \"key*\", \"Transparent pubkey and privkey.\", \"1. ``CPubKey vchPubKey``\", \"1. ``CPrivKey pkey``\"\n-   \"keymeta*\", \"Transparent key metadata.\", \"1. ``CPubKey vchPubKey``\", \"1. ``CKeyMetadata keyMeta``\"\n-   \"**minversion**\", \"Wallet required minimal version.\", \"\", \"\"\n-   \"**mkey**\", \"Master key, used to encrypt public and private keys of the database.\", \"1. ``unsigned int nID``\", \"1. ``CMasterKey kMasterKey``\"\n-   \"name*\", \"Name of an address to insert in the address book.\", \"1. ``string strAddress``\", \"1. ``string strAddress``\"\n-   \"**orderposnext**\", \"Index of next tx.\", \"\", \"1. ``int64_t nOrderPosNext``\"\n-   \"pool*\", \"\", \"1. ``int64_t nIndex``\", \"1. ``CKeyPool keypool``\"\n-   \"purpose*\", \"Short description or identifier of an address.\", \"1. ``string strAddress``\", \"1. ``string strPurpose``\"\n-   \"sapzaddr*\", \"Sapling z-addr Incoming Viewing key and address.\", \"1. ``libzcash::SaplingPaymentAddress addr``\", \"1. ``libzcash::SaplingIncomingViewingKey ivk``\"\n-   \"sapextfvk*\", \"Sapling Extended Full Viewing Key\", \"\", \"\"\n-   \"sapzkey*\", \"Sapling Incoming Viewing Key and Extended Spending Key\", \"1. ``libzcash::SaplingIncomingViewingKey ivk``\", \"1. ``libzcash::SaplingExtendedSpendingKey key``\"\n-   \"tx*\", \"Store all transactions that are related to wallet.\", \"1. ``uint256 hash``\", \"1. ``CWalletTx wtx``\"\n-   \"**version**\", \"The ``CLIENT_VERSION`` from ``clientversion.h``.\", \"\", \"1. ``int nFileVersion``\"\n-   \"vkey*\", \"Sprout Viewing Keys.\", \"1. ``libzcash::SproutViewingKey vk``\", \"1. ``char fYes``\"\n-   \"watchs*\", \"Watch-only t-addresses.\", \"1. ``CScript script``\", \"1. ``char fYes``\"\n-   \"**witnesscachesize**\", \"Shielded Note Witness cache size.\", \"\", \"1. ``int64_t nWitnessCacheSize``\"\n-   \"wkey*\", \"Wallet key.\", \"\", \"\"\n-   \"zkey*\", \"Sprout Payment Address and Spending Key.\", \"1. ``libzcash::SproutPaymentAddress addr``\", \"1. ``libzcash::SproutSpendingKey key``\"\n-   \"zkeymeta*\", \"Sprout Payment Address and key metadata.\", \"1. ``libzcash::SproutPaymentAddress addr``\", \"1. ``CKeyMetadata keyMeta``\"\n+   \"3..6\", \"**hdchain**\", \"Hierarchical Deterministic chain code, derived from seed.\", \"\", \"1. ``CHDChain chain``\"\n+   \"3..4\", \"hdseed*\", \"Hierarchical Deterministic seed. [#zip-0032]_ Replaced by ``mnemonicphrase`` in later versions.\", \"1. ``uint256 seedFp``\", \"1. ``RawHDSeed rawSeed``\"\n+   \"3..6\", \"key*\", \"Transparent pubkey and privkey.\", \"1. ``CPubKey vchPubKey``\", \"1. ``CPrivKey pkey``\"\n+   \"3..6\", \"keymeta*\", \"Transparent key metadata.\", \"1. ``CPubKey vchPubKey``\", \"1. ``CKeyMetadata keyMeta``\"\n+   \"3..6\", \"**minversion**\", \"Wallet required minimal version.\", \"\", \"1. ``int nVersion``\"\n+   \"3..6\", \"**mkey**\", \"Master key, used to encrypt public and private keys of the database.\", \"1. ``unsigned int nID``\", \"1. ``CMasterKey kMasterKey``\"\n+   \"3..6\", \"name*\", \"Name of an address to insert in the address book.\", \"1. ``string strAddress``\", \"1. ``string strAddress``\"\n+   \"3..6\", \"**orderposnext**\", \"Index of next tx.\", \"\", \"1. ``int64_t nOrderPosNext``\"\n+   \"3..6\", \"pool*\", \"\", \"1. ``int64_t nIndex``\", \"1. ``CKeyPool keypool``\"\n+   \"3..6\", \"purpose*\", \"Short description or identifier of an address.\", \"1. ``string strAddress``\", \"1. ``string strPurpose``\"\n+   \"3..6\", \"sapzaddr*\", \"Sapling z-addr Incoming Viewing key and address.\", \"1. ``libzcash::SaplingPaymentAddress addr``\", \"1. ``libzcash::SaplingIncomingViewingKey ivk``\"\n+   \"3..6\", \"sapextfvk*\", \"Sapling Extended Full Viewing Key\", \"1. ``libzcash::SaplingExtendedFullViewingKey extfvk``\", \"1. ``char fYes``\"\n+   \"3..6\", \"sapzkey*\", \"Sapling Incoming Viewing Key and Extended Spending Key\", \"1. ``libzcash::SaplingIncomingViewingKey ivk``\", \"1. ``libzcash::SaplingExtendedSpendingKey key``\"\n+   \"3..6\", \"tx*\", \"All transactions that are related to the wallet.\", \"1. ``uint256 hash``\", \"1. ``CWalletTx wtx``\"\n+   \"3..6\", \"**version**\", \"The ``CLIENT_VERSION`` from ``clientversion.h``.\", \"\", \"1. ``int nFileVersion``\"\n+   \"3..6\", \"vkey*\", \"Sprout Viewing Keys.\", \"1. ``libzcash::SproutViewingKey vk``\", \"1. ``char fYes``\"\n+   \"3..6\", \"watchs*\", \"Watch-only t-addresses.\", \"1. ``CScript script``\", \"1. ``char fYes``\"\n+   \"3..6\", \"**witnesscachesize**\", \"Shielded Note Witness cache size.\", \"\", \"1. ``int64_t nWitnessCacheSize``\"\n+   \"3..6\", \"zkey*\", \"Sprout Payment Address and Spending Key.\", \"1. ``libzcash::SproutPaymentAddress addr``\", \"1. ``libzcash::SproutSpendingKey key``\"\n+   \"3..6\", \"zkeymeta*\", \"Sprout Payment Address and key metadata.\", \"1. ``libzcash::SproutPaymentAddress addr``\", \"1. ``CKeyMetadata keyMeta``\"\n+   \"5..6\", \"**networkinfo**\", \"Network information.\", \"\", \"1. ``string strNetworkInfo``\"\n+   \"5..6\", \"**orchard_note_commitment_tree**\", \"Orchard note commitment tree.\", \"\", \"1. ``OrchardWalletNoteCommitmentTreeWriter noteCommitmentTree``\"\n+   \"5..6\", \"unifiedaccount*\", \"Unified Account metadata.\", \"1. ``ZcashdUnifiedAccountMetadata keyMeta``\", \"1. ``0x00``\"\n+   \"5..6\", \"unifiedfvk*\", \"Unified Full Viewing Key. [#protocol-orchardencodings]_\", \"1. ``libzcash::UFVKId ufvkId``\", \"1. ``string ufvkString``\"\n+   \"5..6\", \"unifiedaddrmeta*\", \"Unified Address metadata, useful for future re-derivation.\", \"1. ``ZcashdUnifiedAddressMetadata addrmeta``\", \"1. ``0x00``\"\n+   \"5..6\", \"**mnemonicphrase**\", \"Mnemonic seed phrase.\", \"1. ``uint256 seedFp``\", \"1. ``MnemonicSeed seed``\"\n+   \"5..6\", \"**cmnemonicphrase**\", \"Encrypted mnemonic seed phrase.\", \"1. ``uint256 seedFp``\", \"1. ``vector<unsigned char> vchCryptedSecret``\"\n+   \"5..6\", \"**mnemonichdchain**\", \"HD chain metadata.\", \"\", \"1. ``CHDChain chain``\"\n+   \"5..6\", \"recipientmapping*\", \"Maps transaction to UA.\", \"1. ``uint256 txid`` \n+   3. ``CSerializeRecipientAddress recipientAddr``\", \"1. ``std::string uaString``\"\n+   \"6\", \"**bestblock_nomerkle**\", \"Replaces ``bestblock``. However, ``bestblock`` is kept empty so that previous versions automatically rescan.\", \"\", \"1. ``CBlockLocator locator``\"\n \n \n Functionality\n@@ -133,16 +138,16 @@ Zcash forum or Community Discord.\n Wallet Encryption\n ^^^^^^^^^^^^^^^^^\n \n-Encryption will not be discussed in this document in detail as it is expected for the algorithm to change in the future according to the Wallet format ZIP issue: [#ZIP400Issue]_.\n+Encryption will not be discussed in this document in detail as it is expected for the algorithm to change in the future according to the Wallet format ZIP issue: [#zips-issue350]_.\n \n \n For a deeper understanding of the current encryption mechanism please refer to [#CrypterCode]_\n \n References\n ==========\n \n-.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n .. [#BDB] `Oracle Berkeley Database <https://www.oracle.com/database/berkeley-db/db.html>`_\n-.. [#ZIP400Issue] `ZIP 400 issue <https://github.com/zcash/zips/issues/350>`_\n+.. [#zips-issue350] `zcash/zips issue #350: [ZIP 400] Format of Zcash wallet.dat files <https://github.com/zcash/zips/issues/350>`_\n .. [#zip-0032] `ZIP 32: Shielded Hierarchical Deterministic Wallets <zip-0032.rst>`_\n .. [#CrypterCode] `Database key encryption implementation <https://github.com/zcash/zcash/blob/master/src/wallet/crypter.h>`_\n+.. [#protocol-orchardencodings] `Zcash Protocol Specification, Version 2024.5.1. Section 5.6.4: Unified and Orchard Encodings <https://zips.z.cash/protocol/protocol.pdf#orchardencodings>`_"}],"filesTruncated":false,"preview":{"filename":"zips/zip-0400.rst","title":"Wallet.dat format","status":"Draft","headings":[{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"schema","text":"Schema"},{"level":2,"id":"functionality","text":"Functionality"},{"level":3,"id":"transactions","text":"Transactions"},{"level":3,"id":"wallet-state-and-transaction-reordering","text":"Wallet state and transaction reordering"},{"level":3,"id":"wallet-recovery","text":"Wallet Recovery"},{"level":3,"id":"wallet-encryption","text":"Wallet Encryption"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":715,"title":"Add repo files to help maintain conventions","author":"AArnott","createdAt":"2023-09-08T12:54:01Z","updatedAt":"2025-05-14T22:48:19Z","url":"https://github.com/zcash/zips/pull/715","diffUrl":"https://github.com/zcash/zips/pull/715.diff","headRef":"gitattributes","headRepo":"AArnott/zips","headSha":"8629e5ff19a44c07e499a89c43f9e72b5ae762d2","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"- `.editorconfig` helps maintain whitespace rules and other things. For now, I'm just setting indenting and trailing whitespace rules. - `.gitattributes` sets checked in line ending rules. Without this file. each git client follows the rules configured by the machine that installed it. This can lead","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":".editorconfig","status":"added","additions":18,"deletions":0,"patch":"@@ -0,0 +1,18 @@\n+# EditorConfig is awesome:http://EditorConfig.org\n+\n+# top-most EditorConfig file\n+root = true\n+\n+[*]\n+indent_style = space\n+insert_final_newline = true\n+trim_trailing_whitespace = true\n+\n+[*.sh]\n+indent_size = 4\n+\n+[*.json]\n+indent_size = 2\n+\n+[*.css]\n+indent_size = 2"},{"filename":".gitattributes","status":"added","additions":7,"deletions":0,"patch":"@@ -0,0 +1,7 @@\n+###############################################################################\n+# Set default behavior to automatically normalize line endings.\n+###############################################################################\n+* text eol=lf\n+\n+# Ensure shell scripts use LF line endings (linux only accepts LF)\n+*.sh eol=lf"}],"filesTruncated":false,"preview":null},{"number":999,"title":"Submit draft of Zcash DAO mechanism","author":"khazaddum","createdAt":"2025-04-01T00:28:49Z","updatedAt":"2025-04-01T00:28:49Z","url":"https://github.com/zcash/zips/pull/999","diffUrl":"https://github.com/zcash/zips/pull/999.diff","headRef":"main","headRepo":"khazaddum/zips","headSha":"8e8de829d96b70b6a316550bed133f2bd1c57ce3","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Per comments received from the [forum post](https://forum.zcashcommunity.com/t/a-proposal-for-a-zcash-dao-mechanism/50754), I am submitting the draft Zcash DAO mechanism as an option for organizing groups withing the Zcash community, present proposals and vote on them using existing tools.","zipRefs":[],"draftRefs":["draft-pitmutt-zcash-dao"],"mapped":true,"claimed":false,"files":[{"filename":"zips/draft-pitmutt-zcash-dao.md","status":"added","additions":164,"deletions":0,"patch":"@@ -0,0 +1,164 @@\n+    ZIP: XXX\n+    Title: Zcash DAO Mechanism\n+    Owners: Rene Vergara <pitmutt@vergara.tech>\n+    Status: Draft\n+    Credits:\n+    Category: Consensus / Process\n+    Created: 2025-03-31\n+    License: MIT\n+    Pull-Request: <https://github.com/zcash/zips/pull/???>\n+\n+# Terminology\n+\n+The key words \"MUST\", \"REQUIRED\", \"MUST NOT\", \"SHOULD\", and \"MAY\" in this document are to be interpreted as described in BCP 14 [^BCP14] when, and only when, they appear in all capitals.\n+\n+The terms \"Mainnet\" and \"Testnet\" in this document are to be interpreted as defined in the Zcash protocol specification [^protocol-networks].\n+\n+# Abstract\n+\n+This proposal specifies a mechanism to manage a Decentralized Autonomous Organization (DAO) using Zcash shielded addresses.\n+\n+# Motivation\n+\n+The Zcash community needs a mechanism to:\n+\n+- Represent different groups of stakeholders\n+- Present proposals to these stakeholders\n+- Hold verifiable votes on the proposals\n+\n+The need to address this problem is pressing, which means that handling this through a protocol change or a network upgrade could not be done in a timely manner.\n+\n+This proposal specifies a Decentralized Autonomous Organization (DAO) with the aim of minimizing any additional development needed to meet the following objectives:\n+\n+1. DAO information is decentralized:\n+1.1. Record membership of the DAO on-chain\n+1.2. Record proposals on-chain\n+1.3. Record votes on-chain\n+1. Use the existing Zcash protocol (does not require ZSAs, programmability or a different transaction format)\n+1. Remain flexible so it's applicable to different stakeholders and governance models\n+1. Existing wallets can interact with the DAO in their current state (Payment URI[^ZIP-321] support is highly recommended)\n+\n+# Specification\n+\n+## Member\n+\n+A member of the DAO is identified by their Zcash unified address and a [minisign](https://jedisct1.github.io/minisign/) public key (UA/PK pair) that is used to validate their interactions with the DAO. This approach minimizes the amount of information that needs to be stored about each member while allowing the DAO to validate that proposals and votes were provided by the member. This approach also allows for recursive structures, where a UA/PK pair may be controlled by a sub-DAO that may define its own separate charter and still participate in the main DAO.\n+\n+## DAO Creation\n+\n+Founding members create 3 Zcash unified addresses with at least an Orchard receiver (optionally, using FROST threshold signatures):\n+\n+- Membership address `MA`: used to record approved voting members of the DAO\n+- Proposals address `PA`: used to record proposals presented to the DAO for voting\n+- Votes address `VA`: used to record votes for proposals\n+\n+Founders create the DAO Charter, a.k.a. \"Proposal 0\" where they determine:\n+\n+- Any requirements for membership\n+- The `MA`, `PA` and `VA`\n+- A process to spend any funds accumulated in these DAO addresses\n+- Voting threshold for proposal approval\n+- Quorum threshold for valid votes\n+- Tie-breaking process\n+- A list of Zcash addresses, one per founding member of the DAO\n+- A minimum amount ZEC may be defined for submissions to `MA`, `PA` and `VA` as a spam-reducing measure\n+- Any other definitions, requirements or processes that the DAO will follow\n+\n+Founders record the charter on the Zcash blockchain by sending a transaction to the `PA` with a memo as defined in **Proposals**.\n+\n+Founders record their signing of the DAO Charter by creating their membership records in `MA` as defined in **Membership**.\n+\n+The founders may now distribute Incoming Viewing Keys of the `MA`, `PA` and `VA` addresses as needed for proposal validation and voting verification.\n+\n+## Membership\n+\n+To request membership, a Zcasher would create a proposal providing their Zcash address, their minisign public key and meet any other requirements established by the DAO Charter. The prospective member would need to request an existing DAO member to submit the proposal to the DAO on their behalf and act as their sponsor. More than one sponsor may be required if Sybil-resistance is a concern.\n+\n+Membership to the DAO is recorded by sending a transaction to `MA` with a memo that contains:\n+\n+- A Zcash unified address\n+- The string `:`\n+- A minisign public key string\n+- The string `:prop:`\n+- The unique identifier of the proposal that established membership\n+- The string `:`\n+- A stable URL to the signature file produced by [minisign](https://jedisct1.github.io/minisign/) as defined in **Signatures** for the file containing the proposal that established membership. For founding members, this is the DAO Charter and each member submits their signature of the charter.\n+\n+A membership record is valid when:\n+- The Zcash unified address matches the one listed in the referred proposal.\n+- The minisign public key string matches the one listed in the referred proposal.\n+- The referred proposal is approved per the charter.\n+\n+## Proposals\n+\n+A proposal is submitted to the DAO by sending a transaction to the `PA` with a memo that contains:\n+\n+- The string `minisig:`\n+- A stable URL to the signature file produced by [minisign](https://jedisct1.github.io/minisign/) as defined in **Signatures** for the file containing the proposal\n+- The string `:`\n+- A stable URL to the contents of the file\n+\n+The proposal file must include:\n+- a unique identifier\n+- a blockheight in the future specifying the deadline for voting\n+- an indexed list of options for voting, for example:\n+\n+```\n+Options:\n+1. Yes\n+2. No\n+```\n+\n+Or\n+\n+```\n+Options:\n+1. Red\n+2. Green\n+3. Blue\n+```\n+\n+## Voting\n+A vote is submitted to the DAO by sending a transaction to the `VA` with a memo that contains:\n+\n+- The string `vote:`\n+- The integer representing the option defined in the proposal\n+- The string `:prop:`\n+- The unique identifier of the proposal being voted on\n+- The string `:minisig:`\n+- A stable URL to the signature file produced by `minisign` as defined in **Signatures** for the file containing the proposal corresponding to the identifier\n+\n+Votes are valid when:\n+- The Zcash transaction containing the memo was mined on a block less than or equal to the deadline blockheight specified in the proposal\n+- The option included in the memo is one of the valid options specified in the proposal\n+- The submitted signature file can be validated by using the public key listed in `MA` for the DAO member.\n+- Any further requirements established in the DAO charter are met\n+\n+If multiple valid votes are found on chain for the same member, the vote with the highest blockheight that is less than the deadline blockheight is considered the final vote and supersedes all previous votes.\n+\n+## Signatures\n+\n+Cryptographic signatures are used to ensure:\n+- Only proposals that are submitted by DAO members are considered valid, all other submissions to `PA` are ignored.\n+- Only votes that are submitted by DAO members are considered valid, all other submissions to `VA` are ignored.\n+- Only membership entries that are submitted by an authorized DAO member (per the Charter) are considered valid, all other submissions to `MA` are ignored.\n+\n+We propose using the [minisign](https://jedisct1.github.io/minisign/) tool to create these signatures, as it uses modern cryptography, is readily available in multiple platforms, there are implementations in [multiple programming languages](https://wasmer.io/jedisct1/minisign) and it produces a small file that is easy to distribute.\n+\n+For our proposed DAO mechanism, the `minisign` feature of \"trusted comment\" (a line of text that is cryptographically signed) must be used to include the Zcash address of the member originating the signature. If the trusted comment in the signature file does not include a Zcash address or if the Zcash address is not registered in `MA`, the signature is not valid.\n+\n+# Acknowledgements\n+\n+This proposed DAO mechanism was inspired by the proposed ECC on-chain accountable voting[^draft-ecc-accountable-voting].\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+\n+[^protocol]: [Zcash Protocol Specification, Version 2024.5.1 or later](protocol/protocol.pdf)\n+\n+[^protocol-networks]: [Zcash Protocol Specification, Version 2024.5.1. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)\n+\n+[^ZIP-321]: [ZIP-321: Payment Request URIs](zip-0321.rst)\n+\n+[^draft-ecc-accountable-voting]: [On-chain Accountable Voting](draft-ecc-onchain-accountable-voting.md)"}],"filesTruncated":false,"preview":{"filename":"zips/draft-pitmutt-zcash-dao.md","title":"Zcash DAO Mechanism","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"member","text":"Member"},{"level":2,"id":"dao-creation","text":"DAO Creation"},{"level":2,"id":"membership","text":"Membership"},{"level":2,"id":"proposals","text":"Proposals"},{"level":2,"id":"voting","text":"Voting"},{"level":2,"id":"signatures","text":"Signatures"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"},{"level":2,"id":"footnote-label","text":"Footnotes"}],"format":"md"}},{"number":965,"title":"ZIP draft: Complete Diversifier Hashing for Sapling","author":"daira","createdAt":"2024-11-29T04:55:04Z","updatedAt":"2025-02-20T13:11:04Z","url":"https://github.com/zcash/zips/pull/965","diffUrl":"https://github.com/zcash/zips/pull/965.diff","headRef":"draft-ecc-complete-diversifier-map-for-sapling","headRepo":"daira/zips","headSha":"e9a0b7cad758895e7a937c533424c0219fa0ee74","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[],"draftRefs":["draft-ecc-complete-diversifier-map-for-sapling"],"mapped":true,"claimed":false,"files":[{"filename":"README.rst","status":"modified","additions":1,"deletions":0,"patch":"@@ -203,6 +203,7 @@ be deleted.\n \n   <embed><table>\n     <tr> <th>Title</th> </tr>\n+    <tr> <td class=\"left\"><a href=\"zips/draft-ecc-complete-diversifier-map-for-sapling.rst\">Complete Diversifier Hashing for Sapling</a></td>\n     <tr> <td class=\"left\"><a href=\"zips/draft-noamchom67-manufacturing-consent.rst\">Manufacturing Consent; Re-Establishing a Dev Fund for ECC, ZF, ZCG, Qedit, FPF, and ZecHub</a></td>\n     <tr> <td class=\"left\"><a href=\"zips/draft-nuttycom-funding-allocation.rst\">Block Reward Allocation for Non-Direct Development Funding</a></td>\n     <tr> <td class=\"left\"><a href=\"zips/draft-zf-community-dev-fund-2-proposal.rst\">Establishing a Hybrid Dev Fund for ZF, ZCG and a Dev Fund Reserve</a></td>"},{"filename":"rendered/draft-ecc-complete-diversifier-map-for-sapling.html","status":"added","additions":447,"deletions":0,"patch":"@@ -0,0 +1,447 @@\n+<!DOCTYPE html>\n+<html>\n+<head>\n+    <title>Draft ecc-complete-diversifier-map-for-sapling: Complete Diversifier Hashing for Sapling</title>\n+    <meta charset=\"utf-8\" />\n+    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css\" integrity=\"sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+\" crossorigin=\"anonymous\">\n+    <script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js\" integrity=\"sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg\" crossorigin=\"anonymous\"></script>\n+    <script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js\" integrity=\"sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk\" crossorigin=\"anonymous\" onload=\"renderMathInElement(document.body);\"></script>\n+<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"stylesheet\" href=\"css/style.css\"></head>\n+<body>\n+    <section>\n+        <pre>ZIP: XXX\n+Title: Complete Diversifier Hashing for Sapling\n+Owners: Daira-Emma Hopwood &lt;daira-emma@electriccoin.co&gt;\n+        Jack Grigg &lt;str4d@electriccoin.co&gt;\n+Credits: Kris Nuttycombe\n+Status: Draft\n+Category: Standards / Wallet\n+Created: 2024-11-28\n+License: MIT</pre>\n+        <section id=\"terminology\"><h2><span class=\"section-heading\">Terminology</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#terminology\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <p>The key words \"MUST\" and \"MUST NOT\" in this document are to be interpreted as described in BCP 14 <a id=\"footnote-reference-1\" class=\"footnote_reference\" href=\"#bcp14\">1</a> when, and only when, they appear in all capitals.</p>\n+            <p>The character § is used when referring to sections of the Zcash Protocol Specification. <a id=\"footnote-reference-2\" class=\"footnote_reference\" href=\"#protocol\">2</a></p>\n+            <p>The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in § 3.12 ‘Mainnet and Testnet’. <a id=\"footnote-reference-3\" class=\"footnote_reference\" href=\"#protocol-networks\">3</a></p>\n+            <p>\"Jubjub\" refers to the elliptic curve defined in § 5.4.9.3 ‘Jubjub’ <a id=\"footnote-reference-4\" class=\"footnote_reference\" href=\"#protocol-jubjub\">6</a>.</p>\n+            <p>A \"Newly Allowed Sapling payment address\" is a Sapling payment address that would have been invalid prior to the activation of this proposal; that is, it has a diversifier\n+                <span class=\"math\">\\(\\mathsf{d}\\)</span>\n+             for which\n+                <span class=\"math\">\\(\\mathsf{DiversifyHash^{Sapling}}(d) = \\bot\\!\\)</span>\n+            .</p>\n+            <p>A \"Newly Allowed Unified Address\" is a Unified Address <a id=\"footnote-reference-5\" class=\"footnote_reference\" href=\"#zip-0316\">20</a> that contains a Sapling Receiver corresponding to a Newly Allowed Sapling payment address.</p>\n+            <p>A \"Newly Allowed Address\" is either a Newly Allowed Sapling payment address or a Newly Allowed Unified Address.</p>\n+            <p>Conversely, a \"Previously Allowed Address\" is an address that would have been valid prior to the activation of this proposal.</p>\n+        </section>\n+        <section id=\"abstract\"><h2><span class=\"section-heading\">Abstract</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#abstract\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <p>In the original design of the Sapling shielded protocol, the \"diversifier hash\" that maps diversifiers to curve points was an incomplete function, resulting in about half of all diversifiers being invalid. When used with ZIP 32 <a id=\"footnote-reference-6\" class=\"footnote_reference\" href=\"#zip-0032\">19</a>, this also meant that about half of all Sapling diversifier <em>indices</em> are invalid for a given ZIP 32 account.</p>\n+            <p>This proposal modifies the Sapling protocol to make its diversifier hash complete.</p>\n+        </section>\n+        <section id=\"motivation\"><h2><span class=\"section-heading\">Motivation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#motivation\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <p>TBD</p>\n+        </section>\n+        <section id=\"requirements\"><h2><span class=\"section-heading\">Requirements</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#requirements\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <ul>\n+                <li>Every Sapling diversifier must be valid, except possibly with negligible probability.</li>\n+                <li>The proposal must be compatible with the existing Sapling design in the sense that any previously valid diversifier (or diversifier index) will map to the same curve point. This property is essential in order for previously created addresses and notes to remain valid.</li>\n+                <li>The algorithm to decrypt a note ciphertext must be well specified for transactions at any block height.</li>\n+                <li>The compatibility consequences of the proposal must be comprehensively assessed and clearly documented.</li>\n+            </ul>\n+        </section>\n+        <section id=\"specification\"><h2><span class=\"section-heading\">Specification</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#specification\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <p>In general terms, the method used to make the diversifier hash complete while maintaining compatibility is to:</p>\n+            <ul>\n+                <li>Define a new complete random-oracle encoding into the large prime-order subgroup of the Jubjub curve, following RFC 9380 <a id=\"footnote-reference-7\" class=\"footnote_reference\" href=\"#rfc-9380\">10</a>.</li>\n+                <li>Define the new diversifier hash to use the incomplete group hash for diversifiers on which it is defined (i.e. does not return\n+                    <span class=\"math\">\\(\\bot\\!\\)</span>\n+                ), and the complete random-oracle encoding otherwise.</li>\n+            </ul>\n+            <p>The complete random-oracle encoding is considered to be an internal primitive only. It MUST NOT be used for diversifiers on which the incomplete group hash is defined.</p>\n+            <section id=\"complete-random-oracle-encoding\"><h3><span class=\"section-heading\">Complete random-oracle encoding</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#complete-random-oracle-encoding\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>In this section we define a complete random-oracle encoding <a id=\"footnote-reference-8\" class=\"footnote_reference\" href=\"#rfc-9380-random-oracle-encodings\">11</a> to the large prime-order subgroup of the Jubjub curve, named\n+                    <span class=\"math\">\\(\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}\\!\\)</span>\n+                . The name of this primitive follows the convention established by section 8.10 of RFC 9380 <a id=\"footnote-reference-9\" class=\"footnote_reference\" href=\"#rfc-9380-suite-id-naming-conventions\">15</a>. This name is not by itself sufficient to infer all parameters of the algorithm, which are specified below.</p>\n+                <p>Jubjub is a complete Twisted Edwards curve (<a id=\"footnote-reference-10\" class=\"footnote_reference\" href=\"#protocol-jubjub\">6</a> and section 4.3.4 of <a id=\"footnote-reference-11\" class=\"footnote_reference\" href=\"#bl2017\">22</a>). In that respect it is similar mathematically to the widely deployed Edwards25519 curve; in particular it is birationally equivalent to a particular Montgomery curve <a id=\"footnote-reference-12\" class=\"footnote_reference\" href=\"#protocol-ecbackground\">8</a>. RFC 9380 already defines a parameter suite for Edwards25519 <a id=\"footnote-reference-13\" class=\"footnote_reference\" href=\"#rfc-9380-suites-for-curve25519-and-e\">14</a>, and we specify\n+                    <span class=\"math\">\\(\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}\\)</span>\n+                 following the same pattern for Jubjub.</p>\n+                <p>The result of the encoding is obtained by adding (as curve points) the results of two maps to the Montgomery curve using the method of section 6.8.2 of RFC 9380 <a id=\"footnote-reference-14\" class=\"footnote_reference\" href=\"#rfc-9380-elligator-2-method-2\">12</a> (based on the Elligator 2 mapping originally defined in <a id=\"footnote-reference-15\" class=\"footnote_reference\" href=\"#bhkl13\">21</a>), and then applying a rational map to obtain a Jubjub curve point.</p>\n+                <p>Let\n+                    <span class=\"math\">\\(q_{\\mathbb{J}}\\!\\)</span>\n+                ,\n+                    <span class=\"math\">\\(a_{\\mathbb{J}}\\!\\)</span>\n+                ,\n+                    <span class=\"math\">\\(d_{\\mathbb{J}}\\!\\)</span>\n+                , and\n+                    <span class=\"math\">\\(h_{\\mathbb{J}}\\)</span>\n+                 be as defined in <a id=\"footnote-reference-16\" class=\"footnote_reference\" href=\"#protocol-jubjub\">6</a>.</p>\n+                <p>\n+                    <span class=\"math\">\\(\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}\\)</span>\n+                 is defined as an RFC-9380 hash-to-curve suite <a id=\"footnote-reference-17\" class=\"footnote_reference\" href=\"#rfc-9380-suites-for-hashing\">13</a> with the following parameters:</p>\n+                <ul>\n+                    <li>encoding type: hash_to_curve</li>\n+                    <li>\n+                        <dl>\n+                            <dt>\n+                                <span class=\"math\">\\(E\\)</span>\n+                             (Jubjub curve):</dt>\n+                            <dd>\n+                                <span class=\"math\">\\(a_{\\mathbb{J}} \\cdot v^2 + w^2 = 1 + d_{\\mathbb{J}} \\cdot v^2 \\cdot w^2\\)</span>\n+                            </dd>\n+                        </dl>\n+                    </li>\n+                    <li>\n+                        <span class=\"math\">\\(p\\)</span>\n+                     (field characteristic):\n+                        <span class=\"math\">\\(q_{\\mathbb{J}}\\)</span>\n+                    </li>\n+                    <li>\n+                        <span class=\"math\">\\(m\\)</span>\n+                     (field extension degree):\n+                        <span class=\"math\">\\(1\\)</span>\n+                    </li>\n+                    <li>\n+                        <span class=\"math\">\\(k\\)</span>\n+                     (target security bits):\n+                        <span class=\"math\">\\(256\\)</span>\n+                    </li>\n+                    <li>expand_message: expand_message_xmd</li>\n+                    <li>\n+                        <span class=\"math\">\\(H\\!\\)</span>\n+                    : BLAKE2b-512</li>\n+                    <li>\n+                        <span class=\"math\">\\(L\\!\\)</span>\n+                    :\n+                        <span class=\"math\">\\(64\\)</span>\n+                    </li>\n+                    <li>\n+                        <span class=\"math\">\\(f\\!\\)</span>\n+                    : Twisted Edwards Elligator 2 method (Section 6.8.2) <a id=\"footnote-reference-18\" class=\"footnote_reference\" href=\"#rfc-9380-elligator-2-method-2\">12</a></li>\n+                    <li>\n+                        <dl>\n+                            <dt>\n+                                <span class=\"math\">\\(M\\)</span>\n+                             (Montgomery curve birationally equivalent to Jubjub):</dt>\n+                            <dd>\n+                                <span class=\"math\">\\(K \\cdot t^2 = s^3 + J \\cdot s^2 + s\\!\\)</span>\n+                            , where\n+                                <span class=\"math\">\\(K = 1\\)</span>\n+                             and\n+                                <span class=\"math\">\\(J = 40962\\)</span>\n+                            </dd>\n+                        </dl>\n+                    </li>\n+                    <li>rational_map: the \"generic mapping\" defined in RFC 9380 Appendix D.1 <a id=\"footnote-reference-19\" class=\"footnote_reference\" href=\"#rfc-9380-generic-mapping-from-montgo\">16</a></li>\n+                    <li>\n+                        <span class=\"math\">\\(Z\\!\\)</span>\n+                    :\n+                        <span class=\"math\">\\(5\\)</span>\n+                    </li>\n+                    <li>h_eff:\n+                        <span class=\"math\">\\(h_{\\mathbb{J}}\\)</span>\n+                    </li>\n+                </ul>\n+                <p>Non-normative notes:</p>\n+                <ul>\n+                    <li>RFC 9380 denotes Twisted Edwards coordinates as\n+                        <span class=\"math\">\\((v, w)\\)</span>\n+                     rather than\n+                        <span class=\"math\">\\((u, v)\\!\\)</span>\n+                    . This is because it uses\n+                        <span class=\"math\">\\(u\\)</span>\n+                     for the field element to be mapped.</li>\n+                    <li>The \"security level\"\n+                        <span class=\"math\">\\(k\\)</span>\n+                     in the RFC is taken to be\n+                        <span class=\"math\">\\(256\\!\\)</span>\n+                    . Although this is greater than the conjectured\n+                        <span class=\"math\">\\(125.8\\!\\)</span>\n+                    -bit security of the Jubjub curve against generic (e.g. Pollard rho) attacks <a id=\"footnote-reference-20\" class=\"footnote_reference\" href=\"#hopwood2022\">23</a>, this design choice is consistent with other instances of extracting a uniformly distributed field element from a hash output in the Sapling protocol, such as\n+                        <span class=\"math\">\\(\\mathsf{ToScalar^{Sapling}}\\)</span>\n+                     defined in <a id=\"footnote-reference-21\" class=\"footnote_reference\" href=\"#protocol-saplingkeycomponents\">4</a>, and\n+                        <span class=\"math\">\\(\\mathsf{H}^⊛\\)</span>\n+                     defined in <a id=\"footnote-reference-22\" class=\"footnote_reference\" href=\"#protocol-concretereddsa\">5</a>.</li>\n+                    <li>\n+                        <span class=\"math\">\\(Z = 5\\)</span>\n+                     is the non-square of smallest magnitude in\n+                        <span class=\"math\">\\(\\mathbb{F}_{q_{\\mathbb{J}}}\\!\\)</span>\n+                    , as determined using the algorithm in RFC 9380 Appendix H.3 <a id=\"footnote-reference-23\" class=\"footnote_reference\" href=\"#rfc-9380-finding-z-for-elligator-2\">17</a>.</li>\n+                    <li>The \"generic mapping\" from the Montgomery form to the Twisted Edwards form of Jubjub defined by <a id=\"footnote-reference-24\" class=\"footnote_reference\" href=\"#rfc-9380-generic-mapping-from-montgo\">16</a> is <em>not</em> the same mapping as\n+                        <span class=\"math\">\\(\\mathsf{MontToCtEdwards}\\)</span>\n+                     <a id=\"footnote-reference-25\" class=\"footnote_reference\" href=\"#protocol-cctconversion\">9</a> used in the Sapling circuits. The latter has an extra factor of\n+                        <span class=\"math\">\\({}^{+}\\kern-0.65em\\sqrt{-40964}\\)</span>\n+                     in the resulting first Twisted Edwards coordinate.</li>\n+                </ul>\n+            </section>\n+            <section id=\"diversifyhash\"><h3><span class=\"section-heading\">DiversifyHash</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#diversifyhash\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>Modify the definition of\n+                    <span class=\"math\">\\(\\mathsf{DiversifyHash}\\)</span>\n+                 in ... [#protocol-concretediversifyhash].</p>\n+                <div class=\"math\">\\(\\begin{array}{l}\n+  \\mathsf{CompleteHash}(M \\;{\\small ⦂}\\; \\mathbb{B}^{{\\tiny\\mathbb{Y}}[\\mathbb{N}]}) \\;{\\small ⦂}\\; \\mathbb{J}^{(r)} = \\\\\n+  \\hspace{2em}\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}\\mathsf{.hash\\_to\\_curve}(\\texttt{\"z.cash:Sapling-gd-jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_\"}, M) \\\\\n+  \\\\\n+  \\mathsf{IncompleteHash}(M \\;{\\small ⦂}\\; \\mathbb{B}^{{\\tiny\\mathbb{Y}}[\\mathbb{N}]}) \\;{\\small ⦂}\\; \\mathbb{J}^{(r)} = \\\\\n+  \\hspace{2em}\\mathsf{GroupHash}_U^{\\mathbb{J}^{(r)*}}(\\texttt{\"Zcash\\_gd\"}, \\mathsf{LEBS2OSP_{\\ell_d}}(\\mathsf{d})) \\\\[2ex]\n+\\end{array}\\)</div>\n+                <div class=\"math\">\\(\\mathsf{DiversifyHash^{Sapling}}(\\mathsf{d}) = \\begin{cases}\n+  \\mathsf{IncompleteHash}(\\mathsf{d}),&amp;\\text{ if } \\mathsf{IncompleteHash}(\\mathsf{d}) \\neq \\bot \\\\\n+  \\mathsf{CompleteHash}(\\mathsf{d}),  &amp;\\text{ if } \\mathsf{IncompleteHash}(\\mathsf{d}) = \\bot \\text{ and } \\mathsf{CompleteHash}(\\mathsf{d}) \\neq \\bot \\hspace{6em} \\\\\n+  \\mathsf{CompleteHash}(\\texttt{\"\"}), &amp;\\text{ otherwise}\n+\\end{cases}\\)</div>\n+            </section>\n+            <section id=\"note-encryption\"><h3><span class=\"section-heading\">Note Encryption</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#note-encryption\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>In <a id=\"footnote-reference-26\" class=\"footnote_reference\" href=\"#protocol-saplingandorchardinband\">7</a>, TBD</p>\n+                <blockquote>\n+                    <p>let DiversifyHash be DiversifyHashSapling in section 5.4.1.6 ‘DiversifyHashSapling and DiversifyHashOrchard Hash Functions’</p>\n+                </blockquote>\n+                <p>on page 76, or DiversifyHashOrchard in the same section.</p>\n+                <p>Modify the uses of\n+                    <span class=\"math\">\\(\\mathsf{DiversifyHash^{Sapling}}\\)</span>\n+                 as follows: TBD</p>\n+            </section>\n+        </section>\n+        <section id=\"specification-compatibility-and-deployment\"><h2><span class=\"section-heading\">Specification: Compatibility and Deployment</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#specification-compatibility-and-deployment\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <p>The deployment of this proposal raises compatibility issues that will require coordination and interoperability testing between wallet implementors. These issues arise from the fact that Newly Allowed Addresses will only work for receiving funds in wallets that have upgraded their note decryption procedure to support them.</p>\n+            <p>We believe that these issues will be tractable for the following reason: under the assumption that a wallet implementation only exposes Newly Allowed Addresses <em>after</em> it has upgraded its note decryption procedure (as required by this specification), then it will always be able to receive funds on those addresses.</p>\n+            <p>There are a couple of caveats to the above observation:</p>\n+            <ul>\n+                <li>If a seed is shared between wallets, or if it is restored from one wallet implementation to another, the above argument may not hold. Sharing seeds concurrently between wallets is generally not a supported usage model, but the ability to restore a seed from one wallet implementation to another is critical to ensure preservation of funds in the case of data loss or device malfunction, and also helps to avoid user lock-in to a particular wallet implementation. A past version of a wallet counts as a different wallet implementation for this discussion.</li>\n+                <li>In the case of a host wallet that relies on a connected hardware wallet to guard spend authority, care must be taken to ensure that both wallet implementations support Newly Allowed Addresses if either exposes them. Otherwise, the implementation that lacks this support could either fail to correctly receive a payment sent to a Newly Allowed Address, or be unable to spend notes from such payments.</li>\n+            </ul>\n+            <p>Note that, according to the specification of the Sapling shielded protocol prior to activation of this proposal, the behaviour of a wallet receiving a decrypted note plaintext with a diversifier that it sees as invalid was well defined: it MUST ignore that note plaintext, as though it were not intended for the wallet. So the user-visible symptom of the situations described above will be that some funds are apparently missing.</p>\n+            <p>These observations apply to scanning using either an incoming viewing key or an outgoing viewing key, because both will perform the check for diversifier validity.</p>\n+            <p>In order to mitigate these issues we impose the following requirements:</p>\n+            <ul>\n+                <li>If a wallet implementation cannot reliably determine that the Sapling outputs of transactions mined since the activation height of this proposal have been scanned by an implementation that would correctly receive payments on Newly Allowed Addresses —possibly including past iterations of the same wallet from which state has been preserved— it MUST rescan them.</li>\n+                <li>If a host wallet and hardware wallet are connected, each SHOULD verify that the other fully supports Newly Allowed Addresses before exposing any such address.</li>\n+            </ul>\n+            <p>After activation of this proposal, a wallet implementation MAY continue to expose only Previously Allowed Addresses. We nevertheless suggest that wallet implementors consider the simplicity advantages of generating addresses at contiguous ZIP 32 indices.</p>\n+            <section id=\"activation-height\"><h3><span class=\"section-heading\">Activation height</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#activation-height\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>Let\n+                    <span class=\"math\">\\(\\mathsf{ZipXXXActivationHeight}\\)</span>\n+                 be the activation height of this ZIP. This is the first block height at which it is valid for transactions in that block to contain Sapling Output descriptions with notes having a diversifier that would be invalid using the previously defined\n+                    <span class=\"math\">\\(\\mathsf{DiversifyHash^{Sapling}}\\!\\)</span>\n+                .</p>\n+                <p>TODO:</p>\n+                <ul>\n+                    <li>replace XXX with the number of this ZIP;</li>\n+                    <li>specify the activation height.</li>\n+                </ul>\n+                <p>Before the activation height, all relevant wallets implementing Sapling should be confirmed to have implemented this specification (apart from setting the mainnet height) and to have conducted comprehensive interoperability testing.</p>\n+            </section>\n+        </section>\n+        <section id=\"test-vectors\"><h2><span class=\"section-heading\">Test Vectors</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#test-vectors\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <section id=\"jubjub-xmd-blake2b-ell2-ro-test-vectors\"><h3><span class=\"section-heading\"><code>jubjub_XMD:BLAKE2b_ELL2_RO_</code> test vectors</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#jubjub-xmd-blake2b-ell2-ro-test-vectors\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>The following test vectors for the\n+                    <span class=\"math\">\\(\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}\\)</span>\n+                 random-oracle encoding are in the same format as used in Appendix J of RFC 9380 <a id=\"footnote-reference-27\" class=\"footnote_reference\" href=\"#rfc-9380-appendix-j\">18</a>. Note that the RFC denotes Twisted Edwards coordinates as <code>(x,</code> <code>y)</code> rather than\n+                    <span class=\"math\">\\((u, v)\\!\\)</span>\n+                .</p>\n+                <pre>suite   = jubjub_XMD:BLAKE2b_ELL2_RO_\n+dst     = z.cash:Sapling-gd-jubjub_XMD:BLAKE2b_ELL2_RO_\n+\n+msg     = Everybody's right to beautiful, radiant things\n+P.x     =\n+P.y     =\n+u[0]    =\n+u[1]    =\n+Q0.x    =\n+Q0.y    =\n+Q1.x    =\n+Q1.y    =</pre>\n+            </section>\n+            <section id=\"diversifyhash-test-vectors\"><h3><span class=\"section-heading\">DiversifyHash test vectors</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#diversifyhash-test-vectors\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>TBD</p>\n+            </section>\n+        </section>\n+        <section id=\"reference-implementation\"><h2><span class=\"section-heading\">Reference Implementation</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#reference-implementation\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <p>TBD</p>\n+        </section>\n+        <section id=\"references\"><h2><span class=\"section-heading\">References</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#references\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n+            <table id=\"bcp14\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>1</th>\n+                        <td><a href=\"https://www.rfc-editor.org/info/bcp14\">Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>2</th>\n+                        <td><a href=\"protocol/protocol.pdf\">Zcash Protocol Specification, Version 2024.6.0 or later [NU6]</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol-networks\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>3</th>\n+                        <td><a href=\"protocol/protocol.pdf#networks\">Zcash Protocol Specification, Version 2024.6.0. Section 3.12: Mainnet and Testnet</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol-saplingkeycomponents\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>4</th>\n+                        <td><a href=\"protocol/protocol.pdf#saplingkeycomponents\">Zcash Protocol Specification, Version 2024.6.0. Section 4.2.2: Sapling Key Components</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol-concretereddsa\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>5</th>\n+                        <td><a href=\"protocol/protocol.pdf#concretereddsa\">Zcash Protocol Specification, Version 2024.6.0. Section 5.4.7: RedDSA, RedJubjub, and RedPallas</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol-jubjub\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>6</th>\n+                        <td><a href=\"protocol/protocol.pdf#jubjub\">Zcash Protocol Specification, Version 2024.6.0. Section 5.4.9.3: Jubjub</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol-saplingandorchardinband\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>7</th>\n+                        <td><a href=\"protocol/protocol.pdf#saplingandorchardinband\">Zcash Protocol Specification, Version 2024.6.0. Section 4.20: In-band secret distribution (Sapling and Orchard)</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol-ecbackground\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>8</th>\n+                        <td><a href=\"protocol/protocol.pdf#ecbackground\">Zcash Protocol Specification, Version 2024.6.0. Appendix A.2: Elliptic curve background</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"protocol-cctconversion\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>9</th>\n+                        <td><a href=\"protocol/protocol.pdf#cctconversion\">Zcash Protocol Specification, Version 2024.6.0. Appendix A.3.3.3: ctEdwards ↔ Montgomery conversion</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>10</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#elligator2\">RFC 9380: Hashing to Elliptic Curves</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-random-oracle-encodings\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>11</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#name-random-oracle-encodings\">RFC 9380: Hashing to Elliptic Curves. Section 2.2.3: Random Oracle Encodings</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-elligator-2-method-2\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>12</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#elligator-2-method-2\">RFC 9380: Hashing to Elliptic Curves. Section 6.8.2: Elligator 2 Method</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-suites-for-hashing\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>13</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-hashing\">RFC 9380: Hashing to Elliptic Curves. Section 8: Suites for Hashing</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-suites-for-curve25519-and-e\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>14</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-curve25519-and-e\">RFC 9380: Hashing to Elliptic Curves. Section 8.5: Suites for curve25519 and edwards25519</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-suite-id-naming-conventions\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>15</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#name-suite-id-naming-conventions\">RFC 9380: Hashing to Elliptic Curves. Section 8.10: Suite ID Naming Conventions</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-generic-mapping-from-montgo\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>16</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#name-generic-mapping-from-montgo\">RFC 9380: Hashing to Elliptic Curves. Appendix D.1: Generic Mapping from Montgomery to Twisted Edwards</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-finding-z-for-elligator-2\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>17</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#name-finding-z-for-elligator-2\">RFC 9380: Hashing to Elliptic Curves. Appendix H.3: Finding Z for Elligator 2</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"rfc-9380-appendix-j\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>18</th>\n+                        <td><a href=\"https://www.rfc-editor.org/rfc/rfc9380.html#appendix-J\">RFC 9380: Hashing to Elliptic Curves. Appendix J: Suite Test Vectors</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"zip-0032\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>19</th>\n+                        <td><a href=\"zip-0032\">ZIP 32: Shielded Hierarchical Deterministic Wallets</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"zip-0316\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>20</th>\n+                        <td><a href=\"zip-0316\">ZIP 316: Unified Addresses and Unified Viewing Keys</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"bhkl13\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>21</th>\n+                        <td><a href=\"https://doi.org/10.1145/2508859.2516734\">Daniel Bernstein, Mike Hamburg, Anna Krasnova, and Tanje Lange. \"Elligator: elliptic-curve points indistinguishable from uniform random strings.\" DOI:10.1145/2508859.2516734</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"bl2017\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>22</th>\n+                        <td><a href=\"https://eprint.iacr.org/2017/293\">Daniel Bernstein and Tanja Lange. \"Montgomery curves and the Montgomery ladder.\" Cryptology ePrint Archive: Report 2017/293</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"hopwood2022\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>23</th>\n+                        <td><a href=\"https://raw.githubusercontent.com/daira/zcash-security/main/zcash-security.pdf\">Daira Hopwood. \"Understanding the Security of Zcash.\" Slide 41: Cryptographic strength</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+        </section>\n+    </section>\n+</body>\n+</html>\n\\ No newline at end of file"},{"filename":"rendered/index.html","status":"modified","additions":1,"deletions":0,"patch":"@@ -149,6 +149,7 @@\n             <p>These are works-in-progress, and may never be assigned ZIP numbers if their ideas become obsoleted or abandoned. Do not assume that these drafts will exist in perpetuity; instead assume that they will either move to a numbered ZIP, or be deleted.</p>\n         <embed><table>\n   <tr> <th>Title</th> </tr>\n+  <tr> <td class=\"left\"><a href=\"draft-ecc-complete-diversifier-map-for-sapling\">Complete Diversifier Hashing for Sapling</a></td>\n   <tr> <td class=\"left\"><a href=\"draft-noamchom67-manufacturing-consent\">Manufacturing Consent; Re-Establishing a Dev Fund for ECC, ZF, ZCG, Qedit, FPF, and ZecHub</a></td>\n   <tr> <td class=\"left\"><a href=\"draft-nuttycom-funding-allocation\">Block Reward Allocation for Non-Direct Development Funding</a></td>\n   <tr> <td class=\"left\"><a href=\"draft-zf-community-dev-fund-2-proposal\">Establishing a Hybrid Dev Fund for ZF, ZCG and a Dev Fund Reserve</a></td>"},{"filename":"zips/draft-ecc-complete-diversifier-map-for-sapling.rst","status":"added","additions":312,"deletions":0,"patch":"@@ -0,0 +1,312 @@\n+::\n+\n+  ZIP: XXX\n+  Title: Complete Diversifier Hashing for Sapling\n+  Owners: Daira-Emma Hopwood <daira-emma@electriccoin.co>\n+          Jack Grigg <str4d@electriccoin.co>\n+  Credits: Kris Nuttycombe\n+  Status: Draft\n+  Category: Standards / Wallet\n+  Created: 2024-11-28\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\" and \"MUST NOT\" in this document are to be interpreted as described in\n+BCP 14 [#BCP14]_ when, and only when, they appear in all capitals.\n+\n+The character § is used when referring to sections of the Zcash Protocol Specification.\n+[#protocol]_\n+\n+The terms \"Mainnet\" and \"Testnet\" are to be interpreted as described in\n+§ 3.12 ‘Mainnet and Testnet’. [#protocol-networks]_\n+\n+\"Jubjub\" refers to the elliptic curve defined in § 5.4.9.3 ‘Jubjub’ [#protocol-jubjub]_.\n+\n+A \"Newly Allowed Sapling payment address\" is a Sapling payment address that would have\n+been invalid prior to the activation of this proposal; that is, it has a diversifier\n+$\\mathsf{d}$ for which $\\mathsf{DiversifyHash^{Sapling}}(d) = \\bot$.\n+\n+A \"Newly Allowed Unified Address\" is a Unified Address [#zip-0316]_ that contains a\n+Sapling Receiver corresponding to a Newly Allowed Sapling payment address.\n+\n+A \"Newly Allowed Address\" is either a Newly Allowed Sapling payment address or a\n+Newly Allowed Unified Address.\n+\n+Conversely, a \"Previously Allowed Address\" is an address that would have been valid\n+prior to the activation of this proposal.\n+\n+\n+Abstract\n+========\n+\n+In the original design of the Sapling shielded protocol, the \"diversifier hash\" that maps\n+diversifiers to curve points was an incomplete function, resulting in about half of all\n+diversifiers being invalid. When used with ZIP 32 [#zip-0032]_, this also meant that about\n+half of all Sapling diversifier *indices* are invalid for a given ZIP 32 account.\n+\n+This proposal modifies the Sapling protocol to make its diversifier hash complete.\n+\n+\n+Motivation\n+==========\n+\n+TBD\n+\n+\n+Requirements\n+============\n+\n+* Every Sapling diversifier must be valid, except possibly with negligible probability.\n+* The proposal must be compatible with the existing Sapling design in the sense that any\n+  previously valid diversifier (or diversifier index) will map to the same curve point.\n+  This property is essential in order for previously created addresses and notes to\n+  remain valid.\n+* The algorithm to decrypt a note ciphertext must be well specified for transactions\n+  at any block height.\n+* The compatibility consequences of the proposal must be comprehensively assessed and\n+  clearly documented.\n+\n+\n+Specification\n+=============\n+\n+In general terms, the method used to make the diversifier hash complete while maintaining\n+compatibility is to:\n+\n+* Define a new complete random-oracle encoding into the large prime-order subgroup of the\n+  Jubjub curve, following RFC 9380 [#RFC-9380]_.\n+* Define the new diversifier hash to use the incomplete group hash for diversifiers on which\n+  it is defined (i.e. does not return $\\bot$), and the complete random-oracle encoding\n+  otherwise.\n+\n+The complete random-oracle encoding is considered to be an internal primitive only. It\n+MUST NOT be used for diversifiers on which the incomplete group hash is defined.\n+\n+Complete random-oracle encoding\n+-------------------------------\n+\n+In this section we define a complete random-oracle encoding [#RFC-9380-random-oracle-encodings]_\n+to the large prime-order subgroup of the Jubjub curve, named $\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}$.\n+The name of this primitive follows the convention established by section 8.10 of RFC 9380\n+[#RFC-9380-suite-id-naming-conventions]_. This name is not by itself sufficient to infer all\n+parameters of the algorithm, which are specified below.\n+\n+Jubjub is a complete Twisted Edwards curve ([#protocol-jubjub]_ and section 4.3.4 of [#BL2017]_).\n+In that respect it is similar mathematically to the widely deployed Edwards25519 curve; in\n+particular it is birationally equivalent to a particular Montgomery curve [#protocol-ecbackground]_.\n+RFC 9380 already defines a parameter suite for Edwards25519 [#RFC-9380-suites-for-curve25519-and-e]_,\n+and we specify $\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}$ following the same pattern for Jubjub.\n+\n+The result of the encoding is obtained by adding (as curve points) the results of two maps to\n+the Montgomery curve using the method of section 6.8.2 of RFC 9380 [#RFC-9380-elligator-2-method-2]_\n+(based on the Elligator 2 mapping originally defined in [#BHKL13]_), and then applying a rational\n+map to obtain a Jubjub curve point.\n+\n+Let $q_{\\mathbb{J}}$, $a_{\\mathbb{J}}$, $d_{\\mathbb{J}}$, and $h_{\\mathbb{J}}$ be as defined in\n+[#protocol-jubjub]_.\n+\n+$\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}$ is defined as an RFC-9380 hash-to-curve suite\n+[#RFC-9380-suites-for-hashing]_ with the following parameters:\n+\n+* encoding type: hash_to_curve\n+* $E$ (Jubjub curve):\n+    $a_{\\mathbb{J}} \\cdot v^2 + w^2 = 1 + d_{\\mathbb{J}} \\cdot v^2 \\cdot w^2$\n+* $p$ (field characteristic): $q_{\\mathbb{J}}$\n+* $m$ (field extension degree): $1$\n+* $k$ (target security bits): $256$\n+* expand_message: expand_message_xmd\n+* $H$: BLAKE2b-512\n+* $L$: $64$\n+* $f$: Twisted Edwards Elligator 2 method (Section 6.8.2) [#RFC-9380-elligator-2-method-2]_\n+* $M$ (Montgomery curve birationally equivalent to Jubjub):\n+    $K \\cdot t^2 = s^3 + J \\cdot s^2 + s$, where $K = 1$ and $J = 40962$\n+* rational_map: the \"generic mapping\" defined in RFC 9380 Appendix D.1 [#RFC-9380-generic-mapping-from-montgo]_\n+* $Z$: $5$\n+* h_eff: $h_{\\mathbb{J}}$\n+\n+Non-normative notes:\n+\n+* RFC 9380 denotes Twisted Edwards coordinates as $(v, w)$ rather than $(u, v)$.\n+  This is because it uses $u$ for the field element to be mapped.\n+* The \"security level\" $k$ in the RFC is taken to be $256$. Although this is greater than\n+  the conjectured $125.8$-bit security of the Jubjub curve against generic (e.g. Pollard rho)\n+  attacks [#Hopwood2022]_, this design choice is consistent with other instances of extracting\n+  a uniformly distributed field element from a hash output in the Sapling protocol, such as\n+  $\\mathsf{ToScalar^{Sapling}}$ defined in [#protocol-saplingkeycomponents]_, and\n+  $\\mathsf{H}^⊛$ defined in [#protocol-concretereddsa]_.\n+* $Z = 5$ is the non-square of smallest magnitude in $\\mathbb{F}_{q_{\\mathbb{J}}}$, as\n+  determined using the algorithm in RFC 9380 Appendix H.3 [#RFC-9380-finding-z-for-elligator-2]_.\n+* The \"generic mapping\" from the Montgomery form to the Twisted Edwards form of Jubjub\n+  defined by [#RFC-9380-generic-mapping-from-montgo]_ is *not* the same mapping as\n+  $\\mathsf{MontToCtEdwards}$ [#protocol-cctconversion]_ used in the Sapling circuits.\n+  The latter has an extra factor of ${}^{+}\\kern-0.65em\\sqrt{-40964}$ in the resulting first\n+  Twisted Edwards coordinate.\n+\n+DiversifyHash\n+-------------\n+\n+Modify the definition of $\\mathsf{DiversifyHash}$ in ... [#protocol-concretediversifyhash].\n+\n+.. math::\n+\n+    \\begin{array}{l}\n+      \\mathsf{CompleteHash}(M \\;{\\small ⦂}\\; \\mathbb{B}^{{\\tiny\\mathbb{Y}}[\\mathbb{N}]}) \\;{\\small ⦂}\\; \\mathbb{J}^{(r)} = \\\\\n+      \\hspace{2em}\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}\\mathsf{.hash\\_to\\_curve}(\\texttt{\"z.cash:Sapling-gd-jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_\"}, M) \\\\\n+      \\\\\n+      \\mathsf{IncompleteHash}(M \\;{\\small ⦂}\\; \\mathbb{B}^{{\\tiny\\mathbb{Y}}[\\mathbb{N}]}) \\;{\\small ⦂}\\; \\mathbb{J}^{(r)} = \\\\\n+      \\hspace{2em}\\mathsf{GroupHash}_U^{\\mathbb{J}^{(r)*}}(\\texttt{\"Zcash\\_gd\"}, \\mathsf{LEBS2OSP_{\\ell_d}}(\\mathsf{d})) \\\\[2ex]\n+    \\end{array}\n+ \n+    \\mathsf{DiversifyHash^{Sapling}}(\\mathsf{d}) = \\begin{cases}\n+      \\mathsf{IncompleteHash}(\\mathsf{d}),&\\text{ if } \\mathsf{IncompleteHash}(\\mathsf{d}) \\neq \\bot \\\\\n+      \\mathsf{CompleteHash}(\\mathsf{d}),  &\\text{ if } \\mathsf{IncompleteHash}(\\mathsf{d}) = \\bot \\text{ and } \\mathsf{CompleteHash}(\\mathsf{d}) \\neq \\bot \\hspace{6em} \\\\\n+      \\mathsf{CompleteHash}(\\texttt{\"\"}), &\\text{ otherwise}\n+    \\end{cases}\n+\n+\n+Note Encryption\n+---------------\n+\n+In [#protocol-saplingandorchardinband]_, TBD\n+\n+    let DiversifyHash be DiversifyHashSapling in section 5.4.1.6 ‘DiversifyHashSapling and DiversifyHashOrchard Hash Functions’\n+\n+on page 76, or DiversifyHashOrchard in the same section.\n+\n+Modify the uses of $\\mathsf{DiversifyHash^{Sapling}}$ as follows: TBD\n+\n+\n+Specification: Compatibility and Deployment\n+===========================================\n+\n+The deployment of this proposal raises compatibility issues that will require coordination\n+and interoperability testing between wallet implementors. These issues arise from the fact\n+that Newly Allowed Addresses will only work for receiving funds in wallets that have upgraded\n+their note decryption procedure to support them.\n+\n+We believe that these issues will be tractable for the following reason: under the assumption\n+that a wallet implementation only exposes Newly Allowed Addresses *after* it has upgraded\n+its note decryption procedure (as required by this specification), then it will always be\n+able to receive funds on those addresses.\n+\n+There are a couple of caveats to the above observation:\n+\n+* If a seed is shared between wallets, or if it is restored from one wallet implementation\n+  to another, the above argument may not hold. Sharing seeds concurrently between wallets\n+  is generally not a supported usage model, but the ability to restore a seed from one wallet\n+  implementation to another is critical to ensure preservation of funds in the case of data\n+  loss or device malfunction, and also helps to avoid user lock-in to a particular wallet\n+  implementation. A past version of a wallet counts as a different wallet implementation for\n+  this discussion.\n+* In the case of a host wallet that relies on a connected hardware wallet to guard spend authority,\n+  care must be taken to ensure that both wallet implementations support Newly Allowed Addresses\n+  if either exposes them. Otherwise, the implementation that lacks this support could either\n+  fail to correctly receive a payment sent to a Newly Allowed Address, or be unable to spend\n+  notes from such payments.\n+\n+Note that, according to the specification of the Sapling shielded protocol prior to activation\n+of this proposal, the behaviour of a wallet receiving a decrypted note plaintext with a\n+diversifier that it sees as invalid was well defined: it MUST ignore that note plaintext,\n+as though it were not intended for the wallet. So the user-visible symptom of the situations\n+described above will be that some funds are apparently missing.\n+\n+These observations apply to scanning using either an incoming viewing key or an outgoing\n+viewing key, because both will perform the check for diversifier validity.\n+\n+In order to mitigate these issues we impose the following requirements:\n+\n+* If a wallet implementation cannot reliably determine that the Sapling outputs of transactions\n+  mined since the activation height of this proposal have been scanned by an implementation\n+  that would correctly receive payments on Newly Allowed Addresses —possibly including past\n+  iterations of the same wallet from which state has been preserved— it MUST rescan them.\n+* If a host wallet and hardware wallet are connected, each SHOULD verify that the other fully\n+  supports Newly Allowed Addresses before exposing any such address.\n+\n+After activation of this proposal, a wallet implementation MAY continue to expose only\n+Previously Allowed Addresses. We nevertheless suggest that wallet implementors consider the\n+simplicity advantages of generating addresses at contiguous ZIP 32 indices.\n+\n+Activation height\n+-----------------\n+\n+Let $\\mathsf{ZipXXXActivationHeight}$ be the activation height of this ZIP. This is the first block\n+height at which it is valid for transactions in that block to contain Sapling Output descriptions\n+with notes having a diversifier that would be invalid using the previously defined\n+$\\mathsf{DiversifyHash^{Sapling}}$.\n+\n+TODO:\n+\n+* replace XXX with the number of this ZIP;\n+* specify the activation height.\n+\n+Before the activation height, all relevant wallets implementing Sapling should be confirmed\n+to have implemented this specification (apart from setting the mainnet height) and to have\n+conducted comprehensive interoperability testing.\n+\n+\n+Test Vectors\n+============\n+\n+``jubjub_XMD:BLAKE2b_ELL2_RO_`` test vectors\n+--------------------------------------------\n+\n+The following test vectors for the $\\texttt{jubjub\\_XMD:BLAKE2b\\_ELL2\\_RO\\_}$ random-oracle\n+encoding are in the same format as used in Appendix J of RFC 9380 [#RFC-9380-appendix-J]_.\n+Note that the RFC denotes Twisted Edwards coordinates as ``(x,`` ``y)`` rather than $(u, v)$.\n+\n+::\n+\n+  suite   = jubjub_XMD:BLAKE2b_ELL2_RO_\n+  dst     = z.cash:Sapling-gd-jubjub_XMD:BLAKE2b_ELL2_RO_\n+\n+  msg     = Everybody's right to beautiful, radiant things\n+  P.x     =\n+  P.y     =\n+  u[0]    =\n+  u[1]    =\n+  Q0.x    =\n+  Q0.y    =\n+  Q1.x    =\n+  Q1.y    =\n+\n+DiversifyHash test vectors\n+--------------------------\n+\n+TBD\n+\n+\n+Reference Implementation\n+========================\n+\n+TBD\n+\n+\n+References\n+==========\n+\n+.. [#BCP14] `Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\" <https://www.rfc-editor.org/info/bcp14>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2024.6.0 or later [NU6] <protocol/protocol.pdf>`_\n+.. [#protocol-networks] `Zcash Protocol Specification, Version 2024.6.0. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#protocol-saplingkeycomponents] `Zcash Protocol Specification, Version 2024.6.0. Section 4.2.2: Sapling Key Components <protocol/protocol.pdf#saplingkeycomponents>`_\n+.. [#protocol-concretereddsa] `Zcash Protocol Specification, Version 2024.6.0. Section 5.4.7: RedDSA, RedJubjub, and RedPallas <protocol/protocol.pdf#concretereddsa>`_\n+.. [#protocol-jubjub] `Zcash Protocol Specification, Version 2024.6.0. Section 5.4.9.3: Jubjub <protocol/protocol.pdf#jubjub>`_\n+.. [#protocol-saplingandorchardinband] `Zcash Protocol Specification, Version 2024.6.0. Section 4.20: In-band secret distribution (Sapling and Orchard) <protocol/protocol.pdf#saplingandorchardinband>`_\n+.. [#protocol-ecbackground] `Zcash Protocol Specification, Version 2024.6.0. Appendix A.2: Elliptic curve background <protocol/protocol.pdf#ecbackground>`_\n+.. [#protocol-cctconversion] `Zcash Protocol Specification, Version 2024.6.0. Appendix A.3.3.3: ctEdwards ↔ Montgomery conversion <protocol/protocol.pdf#cctconversion>`_\n+.. [#RFC-9380] `RFC 9380: Hashing to Elliptic Curves <https://www.rfc-editor.org/rfc/rfc9380.html#elligator2>`_\n+.. [#RFC-9380-random-oracle-encodings] `RFC 9380: Hashing to Elliptic Curves. Section 2.2.3: Random Oracle Encodings <https://www.rfc-editor.org/rfc/rfc9380.html#name-random-oracle-encodings>`_\n+.. [#RFC-9380-elligator-2-method-2] `RFC 9380: Hashing to Elliptic Curves. Section 6.8.2: Elligator 2 Method <https://www.rfc-editor.org/rfc/rfc9380.html#elligator-2-method-2>`_\n+.. [#RFC-9380-suites-for-hashing] `RFC 9380: Hashing to Elliptic Curves. Section 8: Suites for Hashing <https://www.rfc-editor.org/rfc/rfc9380.html#https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-hashing>`_\n+.. [#RFC-9380-suites-for-curve25519-and-e] `RFC 9380: Hashing to Elliptic Curves. Section 8.5: Suites for curve25519 and edwards25519 <https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-curve25519-and-e>`_\n+.. [#RFC-9380-suite-id-naming-conventions] `RFC 9380: Hashing to Elliptic Curves. Section 8.10: Suite ID Naming Conventions <https://www.rfc-editor.org/rfc/rfc9380.html#name-suite-id-naming-conventions>`_\n+.. [#RFC-9380-generic-mapping-from-montgo] `RFC 9380: Hashing to Elliptic Curves. Appendix D.1: Generic Mapping from Montgomery to Twisted Edwards <https://www.rfc-editor.org/rfc/rfc9380.html#name-generic-mapping-from-montgo>`_\n+.. [#RFC-9380-finding-z-for-elligator-2] `RFC 9380: Hashing to Elliptic Curves. Appendix H.3: Finding Z for Elligator 2 <https://www.rfc-editor.org/rfc/rfc9380.html#name-finding-z-for-elligator-2>`_\n+.. [#RFC-9380-appendix-j] `RFC 9380: Hashing to Elliptic Curves. Appendix J: Suite Test Vectors <https://www.rfc-editor.org/rfc/rfc9380.html#appendix-J>`_\n+.. [#zip-0032] `ZIP 32: Shielded Hierarchical Deterministic Wallets <zip-0032.rst>`_\n+.. [#zip-0316] `ZIP 316: Unified Addresses and Unified Viewing Keys <zip-0316.rst>`_\n+.. [#BHKL13] `Daniel Bernstein, Mike Hamburg, Anna Krasnova, and Tanje Lange. \"Elligator: elliptic-curve points indistinguishable from uniform random strings.\" DOI:10.1145/2508859.2516734 <https://doi.org/10.1145/2508859.2516734>`_ \n+.. [#BL2017] `Daniel Bernstein and Tanja Lange. \"Montgomery curves and the Montgomery ladder.\" Cryptology ePrint Archive: Report 2017/293 <https://eprint.iacr.org/2017/293>`_\n+.. [#Hopwood2022] `Daira Hopwood. \"Understanding the Security of Zcash.\" Slide 41: Cryptographic strength <https://raw.githubusercontent.com/daira/zcash-security/main/zcash-security.pdf>`_"}],"filesTruncated":false,"preview":{"filename":"zips/draft-ecc-complete-diversifier-map-for-sapling.rst","title":"Complete Diversifier Hashing for Sapling","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"complete-random-oracle-encoding","text":"Complete random-oracle encoding"},{"level":2,"id":"diversifyhash","text":"DiversifyHash"},{"level":2,"id":"note-encryption","text":"Note Encryption"},{"level":1,"id":"specification-compatibility-and-deployment","text":"Specification: Compatibility and Deployment"},{"level":2,"id":"activation-height","text":"Activation height"},{"level":1,"id":"test-vectors","text":"Test Vectors"},{"level":2,"id":"jubjub-xmd-blake2b-ell2-ro-test-vectors","text":"jubjub_XMD:BLAKE2b_ELL2_RO_ test vectors"},{"level":2,"id":"diversifyhash-test-vectors","text":"DiversifyHash test vectors"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":899,"title":"initial MMP protocol draft","author":"kdenhartog","createdAt":"2024-08-27T07:46:26Z","updatedAt":"2025-01-28T20:22:38Z","url":"https://github.com/zcash/zips/pull/899","diffUrl":"https://github.com/zcash/zips/pull/899.diff","headRef":"kdh/media-memos-protocol","headRepo":"kdenhartog/zips","headSha":"3defc4054f832710319371ad0fa2e729f7fef453","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This is the initial protocol specification for sending messages larger than 512 characters between two parties using ZIP-302 via IPFS. It's been intentionally designed to be extensible in order to allow for additional storage, cryptography, or other more comprehensive protocol behavior while still k","zipRefs":[302],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zips/zip-tbd1.md","status":"added","additions":132,"deletions":0,"patch":"@@ -0,0 +1,132 @@\n+    ZIP: Unassigned\n+    Title: Media Memos Protocol (MMP)\n+    Owners: Kyle Den Hartog <kdenhartog@brave.com>\n+    Status: Draft\n+    Category: Wallet\n+    Created: 2024-08-27\n+    License: MPL 2.0\n+    Pull-Request: <https://github.com/zcash/zips/pull/899>\n+\n+\n+# Abstract\n+\n+In this specification we’ll define how to include an on-chain pointer to a off-chain rich media message which is larger than 512 UTF-8 characters while maintaining the confidentiality guarantees of the memos defined in [^ZIP-302].\n+\n+# Motivation\n+\n+Today it's not possible to send a message larger than 512 UTF-8 characters in a memo. In this ZIP we'll define how this can be done while still maintaining confidentiality between the two communicating parties so that larger messages can be sent and received without needing to store all data on chain. This will allow for use cases like including a PDF invoice with a transaction or sending large personalized messages or videos directly to a ZCash address.\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"MAY\",\n+\"RECOMMENDED\", \"OPTIONAL\", and \"REQUIRED\" in this document are to\n+be interpreted as described in BCP 14 [^BCP-14]_ when, and only when,\n+they appear in all capitals.\n+\n+Definitions of commonly used terms used throughout this ZIP:\n+ - Scheme - This is the scheme of the URI necessary to identify that this is a MMP message. It SHALL match the definition of a scheme as defined in [^RFC-3986] section 3.1.\n+ - Version - This is the version of the Media Memo Protocol in use in order to support extensions of the protocol.\n+ - Message Location - A URL (or derived URL as defined by a separate version spec) which can be used to locate the ciphertext of the message.\n+ - Query Parameters - This is an extensible model for including additional query parameters directly within the MMP message URI. It SHALL match the definition of a query as defined by [^RFC-3986] section 3.4.\n+\n+# Requirements\n+\n+This spec should allow ZCash users to include rich media messages in their transactions through the use of memos as defined in RFC-302. Additionally, in order to maintain cryptographic agility and account for different means of sending messages, it should be extensible by design while still maintaining interoperability.\n+\n+# Specification\n+\n+## Message URI syntax\n+\n+The current memo zip requires that the content of the message is less than 512 ASCII characters which is a sufficient size to include a URI which includes the sufficient details to locate and decrypt the message. Here is how the format of the pointer will be:\n+\n+### ABNF definition of message URI syntax\n+    mmp-uri                         = scheme \":\" version \":\" message-location query fragment\n+    mmp-uri                         = /1*512URICHAR; Limit to 512 ASCII characters that are URI safe\n+    scheme                          = \"mmp\"\n+    version                           = 1*2DIGIT\n+    message-location          = 1*256PCHAR\n+    encrypt-key-fragment    = \"key=\" 1*64unreserved\n+    URICHAR                      = reserved / unreserved\n+\n+If a term is used here, but not specifically defined then see RFC3986 appendix A for it's definition.\n+\n+## Example of message URI\n+\n+mmp:01:bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku#key=Hy9X_k2mLpQrZtNbVc5hA7sDxEuFoP-iQnWyG4M6OjBv\n+\n+## Protocol Versioning\n+\n+Since the version number determines the various associated properties of the protocol (including cryptographic properties to support cryptographic alacrity) there can be a wide range of capabilities that MAY NOT be interoperable. As such, all valid implementations of this protocol MUST implement at least version 1 to ensure a fallback for messaging. \n+\n+## Message Location\n+\n+This message location is a URL which can be used to locate a message. It’s used to locate the encrypted ciphertext of the media which is stored at the URL location. It MUST be defined by each protocol version specification. The message location MUST define how the URL is encoded and define how the URL used to locate and resolve the encrypted message is parsed within the URI. For example in V1, this will be defined as a CID that can be used to get the message from [^IPFS], but in future iterations it could be a URL that points to a S3 bucket, Torrent file, or elsewhere.\n+\n+## Query Parameters\n+\n+All query parameters are optional and MAY be included. Any required query parameters MUST be defined by the version specification. Generally speaking these are expected to behave how they're used in the URL spec, but each spec MUST define specific behavior if a query parameter is necessary within a specification. Generally, these will be the preferred parameterization mechanism for passing data between the sender and recipients.\n+\n+## Fragment Parameters\n+\n+The fragment identifier will primarily be used to pass the key within the URI. However, it is still possible to define additional fragment parameter using a key-value pair delimited with a `=` character. Each fragment key-value pair MUST be separated with an `&`. This serves as a method to match behavior of other URI schemes. Any additional required fragment parameters MUST be defined by the version specification. The exception here is the `key` fragment parameter which is defined in this document because of its expected ubiquitous use across many versions. \n+\n+### Encryption Key Fragment Parameter\n+In all versions of the Media Memo Protocol that require passing a secret key between sender and recipients the key fragment MUST be used to pass the key. The key parameter MUST be a fragment identified by the `key=` identifier and followed with a base64URL encoded encryption key. If encryption is not used within the protocol then this fragment parameter MUST NOT be used. Reuse of this fragment parameter for many protocols will ensure the greatest possible interoperability hence why it's the only defined fragment identifier. The use of a fragment parameter was chosen here because it will assist a naive parser and resolver from accidentally sending the key across the wire when picking up the message from the message location.\n+\n+## Protocol Flow\n+\n+To respond to the message, it's as simple as sending a new message. There isn't a concept of threading built within the protocol itself. Although this could be handled ad a higher layer if desired. Some basic error handling should be defined as well. In general the message should be assumed to be a “broadcast and forget” model where as long as the message has been published it’s not necessary to maintain threading or further capabilities in the message order. Further complex threading, complex protocol features, and complex error handling should be defined in a new message format spec. However this protocol does support some basic error handling to be able to inform the counterparty that a message wasn’t able to be received for some reason.\n+\n+## Basic Error Handling\n+\n+While it is possible to define other methods of version specific error handling here are default error codes which MUST be understood by an implementation. Usage of these rather than protocol specific definitions will allow for more interoperable support. Further error codes can be defined in a protocol version doc in which case a version should be included in the definition. Error Codes defined by a MMP Version Specification MUST NOT override the semantics or values of these error codes.\n+\n+### Default Error Codes\n+Here are the following definitions of the default error codes:\n+\n+- Unsupported Version: `0001`\n+    - Please resend using version 1. Version 1 is specifically chosen here to avoid needing to go through a negotiation protocol to determine a supported version.\n+- Unable to Retrieve: `0100`\n+    - This should include a TTL parameter that the recipient would prefer. If the sender resends the message they MAY opt to use a different TTL value in the follow up response which is advisable in order to avoid resource exhaustion attacks where the recipient sends many errors to try and get the sender to re-encrypt and re-pin the same message multiple times. Therefore, the included TTL parameter in this error code is just a recommended time.\n+- Unable to Retrieve - decryption error: `0101`\n+    - This error code `0101` MAY be returned when the recipient attempts to decrypt a message but the decryption process fails. This typically occurs in two scenarios:\n+        - The provided decryption key is incorrect.\n+        - The message has been tampered with, causing the authentication check to fail.\n+- Unable to Retrieve - content unavailable: `0102`\n+    - The recipient attempted to retrieve the encrypted message and was unable to. This may mean that the recipient was unable to parse or retrieve the message via the URL. This may mean that the message wasn’t still pinned or that they received a 404 on pickup for example. Additionally, it may mean that the protocol necessary to pick the message up may not be supported. For example, if the URL provided is .onion TLD and TOR protocol isn’t supported then the recipient would not be able to retrieve the message.\n+\n+# Security and Privacy Considerations\n+\n+## Encryption Key Message Security\n+\n+This still requires further review, but in general we should be able to extend the confidentiality guarantees of the memo field in order to securely send a symmetric key without the need for a key agreement protocol as well. This section should be updated further before finalization with greater detail about this.\n+\n+## Protocol Version Specifications\n+\n+Since the cryptographic and protocol security will be defined within separate protocol specification documents, these MUST include further security and privacy consideration sections which highlight specific tradeoffs that have been made. It’s generally expected that these sections will follow the guidelines of [^ZIP-0000] which suggests the usage of [^RFC-3552] and [^RFC-6973] as a starting point.\n+\n+# Protocol Versions Registry\n+\n+In order to support additional protocol versions and make it easy to find the documentation of a protocol version for implementers we’ll establish a first come first serve registration process. The version number MUST be the next integer available in the registry table. Minor or patch versions won't be used. The requirements to register a new version are the following:\n+\n+1. It MUST be published as a ZIP and have reached “Final” Status.\n+2. Once the ZIP has been finalized a version identifier can be gotten by registering it with a table entry. The table entry should include the ZIP number defining the version, the ZIP’s title, the next version number, and the date of registration.\n+3. Once this is done the version number MUST NOT be changed or reused\n+\n+## Registry Table\n+| MMP Version | ZIP Number | Title | Registration Date  |\n+| --- | --- | --- | --- |\n+| 1 | [^ZIP-tbd2] | Media Memos Protocol (MMP) Version 1 | 2024-08-27 |\n+| ... | ... | ... | ... |\n+\n+# References\n+\n+[^BCP14]: [Information on BCP 14 — \"RFC 2119: Key words for use in RFCs to Indicate Requirement Levels\" and \"RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words\"](https://www.rfc-editor.org/info/bcp14)\n+[^ZIP-0302]: [ZIP 302: Standardized Memo Field Format](zip-0302.rst)\n+[^RFC-3986]: [Uniform Resource Identifier (URI): Generic Syntax](https://datatracker.ietf.org/doc/html/rfc3986)\n+[^ZIP-0000]: [ZIP 0: ZIP Process](zip-0000.rst)\n+[^RFC-3552]: [Guidelines for Writing RFC Text on Security Considerations](https://datatracker.ietf.org/doc/html/rfc3552)\n+[^RFC-6973]: [Privacy Considerations for Internet Protocols](https://datatracker.ietf.org/doc/html/rfc6973)\n+[^ZIP-tbd2]: [ZIP TBD2: Media Memos Protocol (MMP) Version 1](zip-tbd2.md)\n+[^alacrity]: [Cryptographic Alacrity](https://soatok.blog/2024/08/28/introducing-alacrity-to-federated-cryptography/)\n+[^IPFS]: [IPFS Network](https://ipfs.tech/)\n\\ No newline at end of file"},{"filename":"zips/zip-tbd2.md","status":"added","additions":107,"deletions":0,"patch":"@@ -0,0 +1,107 @@\n+    ZIP: Unassigned\n+    Title: Media Memos Protocol (MMP) Version 1\n+    Owners: kdenhartog@brave.com\n+    Status: Draft\n+    Category: Wallet\n+    Created: 2024-08-27\n+    License: MPL 2.0\n+    Pull-Request: <https://github.com/zcash/zips/pull/899>\n+# Abstract\n+\n+This is the first version of the [^zip-tbd1] specification. It’s intended to be a basic implementation that can be used as a first iteration to build off as well as a fallback protocol in the future. As such, there may be certain capabilities or features that are intentionally excluded which can be added in later versions.\n+\n+# Motivation\n+\n+In order to define an interoperable version of the specification while allowing for extensibility a base version needs to be defined along with the [^zip-tbd1] specification. This first version is intended to be a simple method to define how messages are encrypted and stored on the [^IPFS] network and then subsequently retrieved by a recipient.\n+\n+# Terminology\n+\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", \"MAY\",\n+\"RECOMMENDED\", \"OPTIONAL\", and \"REQUIRED\" in this document are to\n+be interpreted as described in BCP 14 [^BCP-14]_ when, and only when,\n+they appear in all capitals.\n+\n+# Specification\n+\n+## Example\n+\n+Here's a basic example of the MMP URI for the version 1 specification.\n+\n+`mmp:01:bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku?ttl=2024-07-02T14:30:00Z#key=Hy9X_k2mLpQrZtNbVc5hA7sDxEuFoP-iQnWyG4M6OjBv`\n+\n+## Message Location Encoding Scheme\n+\n+In this version, the intention is to remain focused on storing messages on the [^IPFS] network so that the protocol can operate in a maximally decentralized fashion. In order to accomplish this the message location in the MMP URI MUST be a CID of either version 0 or version 1. Both versions SHOULD be supported. Since CIDv1’s rely on the multicodec specification which includes multiple different encodings via multibase there’s a possibility that a codec is chosen that cannot be decoded. If this is the case, the message MUST be ignored. An implementation MAY return an error to the recipient if they choose. Additionally, for this reason implementers SHOULD use base64url codec by default.\n+\n+## Message Encryption\n+\n+In order to properly end-to-end encrypt the media contents and not need to redefine a new encryption process in detail this specification will reuse commonly implemented and defined cryptographic constructions. In this case, since it’s considered out of scope to support multi-messaging capabilities or extremely large file sizes the secretbox construction from libsodium will be sufficient. This makes it easier to implement this functionality safely without requiring re-implementation of the lower level cryptographic functionality which is important for a default fallback protocol.\n+\n+## Encryption process\n+\n+The Message Encryption process MUST utilize the secretbox construction from libsodium with the authentication tag attached. The XSalsa20Poly1305 stream cipher MUST be used also. In order to ensure proper usage both the key and nonce MUST not be reused. For this reason it is RECOMMENDED that implementers leverage the `crypto_secretbox_easy` API in libsodium. \n+\n+## Decryption process\n+\n+The Message Decryption process MUST utilize the secretbox construction from libsodium with the authentication tag attached. The XSalsa20Poly1305 stream cipher MUST be used. The recipient MUST use the key provided in the 'key' query parameter to decrypt the message. Implementers are RECOMMENDED to leverage the `crypto_secretbox_open_easy` API in libsodium to ensure proper decryption and verification of the authentication tag. If decryption fails due to an incorrect key or tampering of the authentication tag (or it failing to be included), the implementation MAY return an `0101` (\"Unable to Retrieve - decryption error\") to the sender and let the user know the message failed to be received. \n+\n+## Message Storage\n+\n+### Message Size\n+\n+The size of the media content MUST NOT exceed 1GB after encryption in order to prevent excessive decryption times and denial of service attacks for the recipient. Implementations MAY set a lower limit when receiving messages based on the limits of their hardware. If they do, they SHOULD return a “message too large” error code to the sender if they opt to not decrypt the message due to the file size. In order to limit further bloat as well the message MUST NOT be further encoded beyond the bytes output by the secretbox construction.\n+\n+### Message Availability\n+\n+Due to the nature of the tradeoffs on the [^IPFS] network there is not a guarantee that content will remain pinned or be replicated by others on the network. Especially since the content is encrypted and therefore unable to be seen by the sender or recipient. For this reason senders SHOULD expect to pin the message on the network for at least 7 days so that the recipient has enough time to retrieve the message. Implementations may opt to use a separate pinning service to extend this time period further. This time period that the sender intends to pin the message MUST be communicated to the recipient via the ttl query parameter. \n+\n+### Message Retention\n+\n+Once the recipient has received the message they SHOULD store it locally as well to avoid loss of the message. Similarly, the sender SHOULD store the message to retain a message sent history. Implementers are generally expected to encrypt their message histories at rest.\n+\n+## Query Parameters\n+\n+### Time To Live Query Parameter Usage\n+Since we’re dealing with a decentralized storage of media in many cases we don’t necessarily have a guarantee that the message will be available when the recipient goes to retrieve the message. As such we need a way for the sender to communicate that a message will be available up to a given point in time and what that time is. While it’s expected that the message will be around at least until the expiration of the TTL there can never be a guarantee that the message will be retrievable even within the time period of the TTL. Therefore this parameter should convey a reasonable time frame that the sender (or a provider of their choosing) is willing to store the message on the [^IPFS] network. Beyond the TTL, the recipient SHOULD NOT expect the message to be stored and the sender MAY delete it. The value of the the query parameter MUST adhere to [RFC-3339].\n+\n+### ABNF for TTL Query Parameter\n+```\n+timestamp = ”ttl=” date-time ; see RFC 3339 for date-time definition\n+```\n+\n+## Fragment Parameters\n+\n+### Encryption Key Fragment Parameter Usage\n+\n+The encryption key MUST be provided as a query parameter named 'key' in the URL. This key MUST be a 32-byte (256-bit) random value encoded as a Base64url string without padding as defined by [^RFC7515] Appendix C. The key MUST be generated using a cryptographically secure random number generator and unique to each message sent. This includes if the same message is being re-encrypted to re-send the message. The 'key' fragment parameter MUST NOT be included anywhere other than the URI syntax which MUST be sent via the memo attachment field to ensure the key is not leaked.\n+\n+### ABNF for Encryption Key Fragment Parameter\n+\n+```\n+Base64URLCHAR = ALPHA / DIGIT / \"-\" / \"_\" / \n+encrypt-key-param = \"key=\" 1*44Base64URLCHAR\n+```\n+\n+# Security and Privacy Considerations\n+\n+This section discusses potential security and privacy issues related to the Version 1 protocol.\n+\n+## Resource Exhaustion Attacks \n+Implementers should be aware of the potential for resource exhaustion attacks. This is best prevented by first checking the size of a message to make sure it conforms with implementation max limits. It’s also a good idea to set user limitations on both the sender and recipient. Additionally, senders should consider unnecessary additional costs that need to be accounted for by setting TTL query parameters at higher thresholds.\n+\n+## Key Leakage Attacks\n+The encryption key is a critical component of the protocol's security. Care should be taken to transmit the key securely and avoid logging or caching of the URI which contains the key. If logging is necessary implementers are expected to drop the key query parameter to avoid key leakage. Additionally, care should be taken when sharing MMP URIs to avoid other forms of key leakage.\n+\n+## IP address and ZCash Address Correlation Attacks\n+Senders who serve messages directly from a self hosted [^IPFS] node will likely inadvertently expose their IP address to the recipient since it’s highly unlikely any other node on the network will have the encrypted message pinned. To avoid correlation of IP address and ZCash addresses a pinning service or a secure proxy can help protect sender’s privacy. Similarly, recipients who retrieve messages directly from an [^IPFS] node may expose their IP address when retrieving a message. To avoid this recipients can use a gateway or a secure proxy for message retrieval to prevent correlation.\n+\n+# MMP Version Registry entry\n+\n+| 1 | [^ZIP-tbd2] | Media Memos Protocol (MMP) Version 1 | 2024-08-27 |\n+\n+# References\n+\n+[^ZIP-tbd1]: [ZIP TBD: Media Memos Protocol (MMP)](zip-tbd1.md)\n+[^RFC-3339]: [Date and Time on the Internet: Timestamps](https://datatracker.ietf.org/doc/html/rfc3339)\n+[^IPFS]: [IPFS Network](https://ipfs.tech/)\n+[^RFC-7515]: [JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515#appendix-C)\n\\ No newline at end of file"}],"filesTruncated":false,"preview":null},{"number":688,"title":"Extract metadata JSON.","author":"nathan-at-least","createdAt":"2023-04-19T23:27:02Z","updatedAt":"2024-10-22T22:02:24Z","url":"https://github.com/zcash/zips/pull/688","diffUrl":"https://github.com/zcash/zips/pull/688.diff","headRef":"extract-metadata","headRepo":"nathan-at-least/zips","headSha":"9d8686a6d21729ea1282c58bdd1b4fea6e5e4f8c","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This branch introduces a (hideous) shell script which generates a single JSON document with all of the ZIP header metadata. It is a finicky unix pipeline parser and it introduces a strict requirement that metadata fields MUST be prefixed with exactly 2 spaces. There was only one ZIP that violated th","zipRefs":[1006],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"Makefile","status":"modified","additions":4,"deletions":1,"patch":"@@ -1,7 +1,7 @@\n # Dependencies: see zip-guide.rst and protocol/README.rst\n \n .PHONY: all all-zips release protocol discard\n-all-zips: .Makefile.uptodate\n+all-zips: .Makefile.uptodate zip-metadata.json\n \tfind . -name 'zip-*.rst' -o -name 'zip-*.md' |sort >.zipfilelist.new\n \tdiff .zipfilelist.current .zipfilelist.new || cp -f .zipfilelist.new .zipfilelist.current\n \trm -f .zipfilelist.new\n@@ -10,6 +10,9 @@ all-zips: .Makefile.uptodate\n \n all: all-zips protocol\n \n+zip-metadata.json: zip-*.rst\n+\t./extract-metadata.sh > $@\n+\n release:\n \t$(MAKE) -C protocol release\n "},{"filename":"extract-metadata.sh","status":"added","additions":55,"deletions":0,"patch":"@@ -0,0 +1,55 @@\n+#!/bin/bash\n+#\n+# Parse all `zip-*.rst` file for header metadata.\n+#\n+# # Output Format:\n+#\n+# This outputs a single JSON object. Each key is the basename of a\n+# `zip-*.rst` file, and value is a JSON object containing the ZIP metadata\n+# fields.\n+#\n+# The ZIP metadata values are strings for single-line values, or JSON\n+# arrays of strings for each line in a multi-line value.\n+#\n+# # Input Format:\n+#\n+# This is a sensitive line-noise unix pipeline that REQUIRES zip rst\n+# files to follow this format:\n+#\n+# - The metadata must occur before the first alphabetic character on column 1.\n+# - The metadata keys must occur in column 3, after two spaces.\n+# - Metadata keys must be immediately followed by \": \".\n+# - Multi-line metadata values must be in continuation lines with the\n+#   first non-space character occurring after column 3.\n+#\n+# Integration testing:\n+#\n+# Run `./extract-metadata.sh  | jq > /dev/null` and verify there's no\n+# stderr and it has a 0 exit status. This indicates the script completed\n+# successfully and the result is valid JSON.\n+\n+set -euo pipefail\n+\n+ZIPSDIR=\"$(dirname \"$(readlink -f \"$0\")\")\"\n+\n+# Whether or not to emit a comma to separate the previous zip entry:\n+OPTCOMMA='' #\n+\n+echo '{'\n+for ziprst in $(find \"$ZIPSDIR\" -type f -name 'zip-*.rst')\n+do\n+  echo \"${OPTCOMMA}\"\n+  OPTCOMMA=','\n+\n+  echo \"  \\\"$(basename \"$ziprst\")\\\": {\"\n+  < \"$ziprst\" \\\n+    sed -n '/^ /p; /^[A-Za-z]/q' | \\\n+    tr '\\n' '|' | \\\n+    sed 's/^  //; s/|  /|/g; s/|  */\", \"/g' | \\\n+    tr '|' '\\n' | \\\n+    sed 's/^\\([^:]*\\): *\\(.*\\)$/\"\\1\": [\"\\2\"],/g; s/^/    /; $s/,$//' | \\\n+    sed '/\\[\".*\", \".*\\]/s/\\[\"/[\\n      \"/g' | \\\n+    sed 's/\", \"/\",\\n      \"/g; s/^      \\(\".*\"\\)\\(\\],*\\)/      \\1\\n    \\2/; s/: \\[\\(\"[^\"]*\"\\)\\]/: \\1/'\n+  echo -n \"  }\"\n+done\n+echo '}'"},{"filename":"zip-1006.rst","status":"modified","additions":11,"deletions":11,"patch":"@@ -1,16 +1,16 @@\n ::\n \n-   ZIP: 1006\n-   Title: Development Fund of 10% to a 2-of-3 Multisig with Community-Involved Third Entity\n-   Owners: James Todaro <james@blocktown.capital>\n-           Joseph Todaro <joseph@blocktown.capital>\n-   Credits: Mario Laul\n-            Chris Burniske\n-   Status: Obsolete\n-   Category: Consensus Process\n-   Created: 2019-08-31\n-   License: MIT\n-   Discussions-To: <https://forum.zcashcommunity.com/t/blocktown-development-fund-proposal-10-to-a-2-of-3-multisig-with-community-involved-third-entity/34782>\n+  ZIP: 1006\n+  Title: Development Fund of 10% to a 2-of-3 Multisig with Community-Involved Third Entity\n+  Owners: James Todaro <james@blocktown.capital>\n+          Joseph Todaro <joseph@blocktown.capital>\n+  Credits: Mario Laul\n+           Chris Burniske\n+  Status: Obsolete\n+  Category: Consensus Process\n+  Created: 2019-08-31\n+  License: MIT\n+  Discussions-To: <https://forum.zcashcommunity.com/t/blocktown-development-fund-proposal-10-to-a-2-of-3-multisig-with-community-involved-third-entity/34782>\n \n \n Terminology"},{"filename":"zip-metadata.json","status":"added","additions":1056,"deletions":0,"patch":"@@ -0,0 +1,1056 @@\n+{\n+\n+  \"zip-0000.rst\": {\n+    \"ZIP\": \"0\",\n+    \"Title\": \"ZIP Process\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Deirdre Connolly <deirdre@zfnd.org>\"\n+    ],\n+    \"Original-Authors\": [\n+      \"Daira Emma Hopwood\",\n+      \"Josh Cincinnati\",\n+      \"George Tankersley\"\n+    ],\n+    \"Credits\": \"Luke Dashjr\",\n+    \"Status\": \"Active\",\n+    \"Category\": \"Process\",\n+    \"Created\": \"2019-02-16\",\n+    \"License\": \"BSD-2-Clause\"\n+  },\n+  \"zip-0001.rst\": {\n+    \"ZIP\": \"1\",\n+    \"Title\": \"Network Upgrade Policy and Scheduling\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Consensus Process\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/343>\"\n+  },\n+  \"zip-0002.rst\": {\n+    \"ZIP\": \"2\",\n+    \"Title\": \"Design Considerations for Network Upgrades\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Informational\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/362>\"\n+  },\n+  \"zip-0032.rst\": {\n+    \"ZIP\": \"32\",\n+    \"Title\": \"Shielded Hierarchical Deterministic Wallets\",\n+    \"Owners\": [\n+      \"Jack Grigg <str4d@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Credits\": [\n+      \"Sean Bowe\",\n+      \"Kris Nuttycombe\",\n+      \"Ying Tong Lai\",\n+      \"Pieter Wuille\",\n+      \"Marek Palatinus\",\n+      \"Pavol Rusnak\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Standards / Wallet\",\n+    \"Created\": \"2018-05-22\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0076.rst\": {\n+    \"ZIP\": \"76\",\n+    \"Title\": \"Transaction Signature Validation before Overwinter\",\n+    \"Owners\": [\n+      \"Jack Grigg <str4d@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Consensus\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/130>\"\n+  },\n+  \"zip-0143.rst\": {\n+    \"ZIP\": \"143\",\n+    \"Title\": \"Transaction Signature Validation for Overwinter\",\n+    \"Owners\": [\n+      \"Jack Grigg <str4d@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Credits\": [\n+      \"Johnson Lau\",\n+      \"Pieter Wuille\",\n+      \"Bitcoin-ABC\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2017-12-27\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0155.rst\": {\n+    \"ZIP\": \"155\",\n+    \"Title\": \"addrv2 message\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Credits\": [\n+      \"Wladimir J. van der Laan\",\n+      \"Zancas Wilcox\"\n+    ],\n+    \"Status\": \"Proposed\",\n+    \"Category\": \"Network\",\n+    \"Created\": \"2021-08-13\",\n+    \"License\": \"BSD-2-Clause\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/542>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/543>\"\n+  },\n+  \"zip-0173.rst\": {\n+    \"ZIP\": \"173\",\n+    \"Title\": \"Bech32 Format\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Credits\": [\n+      \"Pieter Wuille <pieter.wuille@gmail.com>\",\n+      \"Greg Maxwell <greg@xiph.org>\",\n+      \"Rusty Russell\",\n+      \"Mark Friedenbach\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Standards / Wallet\",\n+    \"Created\": \"2018-06-13\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0200.rst\": {\n+    \"ZIP\": \"200\",\n+    \"Title\": \"Network Upgrade Mechanism\",\n+    \"Owners\": \"Jack Grigg <str4d@electriccoin.co>\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2018-01-08\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0201.rst\": {\n+    \"ZIP\": \"201\",\n+    \"Title\": \"Network Peer Management for Overwinter\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Original-Authors\": \"Simon Liu\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Network\",\n+    \"Created\": \"2018-01-15\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0202.rst\": {\n+    \"ZIP\": \"202\",\n+    \"Title\": \"Version 3 Transaction Format for Overwinter\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Original-Authors\": \"Simon Liu\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2018-01-10\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0203.rst\": {\n+    \"ZIP\": \"203\",\n+    \"Title\": \"Transaction Expiry\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Original-Authors\": \"Jay Graber\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2018-01-09\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0204.rst\": {\n+    \"ZIP\": \"204\",\n+    \"Title\": \"Zcash P2P Network Protocol\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Network\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/352>\"\n+  },\n+  \"zip-0205.rst\": {\n+    \"ZIP\": \"205\",\n+    \"Title\": \"Deployment of the Sapling Network Upgrade\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Credits\": \"Simon Liu\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus / Network\",\n+    \"Created\": \"2018-10-08\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0206.rst\": {\n+    \"ZIP\": \"206\",\n+    \"Title\": \"Deployment of the Blossom Network Upgrade\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Credits\": \"Simon Liu\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus / Network\",\n+    \"Created\": \"2019-07-29\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0207.rst\": {\n+    \"ZIP\": \"207\",\n+    \"Title\": \"Funding Streams\",\n+    \"Owners\": [\n+      \"Jack Grigg <str4d@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-01-04\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0208.rst\": {\n+    \"ZIP\": \"208\",\n+    \"Title\": \"Shorter Block Target Spacing\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Original-Authors\": [\n+      \"Daira Emma Hopwood\",\n+      \"Simon Liu\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-01-10\",\n+    \"License\": \"MIT\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/237>\"\n+  },\n+  \"zip-0209.rst\": {\n+    \"ZIP\": \"209\",\n+    \"Title\": \"Prohibit Negative Shielded Chain Value Pool Balances\",\n+    \"Owners\": [\n+      \"Sean Bowe <sean@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-02-25\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0210.rst\": {\n+    \"ZIP\": \"210\",\n+    \"Title\": \"Sapling Anchor Deduplication within Transactions\",\n+    \"Owners\": \"Jack Grigg <str4d@electriccoin.co>\",\n+    \"Status\": \"Withdrawn\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-03-27\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0211.rst\": {\n+    \"ZIP\": \"211\",\n+    \"Title\": \"Disabling Addition of New Value to the Sprout Chain Value Pool\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Credits\": \"Sean Bowe\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-03-29\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0212.rst\": {\n+    \"ZIP\": \"212\",\n+    \"Title\": \"Allow Recipient to Derive Ephemeral Secret from Note Plaintext\",\n+    \"Owners\": \"Sean Bowe <sean@electriccoin.co>\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-03-31\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0213.rst\": {\n+    \"ZIP\": \"213\",\n+    \"Title\": \"Shielded Coinbase\",\n+    \"Owners\": \"Jack Grigg <jack@z.cash>\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-03-30\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0214.rst\": {\n+    \"ZIP\": \"214\",\n+    \"Title\": \"Consensus rules for a Zcash Development Fund\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2020-02-28\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/community-sentiment-polling-results-nu4-and-draft-zip-1014/35560>\"\n+  },\n+  \"zip-0215.rst\": {\n+    \"ZIP\": \"215\",\n+    \"Title\": \"Explicitly Defining and Modifying Ed25519 Validation Rules\",\n+    \"Owners\": \"Henry de Valence <hdevalence@zfnd.org>\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2020-04-27\",\n+    \"License\": \"BSD-2-Clause\"\n+  },\n+  \"zip-0216.rst\": {\n+    \"ZIP\": \"216\",\n+    \"Title\": \"Require Canonical Jubjub Point Encodings\",\n+    \"Owners\": [\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2021-02-11\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/400>\"\n+  },\n+  \"zip-0217.rst\": {\n+    \"ZIP\": \"217\",\n+    \"Title\": \"Aggregate Signatures\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Consensus\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zcash/issues/2914>\"\n+  },\n+  \"zip-0219.rst\": {\n+    \"ZIP\": \"219\",\n+    \"Title\": \"Disabling Addition of New Value to the Sapling Chain Value Pool\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Consensus\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/428>\"\n+  },\n+  \"zip-0220.rst\": {\n+    \"ZIP\": \"220\",\n+    \"Title\": \"Zcash Shielded Assets\",\n+    \"Owners\": [\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Withdrawn\",\n+    \"Category\": \"Consensus\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zcash/issues/830>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/269>\"\n+  },\n+  \"zip-0221.rst\": {\n+    \"ZIP\": \"221\",\n+    \"Title\": \"FlyClient - Consensus-Layer Changes\",\n+    \"Owners\": \"Jack Grigg <jack@electriccoin.co>\",\n+    \"Original-Authors\": [\n+      \"Ying Tong Lai\",\n+      \"James Prestwich\",\n+      \"Georgios Konstantopoulos\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-03-30\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0222.rst\": {\n+    \"ZIP\": \"222\",\n+    \"Title\": \"Transparent Zcash Extensions\",\n+    \"Owners\": [\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Kris Nuttycombe <kris@electriccoin.co>\"\n+    ],\n+    \"Credits\": [\n+      \"Zaki Manian\",\n+      \"Daira Emma Hopwood\",\n+      \"Sean Bowe\"\n+    ],\n+    \"Status\": \"Draft\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2019-07-01\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0224.rst\": {\n+    \"ZIP\": \"224\",\n+    \"Title\": \"Orchard Shielded Protocol\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Sean Bowe <sean@electriccoin.co>\",\n+      \"Kris Nuttycombe <kris@electriccoin.co>\",\n+      \"Ying Tong Lai <yingtong@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2021-02-27\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/435>\"\n+  },\n+  \"zip-0225.rst\": {\n+    \"ZIP\": \"225\",\n+    \"Title\": \"Version 5 Transaction Format\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Sean Bowe <sean@electriccoin.co>\",\n+      \"Kris Nuttycombe <kris@electriccoin.co>\",\n+      \"Ying Tong Lai <yingtong@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2021-02-28\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/440>\"\n+  },\n+  \"zip-0226.rst\": {\n+    \"ZIP\": \"226\",\n+    \"Title\": \"Transfer and Burn of Zcash Shielded Assets\",\n+    \"Owners\": [\n+      \"Pablo Kogan <pablo@qed-it.com>\",\n+      \"Vivek Arte <vivek@qed-it.com>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <str4d@electriccoin.co>\",\n+      \"Deirdre Connolly <deirdre@zfnd.org>\"\n+    ],\n+    \"Credits\": [\n+      \"Daniel Benarroch\",\n+      \"Aurelien Nicolas\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Consensus\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/618>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/649>\"\n+  },\n+  \"zip-0227.rst\": {\n+    \"ZIP\": \"227\",\n+    \"Title\": \"Issuance of Zcash Shielded Assets\",\n+    \"Owners\": [\n+      \"Pablo Kogan <pablo@qed-it.com>\",\n+      \"Vivek Arte <vivek@qed-it.com>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <str4d@electriccoin.co>\",\n+      \"Deirdre Connolly <deirdre@zfnd.org>\"\n+    ],\n+    \"Credits\": [\n+      \"Daniel Benarroch\",\n+      \"Aurelien Nicolas\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Consensus\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/618>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/649>\"\n+  },\n+  \"zip-0239.rst\": {\n+    \"ZIP\": \"239\",\n+    \"Title\": \"Relay of Version 5 Transactions\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Network\",\n+    \"Created\": \"2021-05-29\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/515>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/516>\"\n+  },\n+  \"zip-0243.rst\": {\n+    \"ZIP\": \"243\",\n+    \"Title\": \"Transaction Signature Validation for Sapling\",\n+    \"Owners\": [\n+      \"Jack Grigg <str4d@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Credits\": \"Simon Liu\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2018-04-10\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0244.rst\": {\n+    \"ZIP\": \"244\",\n+    \"Title\": \"Transaction Identifier Non-Malleability\",\n+    \"Owners\": [\n+      \"Kris Nuttycombe <kris@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <str4d@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2021-01-06\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/411>\"\n+  },\n+  \"zip-0245.rst\": {\n+    \"ZIP\": \"245\",\n+    \"Title\": \"Transaction Identifier Digests & Signature Validation for Transparent Zcash Extensions\",\n+    \"Owners\": \"Kris Nuttycombe <kris@electriccoin.co>\",\n+    \"Status\": \"Draft\",\n+    \"Category\": \"Consensus\",\n+    \"Created\": \"2021-01-13\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/384>\"\n+  },\n+  \"zip-0250.rst\": {\n+    \"ZIP\": \"250\",\n+    \"Title\": \"Deployment of the Heartwood Network Upgrade\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus / Network\",\n+    \"Created\": \"2020-02-28\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0251.rst\": {\n+    \"ZIP\": \"251\",\n+    \"Title\": \"Deployment of the Canopy Network Upgrade\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus / Network\",\n+    \"Created\": \"2020-02-28\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0252.rst\": {\n+    \"ZIP\": \"252\",\n+    \"Title\": \"Deployment of the NU5 Network Upgrade\",\n+    \"Owners\": [\n+      \"teor <teor@zfnd.org>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Consensus / Network\",\n+    \"Created\": \"2021-02-23\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/440>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/446>\"\n+  },\n+  \"zip-0300.rst\": {\n+    \"ZIP\": \"300\",\n+    \"Title\": \"Cross-chain Atomic Transactions\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Original-Authors\": \"Jay Graber\",\n+    \"Status\": \"Proposed\",\n+    \"Category\": \"Informational\",\n+    \"Created\": \"2017-03-08\",\n+    \"License\": \"MIT\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/123>\"\n+  },\n+  \"zip-0301.rst\": {\n+    \"ZIP\": \"301\",\n+    \"Title\": \"Zcash Stratum Protocol\",\n+    \"Owners\": \"Jack Grigg <str4d@electriccoin.co>\",\n+    \"Credits\": [\n+      \"5a1t\",\n+      \"Daira Emma Hopwood\",\n+      \"Marek Palatinus (slush) and colleagues\",\n+      \"Jelle Bourdeaud'hui (razakal)\",\n+      \"ocminer\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Standards / Ecosystem\",\n+    \"Created\": \"2016-09-23\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0302.rst\": {\n+    \"ZIP\": \"302\",\n+    \"Title\": \"Standardized Memo Field Format\",\n+    \"Owners\": \"Jack Grigg <jack@electriccoin.co>\",\n+    \"Original-Authors\": \"Jay Graber\",\n+    \"Status\": \"Draft\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Created\": \"2017-02-08\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/366>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/105>\"\n+  },\n+  \"zip-0303.rst\": {\n+    \"ZIP\": \"303\",\n+    \"Title\": \"Sprout Payment Disclosure\",\n+    \"Owners\": \"Deirdre Connolly <deirdre@zfnd.org>\",\n+    \"Original-Authors\": \"Simon Liu\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/119>\"\n+  },\n+  \"zip-0304.rst\": {\n+    \"ZIP\": \"304\",\n+    \"Title\": \"Sapling Address Signatures\",\n+    \"Owners\": \"Jack Grigg <jack@electriccoin.co>\",\n+    \"Credits\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Sean Bowe <sean@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Draft\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Created\": \"2020-06-01\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/345>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/376>\"\n+  },\n+  \"zip-0305.rst\": {\n+    \"ZIP\": \"305\",\n+    \"Title\": \"Best Practices for Hardware Wallets supporting Sapling\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/346>\"\n+  },\n+  \"zip-0306.rst\": {\n+    \"ZIP\": \"306\",\n+    \"Title\": \"Security Considerations for Anchor Selection\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Informational\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/351>\"\n+  },\n+  \"zip-0307.rst\": {\n+    \"ZIP\": \"307\",\n+    \"Title\": \"Light Client Protocol for Payment Detection\",\n+    \"Owners\": [\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Original-Authors\": \"George Tankersley\",\n+    \"Credits\": \"Matthew Green\",\n+    \"Category\": \"Standards / Ecosystem\",\n+    \"Status\": \"Draft\",\n+    \"Created\": \"2018-09-17\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0308.rst\": {\n+    \"ZIP\": \"308\",\n+    \"Title\": \"Sprout to Sapling Migration\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Original-Authors\": [\n+      \"Daira Emma Hopwood\",\n+      \"Eirik Ogilvie-Wigley\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Created\": \"2018-11-27\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0309.rst\": {\n+    \"ZIP\": \"309\",\n+    \"Title\": \"Blind Off-chain Lightweight Transactions (BOLT)\",\n+    \"Owners\": [\n+      \"J. Ayo Akinyele <ayo@boltlabs.io>\",\n+      \"Colleen Swanson <swan@boltlabs.io>\"\n+    ],\n+    \"Credits\": [\n+      \"Ian Miers\",\n+      \"Matthew Green\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Standards / Ecosystem\",\n+    \"Created\": \"2019-07-15\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zcash/issues/2353>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/216>\"\n+  },\n+  \"zip-0310.rst\": {\n+    \"ZIP\": \"310\",\n+    \"Title\": \"Security Properties of Sapling Viewing Keys\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Draft\",\n+    \"Category\": \"Informational\",\n+    \"Created\": \"2020-03-09\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0311.rst\": {\n+    \"ZIP\": \"311\",\n+    \"Title\": \"Sapling Payment Disclosure\",\n+    \"Owners\": \"Deirdre Connolly <deirdre@zfnd.org>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/387>\"\n+  },\n+  \"zip-0312.rst\": {\n+    \"ZIP\": \"312\",\n+    \"Title\": \"Shielded Multisignatures using FROST\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/382>\"\n+  },\n+  \"zip-0313.rst\": {\n+    \"ZIP\": \"313\",\n+    \"Title\": \"Reduce Conventional Transaction Fee to 1000 zatoshis\",\n+    \"Owners\": \"Aditya Bharadwaj <nighthawkwallet@protonmail.com>\",\n+    \"Credits\": [\n+      \"Daira Emma Hopwood\",\n+      \"Deirdre Connolly\",\n+      \"Nathan Wilcox\"\n+    ],\n+    \"Status\": \"Active\",\n+    \"Category\": \"Wallet\",\n+    \"Created\": \"2020-10-11\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/zip-reduce-default-shielded-transaction-fee-to-1000-zats/37566>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/408>\"\n+  },\n+  \"zip-0314.rst\": {\n+    \"ZIP\": \"314\",\n+    \"Title\": \"Privacy upgrades to the Zcash light client protocol\",\n+    \"Owners\": \"Taylor Hornby <taylor@electriccoin.co>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Standards / Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/434>\"\n+  },\n+  \"zip-0315.rst\": {\n+    \"ZIP\": \"315\",\n+    \"Title\": \"Best Practices for Wallet Handling of Multiple Pools\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/447>\"\n+  },\n+  \"zip-0316.rst\": {\n+    \"ZIP\": \"316\",\n+    \"Title\": \"Unified Addresses and Unified Viewing Keys\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Nathan Wilcox <nathan@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Sean Bowe <sean@electriccoin.co>\",\n+      \"Kris Nuttycombe <kris@electriccoin.co>\",\n+      \"Greg Pfeil <greg@electriccoin.co>\"\n+    ],\n+    \"Credits\": [\n+      \"Taylor Hornby\",\n+      \"Ying Tong Lai\",\n+      \"Stephen Smith\"\n+    ],\n+    \"Status\": \"Final\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Created\": \"2021-04-07\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/482>\"\n+  },\n+  \"zip-0317.rst\": {\n+    \"ZIP\": \"317\",\n+    \"Title\": \"Proportional Transfer Fee Mechanism\",\n+    \"Owners\": [\n+      \"Aditya Bharadwaj <nighthawk24@gmail.com>\",\n+      \"Daira Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Credits\": [\n+      \"Madars Virza\",\n+      \"Kris Nuttycombe\",\n+      \"Jack Grigg\",\n+      \"Francisco Gindre\"\n+    ],\n+    \"Status\": \"Draft\",\n+    \"Category\": \"Standards / Wallet\",\n+    \"Obsoletes\": \"ZIP 313\",\n+    \"Created\": \"2022-08-15\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/zip-proportional-output-fee-mechanism-pofm/42808>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/631>\"\n+  },\n+  \"zip-0318.rst\": {\n+    \"ZIP\": \"318\",\n+    \"Title\": \"Associated Payload Encryption\",\n+    \"Owners\": [\n+      \"Kris Nuttycombe <kris@electriccoin.co>\",\n+      \"Daira Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Standards Track\",\n+    \"Created\": \"2022-09-19\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/633>\"\n+  },\n+  \"zip-0319.rst\": {\n+    \"ZIP\": \"319\",\n+    \"Title\": \"Options for Shielded Pool Retirement\",\n+    \"Owners\": [\n+      \"Nathan Wilcox <nathan@electriccoin.co>\",\n+      \"Daira Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Informational\",\n+    \"Created\": \"2022-09-20\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/635>\"\n+  },\n+  \"zip-0321.rst\": {\n+    \"ZIP\": \"321\",\n+    \"Title\": \"Payment Request URIs\",\n+    \"Owners\": [\n+      \"Kris Nuttycombe (kris@electriccoin.co)\",\n+      \"Daira Emma Hopwood (daira@electriccoin.co)\"\n+    ],\n+    \"Status\": \"Proposed\",\n+    \"Category\": \"Standards / Wallet\",\n+    \"Created\": \"2020-08-28\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/347>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/395>\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0322.rst\": {\n+    \"ZIP\": \"322\",\n+    \"Title\": \"Generic Signed Message Format\",\n+    \"Owners\": [\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Standards / RPC / Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/429>\"\n+  },\n+  \"zip-0323.rst\": {\n+    \"ZIP\": \"323\",\n+    \"Title\": \"Specification of getblocktemplate for Zcash\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"RPC / Mining\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/405>\"\n+  },\n+  \"zip-0332.rst\": {\n+    \"ZIP\": \"332\",\n+    \"Title\": \"Wallet Recovery from zcashd HD Seeds\",\n+    \"Owners\": [\n+      \"Kris Nuttycombe <kris@electriccoin.co>\",\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/675>\"\n+  },\n+  \"zip-0339.rst\": {\n+    \"ZIP\": \"339\",\n+    \"Title\": \"Wallet Recovery Words\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/339>\"\n+  },\n+  \"zip-0400.rst\": {\n+    \"ZIP\": \"400\",\n+    \"Title\": \"Wallet.dat format\",\n+    \"Owners\": \"Alfredo Garcia <oxarbitrage@gmail.com>\",\n+    \"Status\": \"Draft\",\n+    \"Category\": \"Wallet\",\n+    \"Created\": \"2020-05-26\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0401.rst\": {\n+    \"ZIP\": \"401\",\n+    \"Title\": \"Addressing Mempool Denial-of-Service\",\n+    \"Owners\": \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+    \"Status\": \"Active\",\n+    \"Category\": \"Network\",\n+    \"Created\": \"2019-09-09\",\n+    \"License\": \"MIT\"\n+  },\n+  \"zip-0402.rst\": {\n+    \"ZIP\": \"402\",\n+    \"Title\": \"New Wallet Database Format\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/365>\"\n+  },\n+  \"zip-0403.rst\": {\n+    \"ZIP\": \"403\",\n+    \"Title\": \"Verification Behaviour of zcashd\",\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"Informational\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/404>\"\n+  },\n+  \"zip-0416.rst\": {\n+    \"ZIP\": \"416\",\n+    \"Title\": \"Support for Unified Addresses in zcashd\",\n+    \"Owners\": [\n+      \"Daira Emma Hopwood <daira@electriccoin.co>\",\n+      \"Jack Grigg <jack@electriccoin.co>\",\n+      \"Sean Bowe <sean@electriccoin.co>\",\n+      \"Kris Nuttycombe <kris@electriccoin.co>\",\n+      \"Ying Tong Lai <yingtong@electriccoin.co>\",\n+      \"Steven Smith <steven@electriccoin.co>\"\n+    ],\n+    \"Status\": \"Reserved\",\n+    \"Category\": \"RPC / Wallet\",\n+    \"Discussions-To\": \"<https://github.com/zcash/zips/issues/503>\"\n+  },\n+  \"zip-1001.rst\": {\n+    \"ZIP\": \"1001\",\n+    \"Title\": \"Keep the Block Distribution as Initially Defined — 90% to Miners\",\n+    \"Owners\": \"mistfpga (zcash forums) <steve@mistfpga.net>\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-08-01\",\n+    \"License\": \"CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/>\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/zip-proposal-keep-the-block-distribution-as-initaly-defined-90-to-miners/33843>\"\n+  },\n+  \"zip-1002.rst\": {\n+    \"ZIP\": \"1002\",\n+    \"Title\": \"Opt-in Donation Feature\",\n+    \"Owners\": \"mistfpga (zcash forums) <steve@mistfpga.net>\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-07-17\",\n+    \"License\": \"CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/>\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/zip-proposal-a-genuine-opt-in-protocol-level-development-donation-option/33846>\"\n+  },\n+  \"zip-1003.rst\": {\n+    \"ZIP\": \"1003\",\n+    \"Title\": \"20% Split Evenly Between the ECC and the Zcash Foundation, and a Voting System Mandate\",\n+    \"Owners\": \"aristarchus (zcash forums)\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-06-19\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/dev-fund-proposal-20-split-between-the-ecc-and-the-foundation/33862>\"\n+  },\n+  \"zip-1004.rst\": {\n+    \"ZIP\": \"1004\",\n+    \"Title\": \"Miner-Directed Dev Fund\",\n+    \"Owners\": \"Andrew Miller (@amiller on zcash forums)\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-06-19\",\n+    \"License\": \"public domain\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/dev-fund-proposal-miner-directed-dev-fund-was-20-to-any-combination-of-ecc-zfnd-parity-or-burn/33864>\"\n+  },\n+  \"zip-1005.rst\": {\n+    \"ZIP\": \"1005\",\n+    \"Title\": \"Zcash Community Funding System\",\n+    \"Owners\": \"Dimitris Apostolou <dimitris.apostolou@icloud.com>\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-06-23\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/zip-proposal-zcfs-zcash-community-funding-system/33898>\"\n+  },\n+  \"zip-1007.rst\": {\n+    \"ZIP\": \"1007\",\n+    \"Title\": \"Enforce Development Fund Commitments with a Legal Charter\",\n+    \"Owners\": [\n+      \"@lex-node (zcash forums)\",\n+      \"@mistfpga (zcash forums) <steve@mistfpga.net>\"\n+    ],\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Concensus Process\",\n+    \"Created\": \"2019-08-24\",\n+    \"License\": \"CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/>\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/dev-fund-supplemental-proposal-enforce-devfund-commitments-with-legal-charter/34709>\"\n+  },\n+  \"zip-1008.rst\": {\n+    \"ZIP\": \"1008\",\n+    \"Title\": \"Fund ECC for Two More Years\",\n+    \"Owners\": [\n+      \"@kek (zcash forums)\",\n+      \"@mistfpga (zcash forums) <steve@mistfpga.net>\"\n+    ],\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-09-02\",\n+    \"License\": \"CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/>\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/kek-s-proposal-fund-ecc-for-2-more-years/34778>\"\n+  },\n+  \"zip-1009.rst\": {\n+    \"ZIP\": \"1009\",\n+    \"Title\": \"Five-Entity Strategic Council\",\n+    \"Owners\": \"Avichal Garg <avichalgarg@electriccapital.com>\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-08-28\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/dev-fund-proposal-5-entity-strategic-council-approach/34801>\"\n+  },\n+  \"zip-1010.rst\": {\n+    \"ZIP\": \"1010\",\n+    \"Title\": \"Compromise Dev Fund Proposal With Diverse Funding Streams\",\n+    \"Owners\": \"Josh Cincinnati <josh@zfnd.org>\",\n+    \"Credits\": [\n+      \"Matt Luongo\",\n+      \"Eran Tromer\",\n+      \"Andrew Miller\",\n+      \"mistfpga\",\n+      \"lex-node\",\n+      \"and many others\"\n+    ],\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-08-31\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/a-grand-compromise-synthesis-zip-proposal/34812>\"\n+  },\n+  \"zip-1011.rst\": {\n+    \"ZIP\": \"1011\",\n+    \"Title\": \"Decentralize the Dev Fee\",\n+    \"Owners\": \"Matt Luongo <matt@thesis.co>\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-09-27\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/decentralizing-the-dev-fee/35252>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/278>\"\n+  },\n+  \"zip-1012.rst\": {\n+    \"ZIP\": \"1012\",\n+    \"Title\": \"Dev Fund to ECC + ZF + Major Grants\",\n+    \"Owners\": \"Eran Tromer <eran@tromer.org>\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-11-10\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/dev-fund-proposal-dev-fund-to-ecc-zfnd-major-grants/35364>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/291>\"\n+  },\n+  \"zip-1013.rst\": {\n+    \"ZIP\": \"1013\",\n+    \"Title\": \"Keep It Simple, Zcashers: 10% to ECC, 10% to ZF\",\n+    \"Owners\": \"Gordon Mohr (@gojomo on relevant forums)\",\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-11-14\",\n+    \"License\": \"Public Domain\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/zip-keep-it-simple-zcashers-kisz-10-to-ecc-10-to-zfnd/35425>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/293>\"\n+  },\n+  \"zip-1014.rst\": {\n+    \"ZIP\": \"1014\",\n+    \"Title\": \"Establishing a Dev Fund for ECC, ZF, and Major Grants\",\n+    \"Owners\": [\n+      \"Andrew Miller <socrates1024@gmail.com>\",\n+      \"Zooko Wilcox <zooko@electriccoin.co>\"\n+    ],\n+    \"Original-Authors\": \"Eran Tromer\",\n+    \"Credits\": [\n+      \"Matt Luongo\",\n+      \"@aristarchus\",\n+      \"@dontbeevil\",\n+      \"Daira Emma Hopwood\",\n+      \"George Tankersley\",\n+      \"Josh Cincinnati\",\n+      \"Andrew Miller\"\n+    ],\n+    \"Status\": \"Active\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-11-10\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/community-sentiment-polling-results-nu4-and-draft-zip-1014/35560>\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/308>\"\n+  },\n+  \"zip-guide.rst\": {\n+    \"ZIP\": \"Unassigned {numbers are assigned by ZIP editors}\",\n+    \"Title\": \"{Something Short and To the Point}\",\n+    \"Owners\": [\n+      \"First Owner <email>\",\n+      \"...\"\n+    ],\n+    \"Credits\": [\n+      \"First Credited\",\n+      \"...\"\n+    ],\n+    \"Status\": \"Draft\",\n+    \"Category\": [\n+      \"{Consensus \",\n+      \"Standards Track \",\n+      \"Network \",\n+      \"RPC \",\n+      \"Wallet \",\n+      \"Informational \",\n+      \"Process}\"\n+    ],\n+    \"Created\": \"yyyy-mm-dd\",\n+    \"License\": \"{usually MIT}\",\n+    \"Pull-Request\": \"<https://github.com/zcash/zips/pull/253>\"\n+  },\n+  \"zip-1006.rst\": {\n+    \"ZIP\": \"1006\",\n+    \"Title\": \"Development Fund of 10% to a 2-of-3 Multisig with Community-Involved Third Entity\",\n+    \"Owners\": [\n+      \"James Todaro <james@blocktown.capital>\",\n+      \"Joseph Todaro <joseph@blocktown.capital>\"\n+    ],\n+    \"Credits\": [\n+      \"Mario Laul\",\n+      \"Chris Burniske\"\n+    ],\n+    \"Status\": \"Obsolete\",\n+    \"Category\": \"Consensus Process\",\n+    \"Created\": \"2019-08-31\",\n+    \"License\": \"MIT\",\n+    \"Discussions-To\": \"<https://forum.zcashcommunity.com/t/blocktown-development-fund-proposal-10-to-a-2-of-3-multisig-with-community-involved-third-entity/34782>\"\n+  }}"}],"filesTruncated":false,"preview":{"filename":"zip-1006.rst","title":"Development Fund of 10% to a 2-of-3 Multisig with Community-Involved Third Entity","status":"Obsolete","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"funding-mechanism-of-the-zcash-development-fund","text":"Funding mechanism of the Zcash Development Fund"},{"level":2,"id":"governance-of-the-zcash-development-fund","text":"Governance of the Zcash Development Fund"},{"level":1,"id":"issues-not-addressed-in-this-proposal-out-of-scope","text":"Issues not addressed in this proposal/Out-of-Scope"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"rationale","text":"Rationale"},{"level":1,"id":"discussion","text":"Discussion"},{"level":1,"id":"acknowledgements","text":"Acknowledgements"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":621,"title":"ZIP 32: Add an Account discovery section","author":"daira","createdAt":"2022-06-08T18:03:44Z","updatedAt":"2022-06-15T21:19:38Z","url":"https://github.com/zcash/zips/pull/621","diffUrl":"https://github.com/zcash/zips/pull/621.diff","headRef":"zip-0032-account-discovery","headRepo":"daira/zips","headSha":"8d0b7ed8c297b6aa0aa458e1c415d79f087efa01","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[32],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zip-0032.html","status":"modified","additions":92,"deletions":33,"patch":"@@ -25,8 +25,8 @@\n             <span class=\"math\">\\(% This ZIP makes heavy use of mathematical markup. If you can see this, you may want to instead view the rendered version at https://zips.z.cash/zip-0032 .\\)</span>\n         </p>\n         <section id=\"terminology\"><h2><span class=\"section-heading\">Terminology</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#terminology\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n-            <p>The key words \"MUST\", \"MUST NOT\", and \"MAY\" in this document are to be interpreted as described in RFC 2119. <a id=\"id1\" class=\"footnote_reference\" href=\"#rfc2119\">1</a></p>\n-            <p>\"Jubjub\" refers to the elliptic curve defined in <a id=\"id2\" class=\"footnote_reference\" href=\"#protocol-jubjub\">15</a>.</p>\n+            <p>The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this document are to be interpreted as described in RFC 2119. <a id=\"id1\" class=\"footnote_reference\" href=\"#rfc2119\">1</a></p>\n+            <p>\"Jubjub\" refers to the elliptic curve defined in <a id=\"id2\" class=\"footnote_reference\" href=\"#protocol-jubjub\">16</a>.</p>\n             <p>A \"chain code\" is a cryptovalue that is needed, in addition to a spending key, in order to derive descendant keys and addresses of that key.</p>\n             <p>The terms \"Testnet\" and \"Mainnet\" are to be interpreted as described in section 3.12 of the Zcash Protocol Specification <a id=\"id3\" class=\"footnote_reference\" href=\"#protocol-networks\">10</a>.</p>\n         </section>\n@@ -104,7 +104,7 @@\n                     <span class=\"math\">\\(\\mathsf{repr}_\\mathbb{J}(P)\\)</span>\n                  is the representation of the Jubjub elliptic curve point\n                     <span class=\"math\">\\(P\\)</span>\n-                 as a bit sequence, defined in <a id=\"id10\" class=\"footnote_reference\" href=\"#protocol-jubjub\">15</a>.</li>\n+                 as a bit sequence, defined in <a id=\"id10\" class=\"footnote_reference\" href=\"#protocol-jubjub\">16</a>.</li>\n                 <li>\n                     <span class=\"math\">\\(\\mathsf{BLAKE2b}\\text{-}\\mathsf{256}(p, x)\\)</span>\n                  refers to unkeyed BLAKE2b-256 in sequential mode, with an output digest length of 32 bytes, 16-byte personalization string\n@@ -144,9 +144,9 @@\n                     <span class=\"math\">\\(d\\)</span>\n                  to a base point on the Jubjub elliptic curve, or to\n                     <span class=\"math\">\\(\\bot\\)</span>\n-                 if the diversifier is invalid. It is instantiated in <a id=\"id11\" class=\"footnote_reference\" href=\"#protocol-concretediversifyhash\">13</a>.</li>\n+                 if the diversifier is invalid. It is instantiated in <a id=\"id11\" class=\"footnote_reference\" href=\"#protocol-concretediversifyhash\">14</a>.</li>\n             </ul>\n-            <p>The following algorithm standardized in <a id=\"id12\" class=\"footnote_reference\" href=\"#nist-sp-800-38g\">22</a> is used:</p>\n+            <p>The following algorithm standardized in <a id=\"id12\" class=\"footnote_reference\" href=\"#nist-sp-800-38g\">23</a> is used:</p>\n             <ul>\n                 <li>\n                     <span class=\"math\">\\(\\mathsf{FF1}\\text{-}\\mathsf{AES256.Encrypt}(key, tweak, x)\\)</span>\n@@ -385,7 +385,7 @@\n                 <section id=\"deriving-a-child-extended-full-viewing-key\"><h4><span class=\"section-heading\">Deriving a child extended full viewing key</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#deriving-a-child-extended-full-viewing-key\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h4>\n                     <p>Let\n                         <span class=\"math\">\\(\\mathcal{G}^\\mathsf{Sapling}\\)</span>\n-                     be as defined in <a id=\"id19\" class=\"footnote_reference\" href=\"#protocol-concretespendauthsig\">14</a> and let\n+                     be as defined in <a id=\"id19\" class=\"footnote_reference\" href=\"#protocol-concretespendauthsig\">15</a> and let\n                         <span class=\"math\">\\(\\mathcal{H}^\\mathsf{Sapling}\\)</span>\n                      be as defined in <a id=\"id20\" class=\"footnote_reference\" href=\"#protocol-saplingkeycomponents\">11</a>.</p>\n                     <p>\n@@ -809,12 +809,63 @@\n                     <span class=\"math\">\\(2^{88}\\)</span>\n                  payment addresses (unlike Sapling, all Orchard diversifiers are valid).</p>\n             </section>\n+            <section id=\"account-discovery\"><h3><span class=\"section-heading\">Account discovery</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#account-discovery\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n+                <p>A wallet that imports a master seed from an external source SHOULD attempt to discover accounts in the following manner.</p>\n+                <p>Let\n+                    <span class=\"math\">\\(M\\)</span>\n+                 be the subset of\n+                    <span class=\"math\">\\(\\{ m_\\mathsf{Sapling}, m_\\mathsf{Orchard} \\}\\)</span>\n+                 corresponding to master keys for address types that this wallet supports.</p>\n+                <ul>\n+                    <li>For each account index starting from\n+                        <span class=\"math\">\\(account = 0\\)</span>\n+                    :\n+                        <ul>\n+                            <li>For\n+                                <span class=\"math\">\\(m \\in M\\)</span>\n+                            :\n+                                <ol type=\"1\">\n+                                    <li>Derive the external full viewing key of the account at path\n+                                        <span class=\"math\">\\(m / purpose' / coin\\_type' / account'\\)</span>\n+                                    .</li>\n+                                    <li>Scan for transactions sending to this account using the algorithm in <a id=\"id37\" class=\"footnote_reference\" href=\"#protocol-scan\">13</a>.</li>\n+                                </ol>\n+                            </li>\n+                        </ul>\n+                        <p>If no transactions are found for either Sapling or Orchard at a given account index, break from the loop over\n+                            <span class=\"math\">\\(account\\)</span>\n+                        .</p>\n+                    </li>\n+                    <li>If the wallet supports Sapling addresses, then for each address index starting from\n+                        <span class=\"math\">\\(address\\_index = 0\\)</span>\n+                    :\n+                        <ol type=\"1\">\n+                            <li>Derive the external full viewing key of the Sapling address at path\n+                                <span class=\"math\">\\(m_{Sapling} / purpose' / coin\\_type' / \\mathtt{0x7FFFFFFF}' / address\\_index\\)</span>\n+                            .</li>\n+                            <li>Scan for transactions sending to this address using the algorithm in <a id=\"id38\" class=\"footnote_reference\" href=\"#protocol-scan\">13</a>.</li>\n+                        </ol>\n+                        <p>If no transactions are found for a range of (<a href=\"#legacy-address-gap-limit\">Legacy address gap limit</a>) consecutive addresses, break from the loop over\n+                            <span class=\"math\">\\(address\\_index\\)</span>\n+                        .</p>\n+                    </li>\n+                </ul>\n+                <p>(The loop over\n+                    <span class=\"math\">\\(address\\_index\\)</span>\n+                 finds legacy Sapling addresses generated by <cite>zcashd</cite>.)</p>\n+                <p>This algorithm is successful because wallets SHOULD disallow creation of new accounts if the previous one has no transaction history, as described in <a href=\"#key-path-levels\">Key path levels</a> above.</p>\n+                <p>Please note that the algorithm works with the transaction history, not account balances, so you can have an account with zero balance and the algorithm will still continue with discovery.</p>\n+                <section id=\"legacy-address-gap-limit\"><h4><span class=\"section-heading\">Legacy address gap limit</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#legacy-address-gap-limit\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h4>\n+                    <p>The RECOMMENDED address gap limit for legacy Sapling addresses is 20. If a wallet hits this number of unused addresses in a row, it expects there to be no used addresses beyond this point and SHOULD stop searching the legacy Sapling address chain. We scan just the external chains, because internal chains receive only coins that come from the associated external chains.</p>\n+                    <p>Wallet software should warn when the user is trying to exceed the gap limit on an external chain by generating a new address.</p>\n+                </section>\n+            </section>\n         </section>\n         <section id=\"specification-fingerprints-and-tags\"><h2><span class=\"section-heading\">Specification: Fingerprints and Tags</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#specification-fingerprints-and-tags\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n             <section id=\"sapling-full-viewing-key-fingerprints-and-tags\"><h3><span class=\"section-heading\">Sapling Full Viewing Key Fingerprints and Tags</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#sapling-full-viewing-key-fingerprints-and-tags\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>A \"Sapling full viewing key fingerprint\" of a full viewing key with raw encoding\n                     <span class=\"math\">\\(\\mathit{FVK}\\)</span>\n-                 (as specified in <a id=\"id37\" class=\"footnote_reference\" href=\"#protocol-saplingfullviewingkeyencoding\">18</a>) is given by:</p>\n+                 (as specified in <a id=\"id39\" class=\"footnote_reference\" href=\"#protocol-saplingfullviewingkeyencoding\">19</a>) is given by:</p>\n                 <ul>\n                     <li>\n                         <span class=\"math\">\\(\\mathsf{BLAKE2b}\\text{-}\\mathsf{256}(\\texttt{“ZcashSaplingFVFP”}, \\mathit{FVK})\\)</span>\n@@ -826,7 +877,7 @@\n             <section id=\"orchard-full-viewing-key-fingerprints-and-tags\"><h3><span class=\"section-heading\">Orchard Full Viewing Key Fingerprints and Tags</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#orchard-full-viewing-key-fingerprints-and-tags\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>An \"Orchard full viewing key fingerprint\" of a full viewing key with raw encoding\n                     <span class=\"math\">\\(\\mathit{FVK}\\)</span>\n-                 (as specified in <a id=\"id38\" class=\"footnote_reference\" href=\"#protocol-orchardfullviewingkeyencoding\">20</a>) is given by:</p>\n+                 (as specified in <a id=\"id40\" class=\"footnote_reference\" href=\"#protocol-orchardfullviewingkeyencoding\">21</a>) is given by:</p>\n                 <ul>\n                     <li>\n                         <span class=\"math\">\\(\\mathsf{BLAKE2b}\\text{-}\\mathsf{256}(\\texttt{“ZcashOrchardFVFP”}, \\mathit{FVK})\\)</span>\n@@ -851,7 +902,7 @@\n             </section>\n         </section>\n         <section id=\"specification-key-encodings\"><h2><span class=\"section-heading\">Specification: Key Encodings</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#specification-key-encodings\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n-            <p>The following encodings are analogous to the <code>xprv</code> and <code>xpub</code> encodings defined in BIP 32 for transparent keys and addresses. Each key type has a raw representation and a Bech32 <a id=\"id39\" class=\"footnote_reference\" href=\"#bip-0173\">7</a> encoding.</p>\n+            <p>The following encodings are analogous to the <code>xprv</code> and <code>xpub</code> encodings defined in BIP 32 for transparent keys and addresses. Each key type has a raw representation and a Bech32 <a id=\"id41\" class=\"footnote_reference\" href=\"#bip-0173\">7</a> encoding.</p>\n             <section id=\"sapling-extended-spending-keys\"><h3><span class=\"section-heading\">Sapling extended spending keys</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#sapling-extended-spending-keys\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h3>\n                 <p>A Sapling extended spending key\n                     <span class=\"math\">\\((\\mathsf{ask, nsk, ovk, dk, c})\\)</span>\n@@ -943,7 +994,7 @@\n                     <span class=\"math\">\\(0\\)</span>\n                 .</p>\n                 <p>When encoded as Bech32, the Human-Readable Part is <code>secret-orchard-extsk-main</code> for Mainnet, or <code>secret-orchard-extsk-test</code> for Testnet.</p>\n-                <p>We define this encoding for completeness, however given that it includes the capability to derive child spending keys, we expect that most wallets will only expose the regular Orchard spending key encoding to users <a id=\"id40\" class=\"footnote_reference\" href=\"#protocol-orchardspendingkeyencoding\">21</a>.</p>\n+                <p>We define this encoding for completeness, however given that it includes the capability to derive child spending keys, we expect that most wallets will only expose the regular Orchard spending key encoding to users <a id=\"id42\" class=\"footnote_reference\" href=\"#protocol-orchardspendingkeyencoding\">22</a>.</p>\n             </section>\n         </section>\n         <section id=\"values-reserved-due-to-previous-specification-for-sprout\"><h2><span class=\"section-heading\">Values reserved due to previous specification for Sprout</span><span class=\"section-anchor\"> <a rel=\"bookmark\" href=\"#values-reserved-due-to-previous-specification-for-sprout\"><img width=\"24\" height=\"24\" class=\"section-anchor\" src=\"assets/images/section-anchor.png\" alt=\"\"></a></span></h2>\n@@ -1047,110 +1098,118 @@\n                 <tbody>\n                     <tr>\n                         <th>9</th>\n-                        <td><a href=\"protocol/protocol.pdf\">Zcash Protocol Specification, Version 2022.2.19 or later [NU5 proposal]</a></td>\n+                        <td><a href=\"protocol/protocol.pdf\">Zcash Protocol Specification, Version 2022.3.2 or later [NU5 proposal]</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n             <table id=\"protocol-networks\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>10</th>\n-                        <td><a href=\"protocol/protocol.pdf#networks\">Zcash Protocol Specification, Version 2022.2.19. Section 3.12: Mainnet and Testnet</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#networks\">Zcash Protocol Specification, Version 2022.3.2. Section 3.12: Mainnet and Testnet</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n             <table id=\"protocol-saplingkeycomponents\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>11</th>\n-                        <td><a href=\"protocol/protocol.pdf#saplingkeycomponents\">Zcash Protocol Specification, Version 2022.2.19. Section 4.2.2: Sapling Key Components</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#saplingkeycomponents\">Zcash Protocol Specification, Version 2022.3.2. Section 4.2.2: Sapling Key Components</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n             <table id=\"protocol-orchardkeycomponents\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>12</th>\n-                        <td><a href=\"protocol/protocol.pdf#orchardkeycomponents\">Zcash Protocol Specification, Version 2022.2.19. Section 4.2.3: Orchard Key Components</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#orchardkeycomponents\">Zcash Protocol Specification, Version 2022.3.2. Section 4.2.3: Orchard Key Components</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-concretediversifyhash\" class=\"footnote\">\n+            <table id=\"protocol-scan\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>13</th>\n-                        <td><a href=\"protocol/protocol.pdf#concretediversifyhash\">Zcash Protocol Specification, Version 2022.2.19. Section 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#scan\">Zcash Protocol Specification, Version 2022.3.2. Section 4.21: Block Chain Scanning (Sapling and Orchard)</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-concretespendauthsig\" class=\"footnote\">\n+            <table id=\"protocol-concretediversifyhash\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>14</th>\n-                        <td><a href=\"protocol/protocol.pdf#concretespendauthsig\">Zcash Protocol Specification, Version 2022.2.19. Section 5.4.6.1: Spend Authorization Signature</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#concretediversifyhash\">Zcash Protocol Specification, Version 2022.3.2. Section 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-jubjub\" class=\"footnote\">\n+            <table id=\"protocol-concretespendauthsig\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>15</th>\n-                        <td><a href=\"protocol/protocol.pdf#jubjub\">Zcash Protocol Specification, Version 2022.2.19. Section 5.4.9.3: Jubjub</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#concretespendauthsig\">Zcash Protocol Specification, Version 2022.3.2. Section 5.4.7.1: Spend Authorization Signature</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-sproutpaymentaddrencoding\" class=\"footnote\">\n+            <table id=\"protocol-jubjub\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>16</th>\n-                        <td><a href=\"protocol/protocol.pdf#sproutpaymentaddrencoding\">Zcash Protocol Specification, Version 2022.2.19. Section 5.6.2.1: Sprout Payment Addresses</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#jubjub\">Zcash Protocol Specification, Version 2022.3.2. Section 5.4.9.3: Jubjub</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-sproutspendingkeyencoding\" class=\"footnote\">\n+            <table id=\"protocol-sproutpaymentaddrencoding\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>17</th>\n-                        <td><a href=\"protocol/protocol.pdf#sproutspendingkeyencoding\">Zcash Protocol Specification, Version 2022.2.19. Section 5.6.2.3: Sprout Spending Keys</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#sproutpaymentaddrencoding\">Zcash Protocol Specification, Version 2022.3.2. Section 5.6.2.1: Sprout Payment Addresses</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-saplingfullviewingkeyencoding\" class=\"footnote\">\n+            <table id=\"protocol-sproutspendingkeyencoding\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>18</th>\n-                        <td><a href=\"protocol/protocol.pdf#saplingfullviewingkeyencoding\">Zcash Protocol Specification, Version 2022.2.19. Section 5.6.3.3: Sapling Full Viewing Keys</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#sproutspendingkeyencoding\">Zcash Protocol Specification, Version 2022.3.2. Section 5.6.2.3: Sprout Spending Keys</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-saplingspendingkeyencoding\" class=\"footnote\">\n+            <table id=\"protocol-saplingfullviewingkeyencoding\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>19</th>\n-                        <td><a href=\"protocol/protocol.pdf#saplingspendingkeyencoding\">Zcash Protocol Specification, Version 2022.2.19. Section 5.6.3.4: Sapling Spending Keys</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#saplingfullviewingkeyencoding\">Zcash Protocol Specification, Version 2022.3.2. Section 5.6.3.3: Sapling Full Viewing Keys</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-orchardfullviewingkeyencoding\" class=\"footnote\">\n+            <table id=\"protocol-saplingspendingkeyencoding\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>20</th>\n-                        <td><a href=\"protocol/protocol.pdf#orchardfullviewingkeyencoding\">Zcash Protocol Specification, Version 2022.2.19. Section 5.6.4.4: Orchard Raw Full Viewing Keys</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#saplingspendingkeyencoding\">Zcash Protocol Specification, Version 2022.3.2. Section 5.6.3.4: Sapling Spending Keys</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"protocol-orchardspendingkeyencoding\" class=\"footnote\">\n+            <table id=\"protocol-orchardfullviewingkeyencoding\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>21</th>\n-                        <td><a href=\"protocol/protocol.pdf#orchardspendingkeyencoding\">Zcash Protocol Specification, Version 2022.2.19. Section 5.6.4.5: Orchard Spending Keys</a></td>\n+                        <td><a href=\"protocol/protocol.pdf#orchardfullviewingkeyencoding\">Zcash Protocol Specification, Version 2022.3.2. Section 5.6.4.4: Orchard Raw Full Viewing Keys</a></td>\n                     </tr>\n                 </tbody>\n             </table>\n-            <table id=\"nist-sp-800-38g\" class=\"footnote\">\n+            <table id=\"protocol-orchardspendingkeyencoding\" class=\"footnote\">\n                 <tbody>\n                     <tr>\n                         <th>22</th>\n+                        <td><a href=\"protocol/protocol.pdf#orchardspendingkeyencoding\">Zcash Protocol Specification, Version 2022.3.2. Section 5.6.4.5: Orchard Spending Keys</a></td>\n+                    </tr>\n+                </tbody>\n+            </table>\n+            <table id=\"nist-sp-800-38g\" class=\"footnote\">\n+                <tbody>\n+                    <tr>\n+                        <th>23</th>\n                         <td><a href=\"https://dx.doi.org/10.6028/NIST.SP.800-38G\">NIST Special Publication 800-38G — Recommendation for Block Cipher Modes of Operation: Methods for Format-Preserving Encryption</a></td>\n                     </tr>\n                 </tbody>"},{"filename":"zip-0032.rst","status":"modified","additions":62,"deletions":15,"patch":"@@ -20,8 +20,8 @@\n Terminology\n ===========\n \n-The key words \"MUST\", \"MUST NOT\", and \"MAY\" in this document are to be interpreted as described in RFC 2119.\n-[#RFC2119]_\n+The key words \"MUST\", \"MUST NOT\", \"SHOULD\", \"SHOULD NOT\", and \"MAY\" in this document are to be\n+interpreted as described in RFC 2119. [#RFC2119]_\n \n \"Jubjub\" refers to the elliptic curve defined in [#protocol-jubjub]_.\n \n@@ -549,6 +549,52 @@ each recipient.\n Note that a given account can have a maximum of :math:`2^{88}` payment addresses (unlike Sapling, all Orchard\n diversifiers are valid).\n \n+Account discovery\n+-----------------\n+\n+A wallet that imports a master seed from an external source SHOULD attempt to discover accounts in the\n+following manner.\n+\n+Let :math:`M` be the subset of :math:`\\{ m_\\mathsf{Sapling}, m_\\mathsf{Orchard} \\}` corresponding to master keys\n+for address types that this wallet supports.\n+\n+* For each account index starting from :math:`account = 0`:\n+\n+  * For :math:`m \\in M`:\n+\n+    1. Derive the external full viewing key of the account at path :math:`m / purpose' / coin\\_type' / account'`.\n+    2. Scan for transactions sending to this account using the algorithm in [#protocol-scan]_.\n+\n+  If no transactions are found for either Sapling or Orchard at a given account index, break from the loop\n+  over :math:`account`.\n+\n+* If the wallet supports Sapling addresses, then for each address index starting from :math:`address\\_index = 0`:\n+\n+  1. Derive the external full viewing key of the Sapling address at path :math:`m_{Sapling} / purpose' / coin\\_type' / \\mathtt{0x7FFFFFFF}' / address\\_index`.\n+  2. Scan for transactions sending to this address using the algorithm in [#protocol-scan]_.\n+\n+  If no transactions are found for a range of (`Legacy address gap limit`_) consecutive addresses, break\n+  from the loop over :math:`address\\_index`.\n+\n+(The loop over :math:`address\\_index` finds legacy Sapling addresses generated by `zcashd`.)\n+\n+This algorithm is successful because wallets SHOULD disallow creation of new accounts if the previous one\n+has no transaction history, as described in `Key path levels`_ above.\n+\n+Please note that the algorithm works with the transaction history, not account balances, so you can have\n+an account with zero balance and the algorithm will still continue with discovery.\n+\n+Legacy address gap limit\n+````````````````````````\n+\n+The RECOMMENDED address gap limit for legacy Sapling addresses is 20. If a wallet hits this number of unused\n+addresses in a row, it expects there to be no used addresses beyond this point and SHOULD stop searching\n+the legacy Sapling address chain. We scan just the external chains, because internal chains receive only\n+coins that come from the associated external chains.\n+\n+Wallet software should warn when the user is trying to exceed the gap limit on an external chain by\n+generating a new address.\n+\n \n Specification: Fingerprints and Tags\n ====================================\n@@ -695,17 +741,18 @@ References\n .. [#slip-0044] `SLIP 44: Registered coin types for BIP-0044 <https://github.com/satoshilabs/slips/blob/master/slip-0044.md>`_\n .. [#bip-0173] `BIP 173: Base32 address format for native v0-16 witness outputs <https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki>`_\n .. [#zip-0316] `ZIP 316: Unified Addresses and Unified Viewing Keys <zip-0316.rst>`_\n-.. [#protocol] `Zcash Protocol Specification, Version 2022.2.19 or later [NU5 proposal] <protocol/protocol.pdf>`_\n-.. [#protocol-networks] `Zcash Protocol Specification, Version 2022.2.19. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n-.. [#protocol-saplingkeycomponents] `Zcash Protocol Specification, Version 2022.2.19. Section 4.2.2: Sapling Key Components <protocol/protocol.pdf#saplingkeycomponents>`_\n-.. [#protocol-orchardkeycomponents] `Zcash Protocol Specification, Version 2022.2.19. Section 4.2.3: Orchard Key Components <protocol/protocol.pdf#orchardkeycomponents>`_\n-.. [#protocol-concretediversifyhash] `Zcash Protocol Specification, Version 2022.2.19. Section 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions <protocol/protocol.pdf#concretediversifyhash>`_\n-.. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2022.2.19. Section 5.4.6.1: Spend Authorization Signature <protocol/protocol.pdf#concretespendauthsig>`_\n-.. [#protocol-jubjub] `Zcash Protocol Specification, Version 2022.2.19. Section 5.4.9.3: Jubjub <protocol/protocol.pdf#jubjub>`_\n-.. [#protocol-sproutpaymentaddrencoding] `Zcash Protocol Specification, Version 2022.2.19. Section 5.6.2.1: Sprout Payment Addresses <protocol/protocol.pdf#sproutpaymentaddrencoding>`_\n-.. [#protocol-sproutspendingkeyencoding] `Zcash Protocol Specification, Version 2022.2.19. Section 5.6.2.3: Sprout Spending Keys <protocol/protocol.pdf#sproutspendingkeyencoding>`_\n-.. [#protocol-saplingfullviewingkeyencoding] `Zcash Protocol Specification, Version 2022.2.19. Section 5.6.3.3: Sapling Full Viewing Keys <protocol/protocol.pdf#saplingfullviewingkeyencoding>`_\n-.. [#protocol-saplingspendingkeyencoding] `Zcash Protocol Specification, Version 2022.2.19. Section 5.6.3.4: Sapling Spending Keys <protocol/protocol.pdf#saplingspendingkeyencoding>`_\n-.. [#protocol-orchardfullviewingkeyencoding] `Zcash Protocol Specification, Version 2022.2.19. Section 5.6.4.4: Orchard Raw Full Viewing Keys <protocol/protocol.pdf#orchardfullviewingkeyencoding>`_\n-.. [#protocol-orchardspendingkeyencoding] `Zcash Protocol Specification, Version 2022.2.19. Section 5.6.4.5: Orchard Spending Keys <protocol/protocol.pdf#orchardspendingkeyencoding>`_\n+.. [#protocol] `Zcash Protocol Specification, Version 2022.3.2 or later [NU5 proposal] <protocol/protocol.pdf>`_\n+.. [#protocol-networks] `Zcash Protocol Specification, Version 2022.3.2. Section 3.12: Mainnet and Testnet <protocol/protocol.pdf#networks>`_\n+.. [#protocol-saplingkeycomponents] `Zcash Protocol Specification, Version 2022.3.2. Section 4.2.2: Sapling Key Components <protocol/protocol.pdf#saplingkeycomponents>`_\n+.. [#protocol-orchardkeycomponents] `Zcash Protocol Specification, Version 2022.3.2. Section 4.2.3: Orchard Key Components <protocol/protocol.pdf#orchardkeycomponents>`_\n+.. [#protocol-scan] `Zcash Protocol Specification, Version 2022.3.2. Section 4.21: Block Chain Scanning (Sapling and Orchard) <protocol/protocol.pdf#scan>`_\n+.. [#protocol-concretediversifyhash] `Zcash Protocol Specification, Version 2022.3.2. Section 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions <protocol/protocol.pdf#concretediversifyhash>`_\n+.. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2022.3.2. Section 5.4.7.1: Spend Authorization Signature <protocol/protocol.pdf#concretespendauthsig>`_\n+.. [#protocol-jubjub] `Zcash Protocol Specification, Version 2022.3.2. Section 5.4.9.3: Jubjub <protocol/protocol.pdf#jubjub>`_\n+.. [#protocol-sproutpaymentaddrencoding] `Zcash Protocol Specification, Version 2022.3.2. Section 5.6.2.1: Sprout Payment Addresses <protocol/protocol.pdf#sproutpaymentaddrencoding>`_\n+.. [#protocol-sproutspendingkeyencoding] `Zcash Protocol Specification, Version 2022.3.2. Section 5.6.2.3: Sprout Spending Keys <protocol/protocol.pdf#sproutspendingkeyencoding>`_\n+.. [#protocol-saplingfullviewingkeyencoding] `Zcash Protocol Specification, Version 2022.3.2. Section 5.6.3.3: Sapling Full Viewing Keys <protocol/protocol.pdf#saplingfullviewingkeyencoding>`_\n+.. [#protocol-saplingspendingkeyencoding] `Zcash Protocol Specification, Version 2022.3.2. Section 5.6.3.4: Sapling Spending Keys <protocol/protocol.pdf#saplingspendingkeyencoding>`_\n+.. [#protocol-orchardfullviewingkeyencoding] `Zcash Protocol Specification, Version 2022.3.2. Section 5.6.4.4: Orchard Raw Full Viewing Keys <protocol/protocol.pdf#orchardfullviewingkeyencoding>`_\n+.. [#protocol-orchardspendingkeyencoding] `Zcash Protocol Specification, Version 2022.3.2. Section 5.6.4.5: Orchard Spending Keys <protocol/protocol.pdf#orchardspendingkeyencoding>`_\n .. [#NIST-SP-800-38G] `NIST Special Publication 800-38G — Recommendation for Block Cipher Modes of Operation: Methods for Format-Preserving Encryption <https://dx.doi.org/10.6028/NIST.SP.800-38G>`_"}],"filesTruncated":false,"preview":{"filename":"zip-0032.rst","title":"Shielded Hierarchical Deterministic Wallets","status":"Final","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"conventions","text":"Conventions"},{"level":1,"id":"specification-sapling-key-derivation","text":"Specification: Sapling key derivation"},{"level":2,"id":"sapling-extended-keys","text":"Sapling extended keys"},{"level":2,"id":"sapling-helper-functions","text":"Sapling helper functions"},{"level":2,"id":"sapling-master-key-generation","text":"Sapling master key generation"},{"level":2,"id":"sapling-child-key-derivation","text":"Sapling child key derivation"},{"level":3,"id":"deriving-a-child-extended-spending-key","text":"Deriving a child extended spending key"},{"level":3,"id":"deriving-a-child-extended-full-viewing-key","text":"Deriving a child extended full viewing key"},{"level":2,"id":"sapling-internal-key-derivation","text":"Sapling internal key derivation"},{"level":3,"id":"deriving-a-sapling-internal-spending-key","text":"Deriving a Sapling internal spending key"},{"level":3,"id":"deriving-a-sapling-internal-full-viewing-key","text":"Deriving a Sapling internal full viewing key"},{"level":2,"id":"sapling-diversifier-derivation","text":"Sapling diversifier derivation"},{"level":1,"id":"specification-orchard-key-derivation","text":"Specification: Orchard key derivation"},{"level":2,"id":"orchard-extended-keys","text":"Orchard extended keys"},{"level":2,"id":"orchard-master-key-generation","text":"Orchard master key generation"},{"level":2,"id":"orchard-child-key-derivation","text":"Orchard child key derivation"},{"level":2,"id":"orchard-internal-key-derivation","text":"Orchard internal key derivation"},{"level":2,"id":"orchard-diversifier-derivation","text":"Orchard diversifier derivation"},{"level":1,"id":"specification-wallet-usage","text":"Specification: Wallet usage"},{"level":2,"id":"key-path-levels","text":"Key path levels"},{"level":2,"id":"sapling-key-path","text":"Sapling key path"},{"level":2,"id":"orchard-key-path","text":"Orchard key path"},{"level":2,"id":"account-discovery","text":"Account discovery"},{"level":3,"id":"legacy-address-gap-limit","text":"Legacy address gap limit"},{"level":1,"id":"specification-fingerprints-and-tags","text":"Specification: Fingerprints and Tags"},{"level":2,"id":"sapling-full-viewing-key-fingerprints-and-tags","text":"Sapling Full Viewing Key Fingerprints and Tags"},{"level":2,"id":"orchard-full-viewing-key-fingerprints-and-tags","text":"Orchard Full Viewing Key Fingerprints and Tags"},{"level":2,"id":"seed-fingerprints","text":"Seed Fingerprints"},{"level":1,"id":"specification-key-encodings","text":"Specification: Key Encodings"},{"level":2,"id":"sapling-extended-spending-keys","text":"Sapling extended spending keys"},{"level":2,"id":"sapling-extended-full-viewing-keys","text":"Sapling extended full viewing keys"},{"level":2,"id":"orchard-extended-spending-keys","text":"Orchard extended spending keys"},{"level":1,"id":"values-reserved-due-to-previous-specification-for-sprout","text":"Values reserved due to previous specification for Sprout"},{"level":1,"id":"test-vectors","text":"Test Vectors"},{"level":1,"id":"reference-implementation","text":"Reference Implementation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":373,"title":"Create  ZIP402","author":"oxarbitrage","createdAt":"2020-05-26T23:07:28Z","updatedAt":"2022-04-18T21:26:02Z","url":"https://github.com/zcash/zips/pull/373","diffUrl":"https://github.com/zcash/zips/pull/373.diff","headRef":"zip402","headRepo":"oxarbitrage/zips","headSha":"ca0701301a85cb3b7381c5f5277a0f8a4e4f2abf","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"Attempt for https://github.com/zcash/zips/issues/365","zipRefs":[402],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zip-0402.rst","status":"added","additions":166,"deletions":0,"patch":"@@ -0,0 +1,166 @@\n+::\n+\n+  ZIP: 402\n+  Title: Change the fundamental wallet database from BerkeleyDB to SQLite\n+  Owners: Alfredo Garcia <oxarbitrage@gmail.com>\n+  Status: Draft\n+  Category: Wallet\n+  Created: 2020-05-30\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\" and \"MAY\" in this document are to be interpreted as described in RFC 2119. [#RFC2119]_\n+\n+Abstract\n+========\n+\n+This document proposes a change of the database zcashd wallet currently use from BerkeleyDB into SQLite. It also proposes the separation of wallet transaction and other recoverable data into a separated database file.\n+\n+Motivation\n+==========\n+\n+The main reason of the database change is reliability, SQLite haves a very good atomicity implementation [#SQLiteAtomic]_. Relational databases reduces redundancy and SQLite specifically is the world most used for local storage, it is also well mantained, have a good C++ interface, it is portable and open source.\n+\n+The database separation between recoverable versus non-recoverable data is to avoid writting too often to the file where the important non-recoverable data like public and private keys are located. Isolating transactions and others from the main wallet database will increase performance and reduce the risk of corruption.\n+\n+Specification\n+=============\n+\n+After [#ZIP400]_ and its detailed information about all database keys-values and functionality we know what is needed to change the fundamental database to something else.\n+\n+This document proposes a new relational schema for 2 new SQLite databases where all the original properties are present but just organized in a different way. Below schema is an aproximation and can change during implementation.\n+\n+The following sql schema is proposed for a database named **wallet.sqlite** with non-recoverable information::\n+\n+    CREATE TABLE accounts(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT\n+        id_address        INTEGER\n+        name              TEXT\n+        account           BLOB);\n+        \n+    CREATE TABLE account_entries(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT,\n+        id_account        INTEGER,\n+        entry             BLOB);\n+\n+    CREATE TABLE addresses(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT,\n+        address           TEXT,\n+        id_account        INTEGER,\n+        name              TEXT,\n+        purpose           TEXT);\n+        \n+    CREATE TABLE destdata(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT,\n+        id_address        INTEGER,\n+        key               TEXT,\n+        value             TEXT);\n+        \n+    CREATE TABLE sapling(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT,\n+        id_address        INTEGER,\n+        key               BLOB,\n+        ckey              BLOB,\n+        meta              BLOB,\n+        cmeta             BLOB,\n+        ivk               BLOB,\n+        extfvk            BLOB);\n+        \n+    CREATE TABLE settings(\n+        chdseed           BLOB\n+        hdchain           BLOB\n+        hdseed            BLOB\n+        minversion        TEXT\n+        mkey              BLOB\n+        version           TEXT\n+        cscript           BLOB);\n+        \n+    CREATE TABLE sprout(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT,\n+        id_address        INTEGER,\n+        key               BLOB,\n+        ckey              BLOB,\n+        meta              BLOB,\n+        cmeta             BLOB,\n+        vkey              BLOB);\n+\n+    CREATE TABLE transparent(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT,\n+        id_address        INTEGER,\n+        key               BLOB,\n+        ckey              BLOB,\n+        poolindex         INTEGER,\n+        meta              BLOB,\n+        cmeta             BLOB,\n+        destination       TEXT,\n+        is_default        BOOLEAN);\n+\n+A separated database named **transactions.sqlite** with recoverable information and the following tables is proposed::\n+\n+    CREATE TABLE transactions(\n+        id                INTEGER PRIMARY KEY AUTOINCREMENT,\n+        hash              BLOB,\n+        tx                BLOB);\n+        \n+    CREATE TABLE state(\n+        bestblock         BLOB,\n+        orderposnext      INTEGER,\n+        witnesscachesize  INTEGER);\n+\n+A new class ``SQLite`` will be added to the wallet collection of classes where the following low level functions can be found:\n+\n+- ``Open(strFile)`` - Open a new database file.\n+- ``Exist(strFile)`` - Check if a database exist.\n+- ``Schema(strSchema)`` - Create a new schema.\n+- ``Write(strSql)`` - Insert or update a database table. \n+- ``Read(strSql)`` - Select data from a table.\n+- ``Erase(strSql)`` - Delete rows from a table.\n+- ``Count(strSql)`` - Get the count of rows from a select query. This is a special case of ``Read()``.\n+- ``GetRow(strSql)`` - Get a single row from a table. This is a special case of ``Read()``.\n+- ``GetVersion(strFile)`` - Get the version of the current database.\n+\n+``CWalletDB`` is renamed to ``CWalletDBD`` to avoid ambiguity. A new class ``CWalletSQLite`` will be created as a copy of the same definition. This means the new class will have the same member functions with the same arguments and the same return values as the original version, the difference is in the implementation of functions where the ones in the new class will write/read to/from SQLite using the low level functions listed above.\n+\n+As an example, in ``CWalletDB`` we currently have ``WriteSaplingPaymentAddress``::\n+\n+    return Write(std::make_pair(std::string(\"sapzaddr\"), addr), ivk, false);\n+\n+After implementation this function will be located at ``CWalletDBD::WriteSaplingPaymentAddress()`` with the same functionality.\n+\n+In ``CWalletSQLite::WriteSaplingPaymentAddress()`` a possible implementation can be something similar to::\n+\n+    auto id_address = GetRow(\"SELECT id from addresses WHERE address=addr\");\n+    return Write(\"INSERT OR REPLACE INTO sapling(id_address, ivk) VALUES(id_address, ivk)\");\n+\n+All member functions in the new ``CWalletSQLite`` class need to be refactored to interact with the new database. Exceptions to this rule can happen if performance can be increased by changing the inputs/outputs of any of the member functions or by combining 2 or more functions into 1.\n+\n+Adding more keys\n+----------------\n+\n+It is common practice for the zcashd developers to add new keys and values to the database for different purposes, to do it in SQLite the schema need to change.\n+The implementation must guarantee that adding new fields to any table will be a non breaking change while renaming tables, renaming columns or changing column types may be fatal if not implemented propertly. \n+\n+To mitigate the risk a version system must be implemented so older database schema can be updated propertly.\n+Schema tests must be created by the implementation.\n+\n+Requirements\n+============\n+\n+Sqlite3 is needed as a dependendency of zcashd [#SQLiteGithub]_. No additional dependencies are needed.\n+\n+Backward compatilibility\n+========================\n+\n+A new wallet created after the code is implemented and tested will be created with SQLite. Command line options to choose the underlying database may be provided. A converter tool from/to SQLite/BDB may be useful and can be provided as part of the implementation.\n+\n+References\n+==========\n+\n+.. [#RFC2119] `Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_\n+.. [#SQLiteAtomic] `SQLite atomic implementation <https://www.sqlite.org/atomiccommit.html>`_\n+.. [#ZIP400] `ZIP 400 - Wallet database format <https://github.com/zcash/zips/pull/372>`_\n+.. [#ZIP402Issue] `ZIP 402 Issue <https://github.com/zcash/zips/issues/365>`_\n+.. [#SQLiteGithub] `SQLite Github mirror <https://github.com/sqlite/sqlite>`_"}],"filesTruncated":false,"preview":{"filename":"zip-0402.rst","title":"Change the fundamental wallet database from BerkeleyDB to SQLite","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"specification","text":"Specification"},{"level":2,"id":"adding-more-keys","text":"Adding more keys"},{"level":1,"id":"requirements","text":"Requirements"},{"level":1,"id":"backward-compatilibility","text":"Backward compatilibility"},{"level":1,"id":"references","text":"References"}],"format":"rst"}},{"number":391,"title":"ZIP: tor transaction relay network - an in-progress standard for improved network privacy via auxillary protocol.","author":"nathan-at-least","createdAt":"2020-08-12T14:16:30Z","updatedAt":"2022-04-18T21:26:02Z","url":"https://github.com/zcash/zips/pull/391","diffUrl":"https://github.com/zcash/zips/pull/391.diff","headRef":"zip-tor-txn-relay-network","headRepo":"nathan-at-least/zips","headSha":"ece8f42b4443bb430e80aafb880bb249e15f4bba","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"This branch and PR build atop of #389.","zipRefs":[],"draftRefs":[],"mapped":false,"claimed":false,"files":[{"filename":"zip-tor-txn-relay-network.rst","status":"added","additions":133,"deletions":0,"patch":"@@ -0,0 +1,133 @@\n+::\n+\n+  ZIP: ??? <TTRN>\n+  Title: The Zcash Tor Transaction Relay Network\n+  Owners: Nathan Wilcox <nathan@electriccoin.co>\n+  Credits: \n+  Status: Proposed\n+  Category: Application Standard\n+  Created: 2020-08-12\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"SHOULD\", and \"OPTIONAL\" in this document are to be interpreted\n+as described in RFC 2119. [#RFC2119]_\n+\n+The term \"network upgrade\" in this document is to be interpreted as described in ZIP 200\n+[#zip-0200]_.\n+\n+The term \"Sapling\" in this document is to be interpreted as described in ZIP 205\n+[#zip-0205]_.\n+\n+The term \"Sprout value pool balance\" in this document is to be interpreted as described\n+in ZIP 209 [#zip-0209]_.\n+\n+\n+Abstract\n+========\n+\n+The Zcash `Tor Transaction Relay Network` (`TTRN`) is an auxillary Zcash protocol to improve network privacy of wallets which issue transactions. TTRN `Service Providers` publish a `Service Descriptor` to a well-known Wishing Well (see [FIXME link to zip-wishing-well]), which includes a `registration fee` paid into the well (ie burned). TTRN-enabled wallets subscribe to that Wishing Well to discover Providers. When sending a shielded transaction the wallet selects a random provider, connects to it over Tor, and delivers the transaction via a TTRN-specific `Delivery Protocol`. These transactions pay `TTRN service fees` to providers.\n+\n+As an auxillary protocol, it is transparent and backwards compatible with the Zcash P2P protocol and consensus layer, and it is also opt-in for wallet usage. Light wallets, as well as full-node wallets, may be able to protect an important aspect of their privacy from hosted service providers with this protocol.\n+\n+Motivation\n+==========\n+\n+When wallets generate transactions they must transmit them to the Zcash network, and this initial transmission is a weak point for Zcash network privacy: it is possible for network peers to determine which node originated a transaction, even when the P2P protocol is routed over Tor [#]. By associating two different fully shielded transactions with the same originating peer, those transactions are linked by this network metadata, thereby violating a key privacy property that Zeroknowledge proofs protect against on-chain analysis. When a peer does not route its P2P traffic over Tor, this association can also include an IP address, which can reveal some information about the physical location of the wallet.\n+\n+.. [#] This assertion is an educated bet by the author. FIXME: find published research or supporting evidence to back up this claim.\n+\n+For mobile light wallets, the originating peer analysis above typically identifies the mobile wallet service provider's peer. Mobile wallets are even more directly vulnerable to their hosting provider in typical deployment architectures, because those hosting providers relay all of a wallet's transactions, and the hosting provider can trivially link those transactions and furthermore typically have more information about the mobile wallet and its user.\n+\n+This protocol aims to protect the association of which transactions originate with which peers, because each transaction arrives to a random TTRN provider via a new fresh Tor circuit with no history. This may potentially also protect the association of which transactions originate with which mobile light wallets against analysis by mobile wallet service providers.\n+\n+Design Goals\n+============\n+\n+TODO: refine this section to be more precise/complete.\n+\n+- Protect linkage between wallets and the transactions they originate.\n+- Providers don't learn anything about the recipient addresses, amounts, memos, or change in a relayed transaction except for the single output amount & memo used to pay for the service.\n+- Opt-in for wallets.\n+- Feasible for mobile light clients.\n+- Auxillary protocol requiring no changes to the Zcash P2P protocol or consensus rules.\n+- Incentivize providers via revenue stream.\n+- Disincentivize attacker control of a large proportion of the provider network via registration fees.\n+\n+Non-Goals\n+---------\n+\n+- Protecting the network topological location of providers. (Anyone may determine a transaction first reaches the P2P network from a provider.)\n+- Protecting providers from \"service identification\": anyone may determine a node is a TTRN provider based on its txn emission patterns.\n+- Protecting against network attacks which Tor is vulnerable against.\n+- Varied / self-set provider fees; service fee and registration fee are the same for all participants.\n+- Dynamic fee adjustments; the fee schedules are manually set for simplicity, even though that may lead to suboptimal resource deployment.\n+\n+Specification\n+=============\n+\n+- `Address Pairs` are a pair of the Provider's `Sapling Address` and `Tor Hidden Service`.\n+- The Tor Hidden Service connects to the Provider's `TTRN Relay Protocol` server.\n+- A Provider periodically `registers` their Address Pair by sending a memo to the `TTRN Wishing Well` which pays at least `Registration Fee` into the well. This is called a `Registration`.\n+- A `Registration` is `active` when it pays at least `Registration Fee` into the well, and it is in a block with at least `REGISTRATION_MIN_CONF` confirmations up until `REGISTRATION_MAX_CONF`.\n+- A Wallet observes all registrations with the `Wishing Well Viewing Key` and maintains them into a list.\n+- The wallet `deduplicates` the list as follows:\n+    - Remove any registration that's not `active`.\n+    - Where the same `Sapling Address` appears multiple times, remove all but the earliest entry (with the lowest block height).\n+    - Where the same `Tor Hidden Service` appears multiple times, remove all but the earliest entry.\n+- When a Wallet intends to send a shielded transfer, it:\n+    - Selects a uniformly random entry in the `deduplicated list`. Wallets must use uniform random selection and not rely on local information such as how reliable a provider is.\n+    - Includes a Sapling Shielded Output to the given Sapling Address transferring at least `Relay Fee` ZEC. The memo field must be empty and is reserved.\n+    - Generates necessary proofs and signs the complete transaction.\n+    - Connects to the hidden service and executes the `TTRN Relay Protocol` as a client.\n+- When a provider receives a transaction via the Hidden Service TTRN Relay Protocol service, it decodes the transfer to its Sapling Address.\n+    - If the fee paid is at least `Relay Fee`, it submits the transaction to the Zcash network vai the P2P protocol.\n+    - If the fee paid is less, it drops the transaction.\n+\n+Security Considerations\n+=======================\n+\n+TODO\n+\n+- unfinished thoughts:\n+    - what is different in privacy protections for wallets between there being 1 provider versus k providers who do not share information? Or maybe a better framing: p providers are malicious and 1-p are not.\n+    - malicious providers can passively surveil, but what else can they do? Drop selected transactions?\n+    - All of my thinking so far has assumed fully shielded transactions. What about partially shielded transactions?\n+\n+Economic Considerations\n+=======================\n+\n+TODO: refine/harden/analyze these:\n+\n+- We call the total number of transactions routed through TTRN in a given time period times the relay fee is `aggregate revenue`.\n+- Since active providers are chosen uniformly randomly, the `expected revenue per provider` (aka `ERPP`) in the time period is `aggregate revenue` / `number of registrations`.\n+- Over time, given stable conditions like a stable transaction rate, we expect the number of providers will increase or decrease such that `ERPP` + `non-financial incentive` + `out-of-band financial incentives` = `Total Cost for Service` + `epsilon` for providers.\n+    -  We include `non-financial incentive` to capture the notion that some providers may have other incentives to overcome excessive costs, such as the altruism of improving privacy for users or to execute an attack on privacy. However, we posit that over long enough time frames the non-financial incentives will tend towards 0 or else the providers become bankrupt and cannot continue.\n+    - We include `out-of-band financial incentives` to capture cases where providers earn other revenue in connection to their service that isn't in the scope of this protocol. For example, as long as a charity pays providers for their service separately from txn fees, this can tip the balance financially for those providers.\n+- The system is economically sustainable when:\n+    - for a given transaction, the `relay fee` is worth less than the additional privacy for a user, and\n+    - that implies that the number of independent providers is large enough to meet the user's privacy threshold, and\n+    - the aggregate rate of such transactions is large enough to fund enough independent providers.\n+- If any of those three do not hold, the system is not sustainable:\n+    - If the additional privacy isn't worth at least `relay fee` for enough transactions, possibly because:\n+    - there aren't enough independent providers to \n+\n+Deployment\n+==========\n+\n+TODO\n+\n+\n+Reference Implementation\n+========================\n+\n+TODO\n+\n+\n+References\n+==========\n+\n+TODO"},{"filename":"zip-wishing-well.rst","status":"added","additions":123,"deletions":0,"patch":"@@ -0,0 +1,123 @@\n+::\n+\n+  ZIP: ??? <Wishing Well>\n+  Title: The Wishing Well Protocol\n+  Owners: Nathan Wilcox <nathan@electriccoin.co>\n+  Credits: \n+  Status: Proposed\n+  Category: Application Standard\n+  Created: 2020-08-10\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"SHOULD\", and \"OPTIONAL\" in this document are to be interpreted\n+as described in RFC 2119. [#RFC2119]_\n+\n+The term \"network upgrade\" in this document is to be interpreted as described in ZIP 200\n+[#zip-0200]_.\n+\n+The term \"Sapling\" in this document is to be interpreted as described in ZIP 205\n+[#zip-0205]_.\n+\n+The term \"Sprout value pool balance\" in this document is to be interpreted as described\n+in ZIP 209 [#zip-0209]_.\n+\n+\n+Abstract\n+========\n+\n+A `Wishing Well` is a Zcash Sapling Z-Address for which no Spending Key exists. The Viewing Key is derived from a `Wishing Well Seed` by the `Wishing Well Derivation Scheme` introduced here. This ensures there is no corresponding `Spending Key`. Participants with the Viewing Key can thus be certain that any ZEC sent to that address is unrecoverable by anyone, which enables certain use cases.\n+\n+Motivation\n+==========\n+\n+The technique of \"burning\" crypto assets has wide applicability across the cryptocurrency design space for both base protocols as well as applications built atop existing base consensus protocols. While base protocols may rely on burning to achieve broad systemic properties of the supply, the use in higher level applications is typically to ensure participants incur some cost while also preventing any party from accruing the associated funds. This often helps ensure fairness while leveraging the crypto asset's scarcity within an application.\n+\n+For example, a broadcast system may require funds to be burnt in order to prevent spam, while also ensuring the funds don't accrue to a centralized party who may unduly influence the broadcast system.\n+\n+Specification\n+=============\n+\n+Concepts and Terminology\n+------------------------\n+\n+The protocol is used to create and interact with `Wishing Well` instances. Each instance has four associated components: a `Well Seed`, a `Sapling incoming viewing key` (aka `SIVK`), a `Sapling address`, and all associated `blockchain transactions` which send ZEC irretrievably to the given address along with an optional `encrypted memo`.\n+\n+Wishing-Well-aware tools can `generate` and `verify` well seeds. A Wishing-Well-aware tool can `derive` a Well's SIVK from a seed.\n+\n+Applications and/or their users manage the creation of instances, the distribution of the SIVK to a specific set of participants, the transfer of ZEC and memos to the address, and detecting/responding to other participants doing the same.\n+\n+Note: a SIVK or Sapling address that is derived from a Sapling Spending Key (or any intermediate prerequisite) is distinguished in this document as a `typical SIVK` or `typical address`.\n+\n+Design Goals\n+------------\n+\n+#. Enable applications wherein a group may privately broadcast memos and provably burn funds.\n+#. Work with transparent backwards compatibility of existing Zcash Sapling protocol and tools.\n+#. Preserve the `Security Properties` below.\n+\n+Security Properties\n+-------------------\n+\n+#. Wishing-Well-aware applications or users can `verify` a seed to reasonably convince themselves that the ZEC sent into the Well is not recoverable. By implication: it is not possible for anyone to create any typical SIVK prerequisite and also a Well seed for the same SIVK [#].\n+#. Any Sapling-enabled wallets or other products can interact with an instance's SIVK and/or address with no awareness of, or specialized features for, the Wishing Well protocol.\n+#. It is not possible, without knowledge of either a Well seed or one typical Sapling address prerequisites, to determine whether or not a Sapling address is a Well address, a typical address, or perhaps derived from some other protocol.\n+#. It is not possible for a sender to a Well instance to determine whether or not their transfer to a Sapling address is into a Well instance versus a typical address or other case without knowledge of the Well seed.\n+#. All privacy properties of Sapling hold against all adversary models and capabilities that apply to typical Sapling usage.\n+#. It is not possible for an adversary observing the blockchain to distinguish Wishing Well transactions from other Sapling transactions. Caveats:\n+  - Different applications may have different usage patterns, especially timing, which may leave a signature on-chain. If a particular application which relies on the Wishing Well Protocol has a unique detectable usage pattern, this does not impact the on-chain privacy of different applications which rely on the Wishing Well Protocol.\n+#. It is not possible for a privacy adversary with knowledge of the Viewing Key to distinguish if that Viewing Key and associated Address is a Well instance versus a typical Sapling address. Caveats:\n+  - An adversary who can observe the lack of outgoing transactions may deduce that the associated address is a well instance.\n+\n+.. [#] This follows from collision resistance of `BLAKE2s-256`. See [FIXME protocol reference].\n+\n+Specification\n+=============\n+\n+Well Seed Generation\n+--------------------\n+\n+A `Well Seed` is an arbitrary application specific value. However, application designers should be aware of the security properties of their choice. For many applications, a seed should be generated as a 256-bit-entropy value. This ensures the seed is unguessable and will not collide with any other application instances.\n+\n+An application may want a \"globally public\" Wishing Well, in which case it may use a well-known constant value.\n+\n+In particular it is recommended against allowing users to select bare seeds.\n+\n+Wishing Well Derivation Scheme\n+------------------------------\n+\n+A Sapling incoming viewing key is derived from a `Well Seed` by using `blake2b` with the personalization ASCII string ``zcash-wishing-well-derivation-v1-u8eQd6IG7wsxlr+PC2UFa1GxttqAA159m+tdV3Tl3XI=``. [#]\n+\n+.. [#] The suffix is random 32 bytes from the author's system encoded as base64, designed to reduce the possibility of an unrelated protocol or application using the same personalization string. While the source randomness is not verifiable, we posit this has no tangible impact.\n+\n+Rationale\n+=========\n+\n+TODO\n+\n+\n+Security and Privacy Considerations\n+===================================\n+\n+TODO\n+\n+\n+Deployment\n+==========\n+\n+TODO\n+\n+\n+Reference Implementation\n+========================\n+\n+TODO\n+\n+\n+References\n+==========\n+\n+TODO"}],"filesTruncated":false,"preview":null},{"number":437,"title":"[ZIP 314] Privacy upgrades to the Zcash light client protocol","author":"defuse","createdAt":"2021-01-11T22:25:35Z","updatedAt":"2022-04-18T21:26:01Z","url":"https://github.com/zcash/zips/pull/437","diffUrl":"https://github.com/zcash/zips/pull/437.diff","headRef":"zip-0314","headRepo":"defuse/zips","headSha":"276b6e54ba0f45bee7a354760f63969e87390d3d","additions":0,"deletions":0,"changedFiles":0,"bodyExcerpt":"","zipRefs":[314],"draftRefs":[],"mapped":true,"claimed":false,"files":[{"filename":"zip-0314.rst","status":"added","additions":41,"deletions":0,"patch":"@@ -0,0 +1,41 @@\n+::\n+\n+  ZIP: 314\n+  Title: Privacy upgrades to the Zcash light client protocol\n+  Owners: Taylor Hornby <taylor@electriccoin.co>\n+  Category: Standards / Ecosystem\n+  Status: Draft\n+  Created: 2020-01-11\n+  License: MIT\n+\n+\n+Terminology\n+===========\n+\n+The key words \"MUST\", \"SHOULD\", and \"MAY\" in this document are to be interpreted as\n+described in RFC 2119. [#RFC2119]_\n+\n+The terms below are to be interpreted as follows:\n+\n+Light client\n+  A client that is not a full participant in the network of Zcash peers. It can send and\n+  receive payments, but does not store or validate a copy of the block chain.\n+\n+Proxy server\n+  lightwalletd TODO define it properly\n+\n+Abstract\n+========\n+\n+This proposal suggests changes to the Zcash light client protocol to acheive\n+improved privacy properties, at the cost of increased bandwidth usage.\n+\n+Motivation\n+==========\n+\n+Privacy!\n+\n+References\n+==========\n+\n+.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_"}],"filesTruncated":false,"preview":{"filename":"zip-0314.rst","title":"Privacy upgrades to the Zcash light client protocol","status":"Draft","headings":[{"level":1,"id":"terminology","text":"Terminology"},{"level":1,"id":"abstract","text":"Abstract"},{"level":1,"id":"motivation","text":"Motivation"},{"level":1,"id":"references","text":"References"}],"format":"rst"}}]}