diff --git a/Makefile b/Makefile index 748426ef..76d0e8d6 100644 --- a/Makefile +++ b/Makefile @@ -37,17 +37,18 @@ discard: rm -r rendered git checkout -- 'README.rst' -.Makefile.uptodate: Makefile render.sh +.Makefile.uptodate: Makefile render.sh expand_macros.py $(MAKE) clean + ./expand_macros.py --test touch .Makefile.uptodate -rendered/index.html: README.rst render.sh +rendered/index.html: README.rst render.sh expand_macros.py ./render.sh --rst $< $@ -rendered/%.html: zips/%.rst render.sh +rendered/%.html: zips/%.rst render.sh expand_macros.py ./render.sh --rst $< $@ -rendered/%.html: zips/%.md render.sh +rendered/%.html: zips/%.md render.sh expand_macros.py ./render.sh $(MARKDOWN_OPTION) $< $@ 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) diff --git a/expand_macros.py b/expand_macros.py new file mode 100755 index 00000000..7509b0b3 --- /dev/null +++ b/expand_macros.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python3 + +import sys +import re +from io import StringIO +from pathlib import Path +from collections import namedtuple, deque + + +def main(args): + if args[1:] == ["--test"]: + return self_test() + + if len(args) != 3: + print("Usage: ./expand_macros.py \n" + "\n" + "Expand using the macros defined in , writing the\n" + "output on stdout. If is \"-\", read from stdin.\n" + "\n" + "The contents of should be a list of file inclusions and\n" + "macro definitions, one per line. Comments (from the rightmost '%' to the\n" + "end of the line), whitespace, and blank lines will be ignored.\n" + "\n" + "A file inclusion has the form\n" + "\n" + "\\includeonly{filename}\n" + "\n" + "and causes another macro file 'filename.tex' in the same directory as\n" + " to be included.\n" + "\n" + "A macro definition has the form\n" + "\n" + "\\newcommand{\\macroname}[n]{replacement}\n" + "\n" + "where n is the number of arguments. If n is zero then the '[0]' can be\n" + "omitted. The replacement text may include '#i' to substitute the i'th\n" + "argument, counting from 1.\n" + "\n" + "For example, given a macro\n" + "\n" + "\\newcommand{\\journey}[3]{from {#1} to {#3} via {#2}}\n" + "\n" + "the invocation '\\journey{Manchester}{Crewe}{London}' will expand to\n" + "'from {Manchester} to {London} via {Crewe}'. (It is usual, but not\n" + "required, to use '{}' around subsitutions to compensate for lack of\n" + "syntactic hygiene in LaTeX.)\n" + "\n" + "Note that this intentionally covers only the most commonly used subset\n" + "of LaTeX macro definition syntax.\n" + "\n" + "A very simple approximation of LaTeX's tokenization is used in order to\n" + "ensure that substitutions are not applied incorrectly in the case of\n" + "no-argument macros. Specifically, a macro named '\\foo' will match only\n" + "instances of the string '\\foo' followed by a non-letter or the end of\n" + "a line, i.e. it will match on '\\foo0' (giving the replacement text\n" + "followed by '0'), but not on '\\foonly'. As in LaTeX, a space after a\n" + "no-argument macro is consumed, i.e. '\\foo ' behaves like '\\foo'.\n" + "\n" + "Unlike LaTeX, macros with arguments will only match if each argument is\n" + "surrounded by '{}'. Nesting of '{}' within macro arguments works, i.e.\n" + "in the example above, '\\journey{{a}}{b}{c}' will expand to\n" + "'from {{a}} to {c} via {b}'. The '{' and '}' characters can be escaped\n" + "as '\\{' and '\\}'.\n" + "\n" + "A token effort (pun intended) is made to ensure that macro expansion\n" + "does not lead to tokenization inconsistent with LaTeX: if a replacement\n" + "starts with a letter that would continue a control word, then a space\n" + "is inserted before the replacement so that the tokenization of the\n" + "preceding control word is preserved. (In combination with the stricter\n" + "syntax for macro arguments, this means that certain patterns in which a\n" + "macro expansion provides arguments to another macro are not supported.)\n" + "Similarly, if a replacement ends with a control word that would be\n" + "extended by the following text, then a space is inserted after the\n" + "replacement to prevent that.\n" + "\n" + "Doubling a '\\' character can be used to escape it, i.e. an even number\n" + "of '\\' characters will be left as-is and will not be considered as the\n" + "start of a potential macro invocation. To write a literal '%' character\n" + "in a macro definition, include a comment after the definition.\n" + "\n" + "Unlike LaTeX, macro arguments cannot span lines; the expansion of each\n" + "line is independent.\n" + "\n" + "Exit code: 0 on success, 1 on failure.\n" + "\n" + "To run a self-test, use './expand_macros.py --test'. This follows the\n" + "same exit code convention.\n") + return 1 + + macroset = parse_macros_from_file(Path(args[1])) + + targetfile = args[2] + if targetfile == "-": + expand_stream(macroset, sys.stdin, "") + else: + with open(targetfile, "r", encoding="utf-8") as targetstream: + expand_stream(macroset, targetstream, targetfile) + + + +class MacroDefinitionError(Exception): + pass + +class MacroSubsitutionError(Exception): + pass + +Macro = namedtuple("Macro", "argcount replacement") +MacroSet = namedtuple("MacroSet", "mapping regex") + +def make_macroset(macros): + regexes = deque() + for name, macro in macros.items(): + assert re.escape(name) == name + if macro.argcount == 0: + regexes.append(r"(?:(?:^|[^\\])(\\\\)*\\" + name + r" ?)") + else: + regexes.append(r"(?:(?:^|[^\\])(\\\\)*\\" + name + r"\{)") + + return MacroSet(macros, re.compile("|".join(regexes))) + +def parse_macros_from_file(filepath): + filepath = filepath.resolve() + basepath = filepath.parent + macros = {} + inclusions = set() + + def opener(includename, location): + if includename in inclusions: + raise MacroDefinitionError(f"Re-inclusion of '{includename}' at {location}") + inclusions.add(includename) + + with open(basepath / includename, "r", encoding="utf-8") as macrostream: + parse_macros_into(macros, opener, macrostream, includename) + + opener(filepath.name, "") + return make_macroset(macros) + +def parse_macros_from_string(s, name): + macros = {} + def opener(includename, location): + raise MacroDefinitionError("Cannot include {includename} from a string at {location}") + + parse_macros_into(macros, opener, StringIO(s), name) + return make_macroset(macros) + + +# The filename pattern cannot access other directories. +INCLUDEONLY = re.compile(r"\\includeonly\{([a-zA-Z][-.a-zA-Z]*)\}") + +NEWCOMMAND_START = re.compile(r"\\newcommand\{\\([a-zA-Z]+)\}(?:\[([0-9])\])?") + +def parse_macros_into(macros, opener, macrostream, filename): + for n, line in enumerate(macrostream): + line = line.partition('%')[0].strip(' \t\n') + if line == "": continue + + include = INCLUDEONLY.match(line) + if include: + opener(include.group(1) + ".tex", f"{filename} line {n+1}") + continue + + start = NEWCOMMAND_START.match(line) + if not start: + raise MacroDefinitionError(f"Non-macro found\n At {filename} line {n+1}: <{line}>") + + name = start.group(1) + argcount = int(start.group(2) or 0) + (replacement, rest) = munch(line[start.end():]) + if replacement is None or rest != "": + raise MacroDefinitionError(f"Incomplete macro definition\n At {filename} line {n+1}: <{line}>\n Still to parse: <{rest}>") + + macros[name] = Macro(argcount, replacement) + +def munch(s): + """Parse a balanced {} group. Return (group, rest) if found, or (None, s) otherwise.""" + if s[0] != '{': + return (None, s) + + depth = 0 + unescaped = 1 + for i in range(len(s)): + c = s[i] + match c: + case '{' : depth += unescaped + case '}' : depth -= unescaped + + unescaped = (1 - unescaped) if c == '\\' else 1 + + if depth == 0: + return (s[1:i], s[i+1:]) + + return (None, s) + +def expand_stream(macroset, targetstream, filename): + for n, line in enumerate(targetstream): + print(expand_line(macroset, line.rstrip("\n"), f"{filename} line {n+1}")) + +# 0-based +SUBST = [re.compile(r"(?:^|[^\\])(\\\\)*#" + str(i)) for i in range(1, 10)] + +ENDS_IN_CONTROL_WORD = re.compile(r"(?:^|[^\\])(\\\\)*\\[a-zA-Z]+$") +ASCII_LETTER = re.compile(r"[a-zA-Z]") + +def would_extend_control_word(before, after): + return (ASCII_LETTER.match(after) is not None) and (ENDS_IN_CONTROL_WORD.search(before) is not None) + +def expand_line(macroset, line, location): + result = "" + rest = line + runaway = 0 + while found := macroset.regex.search(rest): + name = found.group().rstrip('{') + end = found.start() + len(name) + name = name.rpartition('\\')[2] + assert name is not None, f"name: {name}, rest: <{rest}>, found: {found}" + start = end - len(name) + assert start > 0, f"name: {name}, rest: <{rest}>, found: {found}" + name = name.rstrip(' ') + macro = macroset.mapping.get(name) + assert macro is not None, f"name: {name}, rest: <{rest}>, found: {found}, macroset: {macroset}" + replacement = macro.replacement + + # Append the target string up to the macro invocation including any escaped '\'s. + result += rest[:start-1] + rest = rest[end:] + + for subst in SUBST[:macro.argcount]: + (arg, rest) = munch(rest) + if arg is None: + raise MacroSubsitutionError(f"Too few arguments to macro '\\{name}'\n At {location}: <{line}>\n Still to parse: <{rest}>") + replacement = subst.sub(lambda m: m.group()[:-2] + arg, replacement) + + # If a replacement starts with a letter that would continue a control word, then a + # space is inserted before the replacement so that the tokenization of the preceding + # control word is preserved. + if would_extend_control_word(result, replacement + rest): + result += ' ' + + # If a replacement ends with a control word that would be extended by the following + # text, then a space is inserted after the replacement to prevent that. + if would_extend_control_word(result + replacement, rest): + replacement += ' ' + + rest = replacement + rest + runaway += 1 + if runaway == 100: + raise MacroSubsitutionError(f"Runaway macro expansion\n At {location}: <{line}>\n Result: <{result}>\n Still to parse: <{rest}>") + + return result + rest + +def self_test(): + successes = 0 + failures = 0 + for testname, (macrostr, expect_mapping, cases) in TESTS.items(): + try: + macroset = parse_macros_from_string(macrostr, f"test {testname}") + assert macroset.mapping == expect_mapping, f"{testname} definitions\n actual: {macroset.mapping}\n expected: {expect_mapping}" + for i, (target, expect_replacement) in enumerate(cases): + location = f"test {testname} case {i+1}" + try: + replacement = expand_line(macroset, target, location) + except MacroSubsitutionError as e: + replacement = None + if expect_replacement is not None: raise + assert replacement == expect_replacement, f"{location}\n actual: {replacement}\n expected: {expect_replacement}" + + successes += 1 + except (MacroDefinitionError, AssertionError) as e: + print(f"Failed testcase: {testname}\n {e.__class__.__name__}: {e}") + failures += 1 + + if failures > 0: + print(f"\nTests failed: {failures} failures, {successes} successes.\n") + return 1 + else: + print(f"Tests passed: no failures, {successes} successes.\n") + return 0 + + +TESTS = { + "journey": ("\\newcommand{\\journey}[3]{from {#1} to {#3} via {#2}}", + {'journey': Macro(3, 'from {#1} to {#3} via {#2}')}, [ + ("\\journey{Manchester}{Crewe}{London}", "from {Manchester} to {London} via {Crewe}"), + ("\\journey{{a}}{b}{c}", "from {{a}} to {c} via {b}"), + ("\\journey{{a}}{\\b}{\\\\c}", "from {{a}} to {\\\\c} via {\\b}"), + ("\\journey{{a}}{\\}\\{b}{c}", "from {{a}} to {c} via {\\}\\{b}"), + ("\\journey{{a}}{\\}\\\\\\{b}{\\\\c}", "from {{a}} to {\\\\c} via {\\}\\\\\\{b}"), + ("\\journey{{a}{b}{c}", None), + ]), + "noarg": (" \\newcommand{\\foo}{bar}", + {'foo': Macro(0, 'bar')}, [ + ("rhu\\foo b", "rhubarb"), + ("rhu\\foo{}b", "rhubar{}b"), + ]), + "unclosedarg": ("\t\\newcommand{\\foo}[1]{bar}", + {'foo': Macro(1, 'bar')}, [ + ("rhu\\foo{", None), + ]), + "unusedarg": ("\\newcommand{\\foo}[1]{bar} ", + {'foo': Macro(1, 'bar')}, [ + ("rhu\\foo{}b", "rhubarb"), + ]), + "recursive": ("\\newcommand{\\journey}[3]{from {#1} to {#3} via {#2}}\t\n\\newcommand{\\foo}{bar}", + {'journey': Macro(3, 'from {#1} to {#3} via {#2}'), 'foo': Macro(0, 'bar')}, [ + ("\\journey{cabbage}{rhu\\foo b}{cauliflower}", "from {cabbage} to {cauliflower} via {rhubarb}"), + ]), + "comment": ("% abc\n\n \t\\newcommand{\\foo}{bar}\t % def", + {'foo': Macro(0, 'bar')}, [ + ("rhu\\foo b", "rhubarb"), + ]), + "noextbefore": ("\\newcommand{\\foo}{o}", + {'foo': Macro(0, 'o')}, [ + ("\\fo\\foo", "\\fo o"), + ]), + "noextafter": ("\\newcommand{\\foo}{\\bar}", + {'foo': Macro(0, '\\bar')}, [ + ("rhu\\foo b", "rhu\\bar b"), + ("rhu\\\\foo b", "rhu\\\\foo b"), + ("rhu\\\\\\foo b", "rhu\\\\\\bar b"), + ]), + "escape": ("\\newcommand{\\foo}{bar}", + {'foo': Macro(0, 'bar')}, [ + ("rhu\\\\\\foo b", "rhu\\\\barb"), + ]), + "runaway": ("\\newcommand{\\foo}{\\foo}", + {'foo': Macro(0, '\\foo')}, [ + ("\\foo", None), + ]), +} + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/render.sh b/render.sh index 2fee6649..80e23509 100755 --- a/render.sh +++ b/render.sh @@ -7,7 +7,7 @@ set -euo pipefail if ! ( ( [ "x$1" = "x--rst" ] || [ "x$1" = "x--pandoc" ] || [ "x$1" = "x--mmd" ] ) && [ $# -eq 3 ] ); then cat - < +Usage: render.sh --rst|--pandoc|--mmd <inputfile> <htmlfile> --rst render reStructuredText using rst2html5 --pandoc render Markdown using pandoc @@ -29,7 +29,8 @@ if [ "x$1" = "x--rst" ]; then else filetype='.md' fi -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||')" +basefile="$(basename -s ${filetype} ${inputfile})" +title="$(echo ${basefile} | sed -E 's|zip-0{0,3}|ZIP |; s|draft-|Draft |')$(grep -E '^(\.\.)?\s*Title: ' ${inputfile} |sed -E 's|.*Title||')" echo " ${title}" Math1='<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.25/dist/katex.min.css" integrity="sha384-WcoG4HRXMzYzfCgiyfrySxx90XSl2rxY5mnVY5TwtWE6KLrArNKn0T/mOgNL0Mmi" crossorigin="anonymous">' @@ -40,13 +41,19 @@ Mermaid='<script defer src="https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/me ViewAndStyle='<meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="css/style.css">' +macrofile="$(dirname ${inputfile})/${basefile}.macros.tex" + cat <( if [ "x$1" = "x--rst" ]; then # These are basic regexps so \+ is needed, not +. # We use the Unicode 💲 character to move an escaped $ out of the way, # which is much easier than trying to handle escapes within a capture. - cat "${inputfile}" \ + (if [ -f "${macrofile}" ]; then + ./expand_macros.py "${macrofile}" "${inputfile}" + else + cat "${inputfile}" + fi) \ | sed 's|[\][$]|💲|g; s|[$]\([^$]\+\)[$]\([.,:;!?)-]\)|:math:`\1\\!`\2|g; s|[$]\([^$]\+\)[$]|:math:`\1`|g; @@ -55,12 +62,21 @@ cat <( | sed "s|<script src=\"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\"></script>|${Math1}\n ${Math2}\n ${Math3}|; s|</head>|${ViewAndStyle}</head>|" else + delete_expandedfile=0 + if [ -f "${macrofile}" ]; then + expandedfile="$(dirname ${inputfile})/${basefile}.expanded${filetype})" + ./expand_macros.py "${macrofile}" "${inputfile}" >"${expandedfile}" + delete_expandedfile=1 + else + expandedfile="${inputfile}" + fi if [ "x$1" = "x--pandoc" ]; then # Not actually MathJax. KaTeX is compatible if we use the right headers. - pandoc --mathjax --from=markdown --to=html "${inputfile}" --output="${outputfile}.temp" + pandoc --mathjax --from=markdown --to=html "${expandedfile}" --output="${outputfile}.temp" else - multimarkdown ${inputfile} -o "${outputfile}.temp" + multimarkdown ${expandedfile} -o "${outputfile}.temp" fi + if [ ${delete_expandedfile} -eq 1 ]; then rm -f "${expandedfile}"; fi # Both pandoc and multimarkdown just output the HTML body. echo "<!DOCTYPE html>" diff --git a/zips/common-macros.tex b/zips/common-macros.tex new file mode 100644 index 00000000..463966c5 --- /dev/null +++ b/zips/common-macros.tex @@ -0,0 +1,49 @@ +% Common macros + +% To use macros from this file, write `\includeonly{common-macros}` in the +% `zip-xxxx.macros.tex` file. + +\newcommand{\bytes}[1]{\underline{#1}} +\newcommand{\zeros}[1]{[0]^{#1}} +\newcommand{\zerobytes}[1]{[\hexint{00}]^{#1}} +\newcommand{\ones}[1]{[1]^{#1}} +\newcommand{\bit}{\mathbb{B}} +\newcommand{\byte}{\mathbb{B}^{\kern-0.1em\tiny\mathbb{Y}}} +\newcommand{\Nat}{\mathbb{N}} +\newcommand{\PosInt}{\mathbb{N}^+} +\newcommand{\Int}{\mathbb{Z}} +\newcommand{\Rat}{\mathbb{Q}} +\newcommand{\GF}[1]{\mathbb{F}_{\!#1}} +\newcommand{\GFstar}[1]{\mathbb{F}^\ast_{#1}} +\newcommand{\typeexp}[2]{{#1}\vphantom{)}^{[{#2}]}} +\newcommand{\bitseq}[1]{\typeexp{\bit}{#1}} +\newcommand{\bitseqs}{\bitseq{\Nat}} +\newcommand{\byteseq}[1]{\mathbb{B}^{{\kern-0.1em\tiny\mathbb{Y}}[{#1}]}} +\newcommand{\byteseqs}{\byteseq{\Nat}} +\newcommand{\concatbits}{\mathsf{concat}_\bit} +\newcommand{\bconcat}{\,||\,} +\newcommand{\listcomp}[1]{[~{#1}~]} +\newcommand{\biglistcomp}[1]{\bigg[{#1}\bigg]} +\newcommand{\fun}[2]{{#1} \mapsto {#2}} +\newcommand{\exclusivefun}[3]{{#1} \mapsto_{\not\in\kern 0.05em{#3}} {#2}} + +\newcommand{\squash}{\!\!\!} +\newcommand{\caseif}{\squash\text{if }} +\newcommand{\caseotherwise}{\squash\text{otherwise}} + +\newcommand{\length}{\mathsf{length}} +\newcommand{\truncate}[1]{\mathsf{truncate}_{#1}} +\newcommand{\mean}{\mathsf{mean}} +\newcommand{\median}{\mathsf{median}} + +\newcommand{\hexint}[1]{\mathtt{0x#1}} +\newcommand{\ascii}[1]{\textbf{``\texttt{#1}''}} +\newcommand{\setof}[1]{\{{#1}\}} +\newcommand{\bigsetof}[1]{\big\{{#1}\big\}} +\newcommand{\powerset}[1]{\mathscr{P}\big({#1}\big)} +\newcommand{\barerange}[2]{{{#1}\,..\,{#2}}} +\newcommand{\range}[2]{\setof{\barerange{#1}{#2}}} +\newcommand{\bigrange}[2]{\bigsetof{\barerange{#1}{#2}}} +\newcommand{\rangenozero}[2]{\range{#1}{#2} \setminus \setof{0}} +\newcommand{\bigrangenozero}[2]{\bigrange{#1}{#2} \setminus \setof{0}} +\newcommand{\binaryrange}[1]{\range{0}{2^{#1}-1}} diff --git a/zips/draft-ecc-quantum-recoverability.macros.tex b/zips/draft-ecc-quantum-recoverability.macros.tex new file mode 100644 index 00000000..ec6d9165 --- /dev/null +++ b/zips/draft-ecc-quantum-recoverability.macros.tex @@ -0,0 +1 @@ +\includeonly{common-macros} diff --git a/zips/draft-ecc-quantum-recoverability.md b/zips/draft-ecc-quantum-recoverability.md index 2a494b88..4a252938 100644 --- a/zips/draft-ecc-quantum-recoverability.md +++ b/zips/draft-ecc-quantum-recoverability.md @@ -163,7 +163,7 @@ This subsection and the flow diagram below are non-normative. In order to support ZSAs [^zip-0226] [^zip-0227] and memo bundles [^zip-0231], v6 transactions require in any case a new note plaintext -format, with lead byte $\mathtt{0x03}.$ This gives us an opportunity +format, with lead byte $\hexint{03}.$ This gives us an opportunity to change the way that the $\mathsf{pre\_rcm}$ value is computed for this new format, by including all of the note fields in $\mathsf{pre\_rcm}$. The resulting $\mathsf{rcm}$ is essentially a random function of the @@ -450,10 +450,10 @@ in the Orchard key components diagram, with the following note: Replace the paragraph > The field $\mathsf{leadByte}$ indicates the version of the encoding of -> a Sapling or Orchard note plaintext. For Sapling it is $\mathtt{0x01}$ -> before activation of the Canopy network upgrade and $\mathtt{0x02}$ +> a Sapling or Orchard note plaintext. For Sapling it is $\hexint{01}$ +> before activation of the Canopy network upgrade and $\hexint{02}$ > afterward, as specified in [ZIP-212]. For Orchard note plaintexts it is -> always $\mathtt{0x02}$. +> always $\hexint{02}$. with @@ -472,10 +472,10 @@ with > > Define $\mathsf{allowedLeadBytes^{protocol}}(\mathsf{height}, \mathsf{txVersion}) =$ > $\hspace{2em} \begin{cases} -> \{ \mathtt{0x01} \},&\!\!\!\text{if } \mathsf{height} < \mathsf{CanopyActivationHeight} \\ -> \{ \mathtt{0x01}, \mathtt{0x02} \},&\!\!\!\text{if } \mathsf{CanopyActivationHeight} \leq \mathsf{height} < \mathsf{CanopyActivationHeight} + \mathsf{ZIP212GracePeriod} \\ -> \{ \mathtt{0x02} \},&\!\!\!\text{if } \mathsf{CanopyActivationHeight} + \mathsf{ZIP212GracePeriod} \leq \mathsf{height} \text{ and } \mathsf{txVersion} < 6 \\ -> \{ \mathtt{0x03} \},&\!\!\!\text{otherwise.} +> \setof{\hexint{01}},&\caseif \mathsf{height} < \mathsf{CanopyActivationHeight} \\ +> \setof{\hexint{01}, \hexint{02}},&\caseif \mathsf{CanopyActivationHeight} \leq \mathsf{height} < \mathsf{CanopyActivationHeight} + \mathsf{ZIP212GracePeriod} \\ +> \setof{\hexint{02}},&\caseif \mathsf{CanopyActivationHeight} + \mathsf{ZIP212GracePeriod} \leq \mathsf{height} \text{ and } \mathsf{txVersion} < 6 \\ +> \setof{\hexint{03}},&\!\!\!\text{otherwise.} > \end{cases}$ > > The $\mathsf{leadByte}$ of a Sapling or Orchard note MUST satisfy @@ -497,7 +497,7 @@ In the list of places where $\mathsf{PRF^{expand}}$ is used: Replace > * [**NU5** onward] in § 4.2.3 ‘Orchard Key Components’, with inputs -> $[6]$, $[7]$, $[8]$, and with first byte $\mathtt{0x82}$ (the last of +> $[6]$, $[7]$, $[8]$, and with first byte $\hexint{82}$ (the last of > these is also specified in [[ZIP-32]](https://zips.z.cash/zip-0032)); > * in the processes of sending (§ 4.7.2 ‘Sending Notes (Sapling)’ and > § 4.7.3 ‘Sending Notes (Orchard)’) and of receiving @@ -508,23 +508,23 @@ Replace with > * [**NU5** onward] in § 4.2.3 ‘Orchard Key Components’, with inputs -> $[\mathtt{0x06}]$, $[\mathtt{0x07}]$, $[\mathtt{0x08}]$, with first -> byte in $\{ \mathtt{0x0C}, \mathtt{0x0D} \}$ (also specified in -> {{ reference to this ZIP }}), and with first byte $\mathtt{0x82}$ +> $[\hexint{06}]$, $[\hexint{07}]$, $[\hexint{08}]$, with first +> byte in $\setof{\hexint{0C}, \hexint{0D}}$ (also specified in +> {{ reference to this ZIP }}), and with first byte $\hexint{82}$ > (also specified in [[ZIP-32]](https://zips.z.cash/zip-0032)); > * in the processes of sending (§ 4.7.2 ‘Sending Notes (Sapling)’ and > § 4.7.3 ‘Sending Notes (Orchard)’) and of receiving > (§ 4.20 ‘In-band secret distribution (Sapling and Orchard)’) notes, -> for Sapling with inputs $[\mathtt{0x04}]$ and $[\mathtt{0x05}]$, +> for Sapling with inputs $[\hexint{04}]$ and $[\hexint{05}]$, > and for Orchard with first byte in -> $\{ \mathtt{0x05}, \mathtt{0x04}, \mathtt{0x09}, \mathtt{0x0A}, \mathtt{0x0B} \}$ -> ($\mathtt{0x0A}$ and $\mathtt{0x0B}$ are also specified in +> $\setof{\hexint{05}, \hexint{04}, \hexint{09}, \hexint{0A}, \hexint{0B}}$ +> ($\hexint{0A}$ and $\hexint{0B}$ are also specified in > {{ reference to this ZIP }}); Add > * in {{ reference to this ZIP }}, with first byte in -> $\{ \mathtt{0x0A}, \mathtt{0x0B}, \mathtt{0x0C}, \mathtt{0x0D} \}$. +> $\setof{\hexint{0A}, \hexint{0B}, \hexint{0C}, \hexint{0D}}$. Also change the remaining decimal constants to hex for consistency. @@ -535,7 +535,7 @@ from § 5.3 ‘Constants’. Insert after the definition of $\mathsf{ToScalar^{Orchard}}$: -> 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})$ +> 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})$ > $\hspace{23.9em} ||\, \mathsf{I2LEOSP}_{256}(\mathsf{nk})))$. Replace from "From this spending key" up to and including the line @@ -567,10 +567,10 @@ in the algorithm with: > needs to be set to false.) > > Define: -> * $\mathsf{H^{ask}}(\mathsf{sk}) = \mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{sk}}([\mathtt{0x06}])\kern-0.1em\big)$ -> * $\mathsf{H^{nk}}(\mathsf{sk}) = \mathsf{ToBase^{Orchard}}\big(\mathsf{PRF^{expand}_{sk}}([\mathtt{0x07}])\kern-0.1em\big)$ -> * $\mathsf{H^{rivk}}(\mathsf{sk}) = \mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{sk}}([\mathtt{0x08}])\kern-0.1em\big)$ -> * $\mathsf{H^{qsk}}(\mathsf{sk}) = \mathsf{truncate}_{32}\big(\mathsf{PRF^{expand}_{sk}}([\mathtt{0x0C}])\kern-0.1em\big)$ +> * $\mathsf{H^{ask}}(\mathsf{sk}) = \mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{sk}}([\hexint{06}])\kern-0.1em\big)$ +> * $\mathsf{H^{nk}}(\mathsf{sk}) = \mathsf{ToBase^{Orchard}}\big(\mathsf{PRF^{expand}_{sk}}([\hexint{07}])\kern-0.1em\big)$ +> * $\mathsf{H^{rivk}}(\mathsf{sk}) = \mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{sk}}([\hexint{08}])\kern-0.1em\big)$ +> * $\mathsf{H^{qsk}}(\mathsf{sk}) = \mathsf{truncate}_{32}\big(\mathsf{PRF^{expand}_{sk}}([\hexint{0C}])\kern-0.1em\big)$ > * $\mathsf{H^{qk}}(\mathsf{qsk}) = \textsf{BLAKE2s\kern0.1em-256}(\texttt{“Zcash\_qk”}, \mathsf{qsk})$. > > $\mathsf{ask} \;{\small ⦂}\; \mathbb{F}^{*}_{r_{\mathbb{P}}}$, @@ -633,8 +633,8 @@ Replace > Let $\mathsf{CanopyActivationHeight}$ be as defined in § 5.3 ‘Constants’. > > Let $\mathsf{leadByte}$ be the note plaintext lead byte. -> This MUST be $\mathsf{0x01}$ if for the next block, -> $\mathsf{height} < \mathsf{CanopyActivationHeight}$, or $\mathtt{0x02}$ +> This MUST be $\hexint{01}$ if for the next block, +> $\mathsf{height} < \mathsf{CanopyActivationHeight}$, or $\hexint{02}$ > if $\mathsf{height} \geq \mathsf{CanopyActivationHeight}$. with @@ -643,9 +643,9 @@ with > according to § 3.2.1 ‘Note Plaintexts and Memo Fields’ with > $\mathsf{protocol} = \mathsf{Sapling}$. > -> Define $\mathsf{H^{rcm,Sapling}_{rseed}}(\_, \_) = \mathsf{ToScalar^{Sapling}}\big(\mathsf{PRF^{expand}_{rseed}}([\mathtt{0x04}])\kern-0.1em\big)$ +> Define $\mathsf{H^{rcm,Sapling}_{rseed}}(\_, \_) = \mathsf{ToScalar^{Sapling}}\big(\mathsf{PRF^{expand}_{rseed}}([\hexint{04}])\kern-0.1em\big)$ > -> Define $\mathsf{H^{esk,Sapling}_{rseed}}(\_, \_) = \mathsf{ToScalar^{Sapling}}\big(\mathsf{PRF^{expand}_{rseed}}([\mathtt{0x05}])\kern-0.1em\big)$. +> Define $\mathsf{H^{esk,Sapling}_{rseed}}(\_, \_) = \mathsf{ToScalar^{Sapling}}\big(\mathsf{PRF^{expand}_{rseed}}([\hexint{05}])\kern-0.1em\big)$. > > ($\mathsf{H^{rcm,Sapling}}$ and $\mathsf{H^{esk,Sapling}}$ intentionally > take arguments that are unused.) @@ -673,18 +673,18 @@ with > $\mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{rseed}}(\mathsf{pre\_rcm})\kern-0.1em\big)$ > > where $\mathsf{pre\_rcm} = \begin{cases} -> [\mathtt{0x05}] \,||\, \underline{\text{ρ}},&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x02} \\ -> [\mathtt{0x0B}, \mathsf{leadByte}] \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{g}\star_{\mathsf{d}}) \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{pk}\star_{\mathsf{d}}) \\ -> \hphantom{[\mathtt{0x0B}, \mathsf{leadByte}]} \,||\, \mathsf{I2LEOSP}_{64}(\mathsf{v}) \,||\, \underline{\text{ρ}} \,||\, \mathsf{I2LEOSP}_{256}(\text{ψ}) \\ -> \hphantom{[\mathtt{0x0B}, \mathsf{leadByte}]}\,[\,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{AssetBase}\kern0.08em\star)],&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x03} +> [\hexint{05}] \,||\, \underline{\text{ρ}},&\caseif \mathsf{leadByte} = \hexint{02} \\ +> [\hexint{0B}, \mathsf{leadByte}] \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{g}\star_{\mathsf{d}}) \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{pk}\star_{\mathsf{d}}) \\ +> \hphantom{[\hexint{0B}, \mathsf{leadByte}]} \,||\, \mathsf{I2LEOSP}_{64}(\mathsf{v}) \,||\, \underline{\text{ρ}} \,||\, \mathsf{I2LEOSP}_{256}(\text{ψ}) \\ +> \hphantom{[\hexint{0B}, \mathsf{leadByte}]}\,[\,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{AssetBase}\kern0.08em\star)],&\caseif \mathsf{leadByte} = \hexint{03} > \end{cases}$ > -> Define $\mathsf{H^{esk,Orchard}_{rseed}}(\underline{\text{ρ}}) = \mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{rseed}}([\mathtt{0x04}] \,||\, \underline{\text{ρ}})\kern-0.1em\big)$. +> Define $\mathsf{H^{esk,Orchard}_{rseed}}(\underline{\text{ρ}}) = \mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{rseed}}([\hexint{04}] \,||\, \underline{\text{ρ}})\kern-0.1em\big)$. > > 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)$. > where $\mathsf{split\_domain} = \begin{cases} -> \mathtt{0x09}&\text{if } \mathsf{split\_flag} = 0 \\ -> \mathtt{0x0A}&\text{if } \mathsf{split\_flag} = 1\text{.} +> \hexint{09}&\text{if } \mathsf{split\_flag} = 0 \\ +> \hexint{0A}&\text{if } \mathsf{split\_flag} = 1\text{.} > \end{cases}$ Insert before the derivation of $\mathsf{esk}$: @@ -805,11 +805,11 @@ with For § 4.20.2, replace > $\hspace{1.0em}$ [**Canopy** onward] let $\underline{\mathsf{rcm}} = \begin{cases} -> \mathsf{rseed},&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x01} \\ +> \mathsf{rseed},&\!\!\!\text{if } \mathsf{leadByte} = hexint{01} \\ > \mathsf{ToScalar^{protocol}}(\mathsf{PRF^{expand}_{rseed}}(\mathsf{pre\_rcm})),&\!\!\!\text{otherwise} > \end{cases}$ <br> > $\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> -> $\hspace{1.0em}$ [**Canopy** onward] if $\mathsf{leadByte} \neq \mathtt{0x01}$: <br> +> $\hspace{1.0em}$ [**Canopy** onward] if $\mathsf{leadByte} \neq hexint{01}$: <br> > $\hspace{2.5em}$ $\mathsf{esk} = \mathsf{ToScalar^{protocol}}(\mathsf{PRF^{expand}_{rseed}}(\mathsf{pre\_esk}))$ <br> > $\hspace{2.5em}$ if $\mathsf{repr}_{\mathbb{G}}(\mathsf{KA.DerivePublic}(\mathsf{esk}, \mathsf{g_d})) \neq \mathtt{ephemeralKey}$, return $\bot$ > @@ -818,7 +818,7 @@ For § 4.20.2, replace with > $\hspace{1.0em}$ let $\mathsf{g_d} = \mathsf{DiversifyHash}(\mathsf{d})$; for Sapling, if $\mathsf{g_d} = \bot$, return $\bot$ <br> -> $\hspace{1.0em}$ [**Canopy** onward] if $\mathsf{leadByte} \neq \mathtt{0x01}$: <br> +> $\hspace{1.0em}$ [**Canopy** onward] if $\mathsf{leadByte} \neq hexint{01}$: <br> > $\hspace{2.5em}$ let $\mathsf{esk} = \mathsf{H^{esk,protocol}_{rseed}}(\underline{\text{ρ}})$ <br> > $\hspace{2.5em}$ if $\mathsf{repr}_{\mathbb{G}}(\mathsf{KA.DerivePublic}(\mathsf{esk}, \mathsf{g_d})) \neq \mathtt{ephemeralKey}$, return $\bot$ <br> > $\hspace{2.5em}$ let $\text{ψ} = \mathsf{H^{\text{ψ},Orchard}_{rseed}}(\underline{\text{ρ}}, 0)$ for Orchard or $\bot$ for Sapling @@ -826,7 +826,7 @@ with > $\hspace{1.0em}$ let $\mathsf{pk_d} = \mathsf{KA.DerivePublic}(\mathsf{ivk}, \mathsf{g_d})$ <br> > $\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> > $\hspace{1.0em}$ let $\mathsf{rcm} = \begin{cases} -> \mathsf{LEOS2IP}_{256}(\mathsf{rseed}),&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x01} \\ +> \mathsf{LEOS2IP}_{256}(\mathsf{rseed}),&\caseif \mathsf{leadByte} = \hexint{01} \\ > \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} > \end{cases}$ <br> > $\hspace{1.0em}$ if $\mathsf{rcm} \geq r_{\mathbb{G}}$, return $\bot$ @@ -837,7 +837,7 @@ $\mathsf{rcm}$ can depend on $\mathsf{g_d}$ and $\mathsf{pk_d}$.) For § 4.20.3, replace > $\hspace{1.0em}$ [**Canopy** onward] let $\underline{\mathsf{rcm}} = \begin{cases} -> \mathsf{rseed},&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x01} \\ +> \mathsf{rseed},&\!\!\!\text{if } \mathsf{leadByte} = hexint{01} \\ > \mathsf{ToScalar}(\mathsf{PRF^{expand}_{rseed}}(\mathsf{pre\_rcm})),&\!\!\!\text{otherwise} > \end{cases}$ <br> > $\hspace{1.0em}$ let $\mathsf{rcm} = \mathsf{LEOS2IP}_{256}(\underline{\mathsf{rcm}})$ and $\mathsf{g_d} = \mathsf{DiversifyHash}(\mathsf{d})$ <br> @@ -849,7 +849,7 @@ with > $\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> > $\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> > $\hspace{1.0em}$ let $\mathsf{rcm} = \begin{cases} -> \mathsf{LEOS2IP}_{256}(\mathsf{rseed}),&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x01} \\ +> \mathsf{LEOS2IP}_{256}(\mathsf{rseed}),&\caseif \mathsf{leadByte} = \hexint{01} \\ > \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} > \end{cases}$ <br> > $\hspace{1.0em}$ if $\mathsf{rcm} \geq r_{\mathbb{G}}$, return $\bot$ @@ -900,7 +900,7 @@ Add the following to the section [Note Structure & Commitment](https://zips.z.ca > When § 4.7.3 ‘Sending Notes (Orchard)’ or § 4.8.3 ‘Dummy Notes (Orchard)’ > are invoked directly or indirectly in the computation of $\text{ρ}$ and > $\text{ψ}$ for an OrchardZSA note, $\mathsf{leadByte}$ MUST be set to -> $\mathtt{0x03}$. +> $\hexint{03}$. In section [Split Notes](https://zips.z.cash/zip-0226#split-notes), change: @@ -910,7 +910,7 @@ In section [Split Notes](https://zips.z.cash/zip-0226#split-notes), change: to > where $\text{ψ}^{\mathsf{nf}}$ is computed as -> $\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)$ +> $\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)$ > for $\mathsf{rseed\_nf}$ sampled uniformly at random on $\mathbb{B}^{{\kern-0.1em\tiny\mathbb{Y}}[32]}$, ... @@ -949,22 +949,22 @@ Import this definition from § 4.7.3 ‘Sending Notes (Orchard)’: > $\mathsf{ToScalar^{Orchard}}\big(\mathsf{PRF^{expand}_{rseed}}(\mathsf{pre\_rcm})\kern-0.1em\big)$ > > where $\mathsf{pre\_rcm} = \begin{cases} -> [\mathtt{0x05}] \,||\, \underline{\text{ρ}},&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x02} \\ -> [\mathtt{0x0B}, \mathsf{leadByte}] \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{g}\star_{\mathsf{d}}) \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{pk}\star_{\mathsf{d}}) \\ -> \hphantom{[\mathtt{0x0B}, \mathsf{leadByte}]} \,||\, \mathsf{I2LEOSP}_{64}(\mathsf{v}) \,||\, \underline{\text{ρ}} \,||\, \mathsf{I2LEOSP}_{256}(\text{ψ}) \\ -> \hphantom{[\mathtt{0x0B}, \mathsf{leadByte}]}\,[\,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{AssetBase}\kern0.08em\star)],&\!\!\!\text{if } \mathsf{leadByte} = \mathtt{0x03}\text{.} \\ +> [\hexint{05}] \,||\, \underline{\text{ρ}},&\caseif \mathsf{leadByte} = \hexint{02} \\ +> [\hexint{0B}, \mathsf{leadByte}] \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{g}\star_{\mathsf{d}}) \,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{pk}\star_{\mathsf{d}}) \\ +> \hphantom{[\hexint{0B}, \mathsf{leadByte}]} \,||\, \mathsf{I2LEOSP}_{64}(\mathsf{v}) \,||\, \underline{\text{ρ}} \,||\, \mathsf{I2LEOSP}_{256}(\text{ψ}) \\ +> \hphantom{[\hexint{0B}, \mathsf{leadByte}]}\,[\,||\, \mathsf{LEBS2OSP}_{256}(\mathsf{AssetBase}\kern0.08em\star)],&\caseif \mathsf{leadByte} = \hexint{03}\text{.} \\ > \end{cases}$ Define: * $\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> where $\mathsf{split\_domain} = \begin{cases} - \mathtt{0x09},&\!\!\!\text{if } \mathsf{split\_flag} = 0 \\ - \mathtt{0x0A},&\!\!\!\text{if } \mathsf{split\_flag} = 1\text{.} + \hexint{09},&\caseif \mathsf{split\_flag} = 0 \\ + \hexint{0A},&\caseif \mathsf{split\_flag} = 1\text{.} \end{cases}$ -* $\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})$ +* $\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})$ $\hspace{21.58em} ||\, \mathsf{I2LEOSP}_{256}(\mathsf{nk})))$ -* $\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})$ +* $\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})$ $\hspace{21.58em} ||\, \mathsf{I2LEOSP}_{256}(\mathsf{nk})))$ * $\mathcal{G}^{\mathsf{Orchard}} = \mathsf{GroupHash}^{\mathbb{P}}(\texttt{“z.cash:Orchard”}, \texttt{“G”})$ @@ -1023,7 +1023,7 @@ $\begin{array}{l} \wedge\; \text{let } \mathsf{pk_d} = [\mathsf{ivk}]\, \mathsf{g_d} \\ \wedge\; \text{let } \text{ψ} = \mathsf{H}^{\text{ψ}}_{\mathsf{r}\text{ψ}}(\underline{\text{ρ}}, \mathsf{split\_flag}) \\ \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) \\ -\wedge\; \text{let } \mathsf{rcm} = \mathsf{H^{rcm,Orchard}_{rseed}}\big(\mathtt{0x03}, \mathsf{note\_repr}\big) \\ +\wedge\; \text{let } \mathsf{rcm} = \mathsf{H^{rcm,Orchard}_{rseed}}\big(\hexint{03}, \mathsf{note\_repr}\big) \\ \wedge\; \text{let } \mathsf{cm} = \mathsf{NoteCommit^{Orchard}_{rcm}}(\mathsf{note\_repr}) \\ \wedge\; \mathsf{cm} \neq \bot \\ \wedge\; \text{let } \mathsf{cm}_x = \mathsf{Extract}_{\mathbb{P}}(\mathsf{cm}) \\ @@ -1121,11 +1121,11 @@ We prefer to fix this without changing $\mathsf{NoteCommit^{Orchard}}$ itself. Instead we change how $\mathsf{rcm}$ is computed to be a hash of $\mathsf{rseed}$ and $\mathsf{noterepr} = (\mathsf{g}\star_{\mathsf{d}}, \mathsf{pk}\star_{\mathsf{d}}, \mathsf{v}, \underline{\text{ρ}}, \text{ψ}[, \mathsf{AssetBase}\kern0.08em\star])$, as detailed in the [Specification] section. -Specifically, when $\mathsf{leadByte} = \mathtt{0x03}$ we have: +Specifically, when $\mathsf{leadByte} = \hexint{03}$ we have: $\mathsf{rcm} = \mathsf{H^{rcm,Orchard}_{rseed}}(\mathsf{leadByte}, \mathsf{noterepr}) = \mathsf{ToScalar^{Orchard}}(\mathsf{PRF^{expand}_{rseed}}(\mathsf{pre\_rcm}))$ -$\text{where } \mathsf{pre\_rcm} = [\mathtt{0x0B}, \mathsf{leadByte}] \,||\, \mathsf{encode}(\mathsf{noterepr})$ +$\text{where } \mathsf{pre\_rcm} = [\hexint{0B}, \mathsf{leadByte}] \,||\, \mathsf{encode}(\mathsf{noterepr})$ Then we view the output of $\mathsf{NoteCommit^{Orchard}_{rcm}}(\mathsf{noterepr})$ @@ -1405,7 +1405,7 @@ TBD: explain that such attacks can break Balance and Spendability, including Spendability for transactions after switching to the Recovery Protocol. Note that we can identify the precise set of note commitments for -recoverable (lead byte $\mathtt{0x03}$) Orchard notes, since they are +recoverable (lead byte $\hexint{03}$) Orchard notes, since they are the commitments for Orchard outputs of v6 transactions. However, we cannot identify the precise set of nullifiers for recoverable notes: an Orchard action in a v6 transaction could be diff --git a/zips/zip-guide-markdown.md b/zips/zip-guide-markdown.md index 8a8a425e..9b0db995 100644 --- a/zips/zip-guide-markdown.md +++ b/zips/zip-guide-markdown.md @@ -154,6 +154,35 @@ In general the conventions in the Zcash protocol specification SHOULD be followe If you find this difficult, don't worry too much about it in initial drafts; the ZIP editors will catch any inconsistencies in review. +## Macros + +$\LaTeX$-style macros are supported in ZIPs. The macros for `zips/<name>.{rst,md}` +must be defined in `zips/<name>.macros.tex`. Macros will only be processed for a +given document if this file exists. + +There is a common set of useful macro definitions in `zips/common-macros.tex`, +but they will not be used by default. To use them, write this line in +`zips/<name>.macros.tex`:: + + \includeonly{common-macros} + +The syntax of macro invocations follows LaTeX and is the same for ZIPs written in +reStructuredText and Markdown. + +For example, the following macro definition: + + \newcommand{\hello}[1]{Hello, #1!} + +causes `\hello{world}` to be replaced with `Hello, world!`. Note that it is more +typical to surround argument subsitutions with braces, e.g. `{#1}`, in the case +of macros intended for use in LaTeX. + +Run `./expand_macros.py --help` for more details, including discussion of escaping +and corner cases. + +Macros can be used anywhere in a document for which they are enabled; their use +is not restricted to embedded LaTeX. + ## Notes and warnings <div class="note"></div> diff --git a/zips/zip-guide.rst b/zips/zip-guide.rst index 43df1676..c272c96a 100644 --- a/zips/zip-guide.rst +++ b/zips/zip-guide.rst @@ -163,6 +163,47 @@ In general the conventions in the Zcash protocol specification SHOULD be followe If you find this difficult, don't worry too much about it in initial drafts; the ZIP editors will catch any inconsistencies in review. +Macros +------ + +$\LaTeX$-style macros are supported in ZIPs. The macros for ``zips/<name>.{rst,md}`` +must be defined in a file called ``zips/<name>.macros.tex``. Macros will only be +processed for a given document if this file exists. + +There is a common set of useful macro definitions in ``zips/common-macros.tex``, +but they will not be used by default. To use them, write this line in +``zips/<name>.macros.tex``:: + + \includeonly{common-macros} + +The syntax of macro invocations follows LaTeX and is the same for ZIPs written in +reStructuredText and Markdown. + +For example, the following macro definition:: + + \newcommand{\hello}[1]{Hello, #1!} + +causes ``\hello{world}`` to be replaced with ``Hello, world!``. Note that it is more +typical to surround argument subsitutions with braces, e.g. ``{#1}``, in the case +of macros intended for use in LaTeX. + +Run ``./expand_macros.py --help`` for more details, including discussion of escaping +and corner cases. + +Macros can be used anywhere in a document for which they are enabled; their use +is not restricted to embedded LaTeX. + +then ``\hello{world}`` will be replaced with ``Hello, world!``. Note that it +is more typical to surround argument subsitutions with braces, e.g. ``{#1}``, in +the case of macros intended for use in LaTeX. + +To escape the ``\`` character, use ``\\``. + +Run ``./expand_macros.py --help`` for more details and discussion of corner cases. + +Macros can be used anywhere in a reStructuredText or Markdown file; their use +is not restricted to embedded LaTeX. + Notes and warnings ------------------