Endoscalars
The curve cycles chapter left a promise open. It split each fold between two circuits and let one transcript supply the challenges for both. In its toy run the challenges crossed between the circuits as small integers, and the secondary circuit used them in plain double-and-add. That chapter also noted that Ragu's code does something else, because in the code a short value maps to a scalar that is not the plain integer.
This chapter supplies that map. We start from a symmetry of the curves that makes one particular large scalar free to apply, and we build a way of reading a short bit string that uses this symmetry at every step. The same string then acts as one scalar in each field of the cycle, which is what lets a challenge cross. After that we count the gates, show why distinct strings stay distinct on Pasta and fail to on the toy curves, and rerun the fold from Chapter 13 with the real map.
Reconstructed from code at commit 3219e08, not confirmed by the authors
The radix-3 encoding, its cost and its injectivity argument come from the Ragu Book and the code, and our script reruns them. The toy fold in Sections 10 and 11 is our own small assembly of how the code uses them. Its width of 5 bits has no counterpart in Ragu.
Terminology
An endoscalar is the bit string itself, which the book writes . We write for its length, 143 in Ragu, because has another job in this chapter. Endoscaling means multiplying a point by the string. Lifting means computing the scalar that the string stands for in a given field. We print bit strings with the first bit on the left, and the first bit is the least significant, as in the code. We keep the names from Chapter 13, so the native side has wires in and the nested side has wires in , and the toy cycle has and in those roles.
1. What the plain integer costs
A short integer does fit in both fields, so the plain approach of Chapter 13 can work in principle. Its trouble is the price. A fuse has to fold about a hundred commitments on each side, each fold multiplies a point by the challenge, and a Ragu circuit holds only gates. The double-and-add from the recursive proofs chapter spends about ten gates on every bit, so one multiplication by a challenge of 143 bits would fill most of a circuit.
The way to reduce that price is to get several bits of scalar out of each point addition, and this needs a supply of multiples of the base point that cost nothing to compute. Curves of the form have such a supply. Using it gives up the plain integer, though, so we then have to check that the circuits still agree on what the string means.
2. The curve's endomorphism
The equation sees only through its cube. So if is a cube root of 1 in the coordinate field, then replacing with leaves the equation unchanged, and the map
sends points of the curve to points of the curve. A cube root other than 1 exists when the field size is 1 modulo 3, which holds for both toy fields and both Pasta fields.
The cube roots of 1 in are , and , since and . We take . The generator maps to , and this image is on , because and , which equals modulo 31.
To be useful for scalar multiplication, the map has to respect addition. We can check this on the addition formula. For points with , the sum has slope , with and . Apply to both inputs. The new slope is
Since , we have , and the new -coordinate is
The new -coordinate is
So . The tangent slope for doubling becomes too, because , and the same two lines then cover doubling. A map from a group to itself that respects the operation is called an endomorphism.
Take and from chapter 08. Their sum is , which maps to . Mapping first gives and , and these add to . We confirmed the identity on all ordered pairs of affine points of .
Now we use the fact that our curves have prime order. Every point is a multiple of one generator , and is some multiple . For any other point , respecting addition gives
So multiplies every point by one scalar , which we call its eigenvalue. Applying three times multiplies by , so in the scalar field. Since is not the identity map, , and dividing by the first factor leaves
| Point | Multiple of that equals | |
|---|---|---|
Every row gives the same multiple, and the same holds for all 42 affine points. So on with we have , and indeed .
This gives us what Section 1 asked for, since a circuit can compute a large multiple of a point at almost no cost:
Computing by the double-and-add of the recursive proofs chapter would take gates for a 3-bit scalar, while takes no gates, because multiplying a wire by the constant is part of a linear combination. The contrast is sharper on Pasta, where is a scalar of about 251 bits, and a multiplication by it would cost about gates with the same method.
Negation is free as well, because it only flips the sign of . So from one point we get the six multiples , , with no gates beyond selecting among them.
3. One constant for each field
Each field has two cube roots other than 1, and the choice of decides which root of the scalar field is the eigenvalue. Here are all the choices on the toy cycle:
| Curve | Coordinate field | Scalar field | ||
|---|---|---|---|---|
A circuit needs on the coordinates of its curve, and it also needs when it does field arithmetic with the same challenge. On a cycle these two roles fall on the same field, because the coordinate field of one curve is the scalar field of the other. Ragu therefore keeps one constant per field, called ZETA, and uses it for both roles. That only works if the choices line up on both curves, and being a cube root other than 1 does not by itself decide this.
The table shows that the toy cycle has such a choice. We take the constant for and the constant for , so that each is the coordinate constant of its own curve and the eigenvalue on the partner curve.
On Pasta, we tested this pair, taken from the pasta_curves crate:
We multiplied the point of Pallas by and got , and we multiplied the point of Vesta by and got . Replacing with its square while stays the same makes the first test fail.
Ragu reads its constants from a dependency, zakura-udon, which computes them with a macro that we did not evaluate. Ragu's consistency test checks the pairing on both curves. The other consistent pair, the squares of these two, passes our test as well.
4. Elements with two images
We now need a way to talk about a combination such as without saying which curve lies on. Treat as a formal symbol that obeys , and work with pairs of integers. These form a ring called , the Eisenstein integers. Products follow from the rule for :
An element of this ring acts on a point of either curve, and replacing the symbol with the curve's eigenvalue gives the same result as a scalar:
Take . With it maps to in , and with it maps to in . Applied to on , the element means , and is the same point. Applied to on , it means , and is the same point.
The rest of the chapter builds on this fact. One element of has an image in each scalar field, and both images are computed by the same rule from the same pair of integers.
We also need to know when an element has image zero, since that decides when two different elements become the same scalar. The tool is the norm,
It equals the squared absolute value of when we read as the complex number , so it is never negative, it is zero only for the zero element, and the triangle inequality applies to its square root.
Suppose maps to zero modulo a prime , where now stands for a root in . Then , and substituting into the norm gives
So the image can be zero only if divides the norm. In particular, a nonzero element whose norm is below cannot map to zero:
The element has norm . With it maps to in , while in it maps to . The element has norm , and it maps to in and to in . We searched all coefficients from to . Each of the nonzero elements that map to in has a norm divisible by 31, and each of the that map to in has a norm divisible by 43.
5. Reading a string as an element
We want a rule that reads bits into an element, using only cheap multiples, with a large number of bits per point addition. Suppose we walk the accumulator by repeated multiplication by a radix , adding one digit multiple of the base point at each stage. The cleanest way to read the result back is to recognize the last digit from the residue of the element modulo , and then repeat on what remains. So we look for a digit set with one digit for each nonzero residue.
The ring modulo 3 has nine residue classes, because and each have three residues. So a tripling walk can use eight nonzero digits, which is three bits for each tripling. The six free multiples fill six classes, and fills the other two:
| Digit | As | Modulo 3 | Norm | |
|---|---|---|---|---|
The last two rows are not free multiples, but Section 7 shows that they still cost little. Halo's original walk uses doubling, with the digits , which gives two bits for each doubling. A doubling walk cannot also use , since the ring modulo 2 has only four classes and the extra digits let two strings reach one element:
For instance, and are both , because .
The layout in group_scale then reads as follows. Bits 0 and 1 are , and each later group of three bits is one digit from the table. On a base point , with digits, the walk is
Expanding the recurrence shows which element the string names:
Ragu uses , and accounts for the width. The doubling at the start uses the two leftover bits, and Section 8 shows why it also matters that is more than a single unit.
Because each digit has its own residue, the element determines the string. Here is the decoding on a small case:
Take the 11-bit string 01011100110, which has three digits. Its first two bits say and , its digits are , and , and Horner gives . Starting from alone, we read the last digit from its residue modulo 3, subtract it, and divide by 3:
| Current value | Modulo 3 | Digit it must end in | After subtracting and dividing by 3 |
|---|---|---|---|
The value that remains is , which is , so the first two bits are and . The string comes back in full.
Every step of that decoding is forced, so two strings cannot name the same element of the ring:
We enumerated every string for from 0 to 4, which at means strings, and no two gave the same element of .
6. One string, two scalars
Our toy endoscalar has one digit and therefore 5 bits. The lift function computes the element's image in a chosen field, by running the Horner form of the formula above with that field's ZETA.
Take the string 11111. Its first two bits are and , so the initial unit is . The remaining three bits are , so the digit is . The element is
With this is in , and with it is in . As an integer, the string is , and neither scalar has any simple relation to that number.
A few more strings show that the two scalars vary independently of each other and of the integer:
| String | As an integer | Element | Scalar in | Scalar in |
|---|---|---|---|---|
11000 | ||||
11010 | ||||
10110 | ||||
00101 | ||||
01011 | ||||
11111 |
Endoscaling a point by this string means running the walk with and the digit multiples. It needs no knowledge of or of the scalar field, because the circuit only computes with coordinates. The result is still the multiple that the lift predicts, on each curve:
| Curve | Base point | Digit point | Direct multiple that equals | |||
|---|---|---|---|---|---|---|
Both rows end at the multiple the lift predicts. On the tripled point returns to itself, because has index and , which is modulo 31.
This matches the book's consistency property, and we can now say why a challenge can cross the cycle. Look at the two uses of each scalar. The scalar in scales polynomials over , whose commitments lie on . A circuit over computes directly by lifting, and it uses the result to fold values. A circuit over cannot hold , but it does not need to, since it walks points of by the bits, and the walk acts as . The other pair works the same way in the opposite direction:

The bits are all that the two circuits share, and a bit is 0 or 1 in any field. Neither circuit ever handles a number from the other's field.
The same equality holds at full size. We ran it with Ragu's own test value:
The code's consistency test uses the endoscalar . Our Python lift sends it to a scalar beginning 0x2d3c28380a8fb270... in and a scalar beginning 0x26ea1541fb5a78a6... in . The walk on on Pallas, in gates over , gives the same point as a direct multiplication by the first, and the walk on on Vesta, in gates over , gives the same point as a direct multiplication by the second. Each walk took gates.
7. The walk in gates
Now we count, using the gates from the recursive proofs chapter, where a gate is one multiplication and a division counts as one multiplication checked backwards. A digit step has two parts, which are picking the digit's point and computing .
Tripling and adding
Done naively, is three additions at three gates each, or nine gates. Two of those gates compute -coordinates of intermediate points that are never kept, so we try to remove them. Write . The first addition has slope , with and . The second addition has slope
So has disappeared, and the slope equation becomes . The third addition follows in the same way, and only its output needs a -coordinate. The code ends up with seven gates.
Picking the digit
Choosing among eight points would normally cost gates for each coordinate of each option, and the base point's coordinates are arbitrary wires. The code avoids this by first moving the point to a curve where its two coordinates are equal. The map takes to , for any nonzero . Choosing makes both new coordinates equal to , and the addition formulas never read , so the walk can run on the new curve and move back at the end.
For on , we get and . The new curve has constant , and lies on it because and modulo 31.
On this curve the base point is , so its free multiples are , and . For the fourth digit we add and . The slope is the constant . From we get and , so the -coordinate is
The -coordinate is . Expanding with and using gives
where the last step uses and . All four digits therefore have coordinates that are affine in :
| Digit | At | |||
|---|---|---|---|---|
Since each coordinate is times a constant, plus a constant, a selection needs only one product with per coordinate once the bits have been combined. Writing and , we can read off an interpolation from the table:
For the first line gives , and the second gives , as the table requires. The products and would cost gates of their own. The plain gadget instead factors the selection so that it takes three gates in total, which we use here without deriving it:
We compared both selectors with direct point arithmetic for all eight digits at every affine point of both curves, which makes comparisons, and they all agreed.
One digit step
Here is the tripling on the normalized curve:
Here is the digit step for our string on . The normalized accumulator is and the digit point is .
| Gate | Equation | Value |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 | ||
| 4 | ||
| 5 | ||
| 6 | ||
| 7 |
So the result on the normalized curve is , which three additions outside the circuit confirm. Dividing by and gives , and that is . The tracer counted gates, namely 3 to normalize, 4 for the doubling, 10 for the digit and 3 to move back.
The start of the walk is cheap for a related reason. The tangent at has slope , which is linear in . Then two gates compute the doubled point and two more apply the sign and the twist. With 47 digits, the total is gates.
Sharing the products
A fuse walks many points by one string, so each of those walks repeats the same products and . Ragu hoists them into a stage that the walk circuits share, and one circuit checks them once. Each digit then needs only the two products with from the equations above, which saves one gate per digit. Here are the costs together:
| Method | Bits | Gates | Gates per bit |
|---|---|---|---|
| Double-and-add from Chapter 10 | 255 | 2,541 | 9.96 |
| Double-and-add from Chapter 10 | 143 | 1,421 | 9.94 |
| Radix 2 with the endomorphism, the book's figure | 128 | 455 | 3.55 |
| Radix 3, plain selector | 143 | 480 | 3.36 |
| Radix 3, hoisted selector | 143 | 433 | 3.03 |
The last two rows come from running our tracer on the point of Vesta with a 143-bit string, where it counted and gates and the result matched a direct multiplication by the lifted scalar. A circuit has gates, so it holds double-and-add multiplication or hoisted walks.
The hoisted count fits the limit of four endoscalings per circuit that Chapter 13 mentioned.
8. Why distinct strings stay distinct
Section 5 showed that strings name distinct elements of the ring, but the circuits work in the fields, and a nonzero ring element can have image zero there. Section 4 tells us when this can happen, so we bound the norm of a difference.
Take two strings and subtract their elements:
where and are the initial units. Units have absolute value 1, so , and digits have absolute value at most , so . Weakening the 4 to gives a tidier result, and the geometric sum takes care of the rest:
Squaring gives the book's bound on the norm, . The book states the bound without these steps, so the derivation is ours. If the bound is below the field size, the difference cannot have image zero, because of the divisibility rule in Section 4.
With 47 digits the bound is , which is about , and the Pasta group orders are about . A nonzero difference therefore cannot have a norm divisible by either order, and the 143-bit strings stay distinct in both fields.
The toy curves are far too small for this, and that makes them a useful illustration of what the bound prevents:
With one digit the bound is , and the largest norm that actually occurs among differences of our 32 strings is . Both exceed 31 and 43, so the argument does not apply on the toy curves.
The 32 strings reach different scalars in and in . For one clash, the strings 00000 and 11101 give the elements and , whose difference has norm , a multiple of 31. Both map to in .
There is also a second use of the norm. The walk uses incomplete additions, which divide by a difference of -coordinates and so fail if the two inputs are equal or opposite. Let the accumulator have index and the digit have index . The three additions need , then and , and then and . Each accumulator has absolute value at least 2 because of the initial doubling, and each later one is , while digits have absolute value at most . So none of these combinations is zero in the ring. Their norms are again below the Pasta group orders, so none has image zero, and the circuit does not need to check these denominators at all. The walk relies on this by using an unchecked bank. On the toy curves the argument does not apply, and we can watch it fail:
We ran all 32 strings on every affine point of both curves, which makes walks. Every walk that finished gave the point the lift predicts. On , strings failed with equal -coordinates in an addition, and on , failed. One failure on is the string 00011, with and digit . After the first addition the accumulator is , and adding again would give the index , the norm-31 element from Section 4, which is the identity on .
The same reasoning needs a base point with nonzero coordinates, since normalization divides by and by powers of . A curve of prime order larger than 3 has no point with , which would have order 2, and none with , which would have order 3.
9. Extracting bits from a challenge
A transcript produces a field element, and the circuit needs its bits. A field element does not always have a unique bit decomposition inside a circuit, because a circuit checks modulo the field size. If the bits have enough room to exceed the field size, a prover can choose between spellings:
In a 6-bit string can name values up to 63, so field elements have two spellings, one of them the value plus 43. The element can be written as 101000 or as 000011, which is . These spellings have different low bits, 10100 and 00001, so they would name two different endoscalars. If we require the top bit to be 0, each of the 32 elements below has one spelling, and the elements from 32 to 42 have none and are rejected.
Ragu therefore requires the value to fit below , the largest power of 2 below the field size, and then takes the low 143 bits of the unique spelling. Extraction also gives the right distribution:
Both Pasta primes lie just above , so the capacity is 254 bits. The rejected values make up about of and about of , which is the figure of about in the source. Each 143-bit string has exactly in-range values above it, so a uniform in-range value gives a uniform string.
An honest prover almost never meets a rejected value. The grinding that Chapter 03 mentioned deals with the rare case for pre_beta, the last challenge of the transcript and the one that becomes . There the prover re-randomizes the eval commitment and squeezes again until the value is in range.
10. Folding with an endoscalar
Now we can rerun the fold of Chapter 13 with the real map. Before doing so, we should separate the challenges that act on foreign points from those that do not. In the fold on the first side, the values and only enter the computation of , which is native to the circuit that folds values. Only also scales commitments, so only has to be applied through a walk. The code matches this on the native side, where it uses and as full field elements.
The nested accumulator needs challenges of its own in , and it gets them by lifting the low bits of each native challenge. The nested has no numeric relation to the native , and it does not need one, since the two batch different polynomials. For both sides lift the same string.
We keep Chapter 13's claims, namely , on and , on . Section 8 showed that some toy strings hit an exceptional addition, so we picked a pre_beta whose walks all finish. Pasta needs no such choice.
Let the transcript give , and pre_beta in , all below 32. The circuit over uses and as they are, and it lifts pre_beta to . The circuit over uses only lifts of the same bit strings, which are , and .
Each value follows from the formula of Chapter 13 with its own scalars. Each point follows the same Horner form, starting from the commitment to , with a walk in place of the double-and-add:
| Output | Computed in | Scalar used | Operation | Result | Gates |
|---|---|---|---|---|---|
| over | Circuit over | Field arithmetic | 8 | ||
| on | Circuit over | Bits, acting as | Walk | 46 | |
| over | Circuit over | Field arithmetic | 8 | ||
| on | Circuit over | Bits, acting as | Walk | 46 |
The decider then works directly with the polynomials, and it confirms the table:
The decider commits to the combined polynomials and , and gets on and on . It evaluates them at and at , and gets in and in . All four numbers match the table, although neither circuit ever computed in the other's field.
Here is how the cost compares with Chapter 13:
Each point now takes gates, two walks of and two additions of , where the 4-bit double-and-add of chapter 13 took gates for a shorter challenge. Each circuit also pays gates to show that the bits are bits and gates to lift them.
The field value has to be the lift, because the walk acts as the lift. The plain integer now gives the wrong answer:
Suppose the circuit over used the integer as in its field arithmetic. The walk on does not act as . It takes the commitment to , while . The value and the point would then describe different polynomials, and the decider would reject.
11. Transcript bridging
The book's name for sharing one transcript between the two circuits is transcript bridging. Chapter 13 explained why it is sound only with input consistency. We now have the tools to say how the lifted values reach the nested side and how that side is held to them.
The nested circuits see no transcript. They read every challenge from a stage of their own trace, committed without blinding on the curve whose coordinates are native to the primary circuit. So its commitment is a known combination of generators, .
The native circuit holds each in its own field, and it cannot compute the lift, which lies in the other field. But walking by the bits of acts as exactly that lift, by the consistency property of Section 6, and has coordinates in the native field. Section 9 of Chapter 13 did this check with plain integers, and here is the real version:
The stage holds with no blinding, on the generators of from chapter 13, and its commitment is . The circuit over holds the three transcript values as wires in its own field, breaks each into bits, and walks each generator by those bits. The walks give , and , which add to in gates. That circuit never computed , or , because it has no wires modulo 31, and it still confirmed a commitment to exactly those values. If the prover puts any of the other values in the place of , the stage commits to a different point and the comparison fails.
So the endoscaling walk has two uses in a fuse. It folds commitments, and it also certifies foreign field values. The code divides the second use by timing. Four bind_challenges circuits handle two challenges each and export their partial sum. The challenge pre_beta comes last in the transcript, after the stage holding those partial sums has been committed, so its term has to wait. The next step's bind_beta adds it and compares the sum with the stage commitment, and for the root proof the verifier rederives the stage itself.
| Challenge | Native circuit uses | Nested circuit uses | What holds the nested copy |
|---|---|---|---|
| The field element | Its lift, from the challenge stage | bind_challenges | |
pre_beta | , its lift in , and its bits for the walk on Pallas | , its lift in , and its bits for the walk on Vesta | bind_beta, then the lift check below |
One gap remains. Each walk reads the bits from a stage as well, and we have to tie those bits to pre_beta. On the native side this is direct, because bind_endoscalar extracts the bits from pre_beta again and compares them wire by wire. The nested side cannot do this, because it holds no pre_beta. It has only the lifted . So its compute_v circuit lifts its own bits and requires the result to equal . This check is only as strong as the lift is injective, as the toy curves show:
The string 00100 also lifts to in , so a prover could load it into the circuit over and pass the comparison with . In that string lifts to and not , so the walk on now acts as and produces in place of . The false accumulator has a value computed with and a point computed with .
Among the 32 toy strings, pairs agree in and disagree in . On Pasta no such pair exists, by the norm bound of Section 8.
This is the reason the book cares about injectivity, beyond keeping the sample space large.
12. Why cheating fails
We go through what a cheater can try, in the order of the sections above.
A second spelling of the challenge. Section 9 showed that a value with two bit decompositions would let a prover pick its endoscalar after the hash. The range requirement leaves one spelling. The verifier rejects a proof whose challenge is out of range, and no in-circuit assignment exists for such a value.
Wrong lifts on the nested side. The stage is unblinded and its commitment is recomputed from the transcript's bits, as Section 11 showed. Any other value changes the point, under the discrete log relation assumption for the generators of that curve.
Other bits in a walk. The native side compares its bits directly with the transcript. The nested side compares a lift, and on Pasta only one string has that lift.
A lucky challenge. A short challenge has a smaller range, as Chapter 13 counted. The lift adds a condition, which is that distinct strings must not pile up on the same scalar:
Take a cheater whose error in is the polynomial , as in chapter 13. It passes when the string lifts to . With an injective lift, one string out of the 32 would pass. On the toy cycle the lift is not injective, and the cheater does best with , which strings reach, for a chance of in 32. So a collision in the lift directly raises a cheater's chance.
On Pasta the lift is injective in both fields, so each root of a fixed nonzero polynomial is hit by at most one string out of .
The book adds up the roots charged to each challenge for one fuse step, and gets over all nine challenges. We recomputed that sum and the resulting .
The book calls this a conditional accounting for one step and not a security theorem, and it lists what a full argument would still have to cover, including an adversary's repeated Fiat–Shamir trials. We do not go further than that here.
Using an endoscalar for a secret. All of the above concerns public challenges. For a secret, the structure of the expansion helps an attacker, which is why the book and the code restrict endoscalars to public values:
Suppose the secret string is 00111, and an attacker sees . The attacker tabulates the 8 possible digit points, tries the 4 possible values of the head , and looks up minus each head in the table. That is 12 operations against 32 for trying every string, and the search finds the string. The saving grows with the width, because the expansion splits at any digit into a head and a tail that add up. At 143 bits the same idea takes roughly work.
That is far below the roughly work of a generic discrete log search in a group of elements, so a blinding factor must not be an endoscalar. A public challenge faces a different event, in which a fresh value hits a root that was fixed beforehand, and a meet-in-the-middle search does not apply to that.
The argument rests on these assumptions, and it is informal. Discrete log has to be hard on both curves, and the transcript hash has to behave as a random oracle. Every commitment has to enter the transcript before the challenges that test it, which is the input consistency of Chapter 13. The base points have to lie on the curve, which the circuits enforce separately. Each check also depends on a later step, so the decider has to finish the work at the root.
13. Feedback between the walks
A circuit does at most four endoscalings, so a walk over many points spreads over several step circuits. These step circuits have traces, and those traces have commitments, and the other side then has to fold those commitments too. Adding circuits on one side therefore adds work on the other side, and we need to know whether this settles at a finite number. The book treats this as a convergence problem.
Let and be the base number of endoscalings on each curve, and let be the capacity of one circuit. The book's model says that each circuit adds one endoscaling on the other curve. The first round needs circuits on one side. They add endoscalings on the other side, or circuits there, which add circuits back on the first side, and so on. Collecting the terms on one curve gives two geometric series with ratio :
For the sum is against a first round of , which is a factor of . The book also gives , where the factor is .
The code's batch has a different count. Its point formula is , because a fuse folds the commitments of both children, so one more step circuit adds two points to the other walk rather than one. The other side's formula has the same shape. We can run the feedback from zero, with 22 fixed rx polynomials on the native side and 13 on the nested side:
| Round | Points the native walk folds | Native steps | Points the nested walk folds | Nested steps |
|---|---|---|---|---|
| 1 | 41 | 10 | 59 | 15 |
| 2 | 71 | 18 | 79 | 20 |
| 3 | 81 | 20 | 95 | 24 |
| 4 | 89 | 22 | 99 | 25 |
| 5 | 91 | 23 | 103 | 26 |
| 6 | 93 | 23 | 105 | 26 |
The iteration stops at native steps and nested steps, the values that the code pins. With no feedback the first round would have been enough, with steps in all, so the feedback multiplies the count by about .
Each step now adds 2 points to a walk that does 4 per step, so the ratio per round is and not . For equal base counts the series sums to , a factor of , which agrees with the count above.
So the process converges in either model, and the factor is a modest constant. Section 14 records the difference between the two factors.
14. Endoscalars in Ragu
Reconstructed from code at commit 3219e08, not confirmed by the authors
- The gadgets.
Endoscalarholds 143 Boolean wires, andHoistedEndoscalaradds the product wires.EndoscalarChallengecarries the element together with its constrained decomposition. - Which gadget runs where. The native and nested step circuits walk with the hoisted gadget. The binders walk constant generators by different strings, so they use the plain gadget.
- The native field value.
compute_vextracts the string frompre_betaand lifts it for the Horner sum of . The prover does the same outside the circuit. - Machine-checked parts. The book reports Lean proofs of the gadget contracts for the walk and the lift, and it says that the sharing of staged wires and the protocol's overall soundness lie outside them. We did not run those proofs.
Which challenges are endoscalars
Book: the transcript bridging section sets every verifier challenge to its extracted endoscalar, viewed as a subset of , and multiplies by it directly in the primary circuit.
Code at 3219e08: native challenges other than pre_beta stay full field elements, and a string never acts as its integer. The primary circuit multiplies by the lift. The book's endoscalar page agrees with the code on the nested lifts.
This site follows the code, which settles the difference that Chapter 13 recorded.
How a string reaches the other circuit
Book: the endoscalar page describes running with the endoscalar as a public output of the circuit and using it as a public input of the circuit.
Code at 3219e08: the bits are not a public input. The nested side holds them in a stage, and the checks of Section 11 tie them to pre_beta. The same page's later section on hoisting describes this design.
This site follows the code.
Out-of-range challenges
Book: a prover that derives a value from a transcript re-randomizes and tries again.
Code at 3219e08: only pre_beta is ground in this way. For the other challenges, the lift returns an error, an event the code puts at .
This site follows the code. The difference has little practical effect, given how rarely the event can occur.
The feedback factor
Book: each circuit adds one endoscaling on the other curve, which gives a factor of about 1.33 for . The book's closing arithmetic for also writes the ratio inverted, although its value of 1.14 is correct for the ratio taken the other way.
Code at 3219e08: each step adds a point per child, and Section 13's iteration gives a factor near 2. The reading that this explains the difference is ours, and the book may intend its model only as a simplification.
This site uses the code's counts.
All of this describes the Ragu revision at 3219e08. Tachyon, which has not activated on mainnet and whose design is still changing, pins a different Ragu revision.
15. Where this chapter leaves us
This chapter took the following steps:
- On a curve with a cube root , the map respects addition, and on a curve of prime order it multiplies every point by one scalar with .
- Each field of a cycle keeps one constant, which acts on the coordinates of its own curve and as the eigenvalue on the partner curve, and these choices have to line up.
- Elements have one image in each scalar field, and an image can be zero only when the field size divides the norm .
- A string of bits reads as an element through a radix-3 walk, whose eight digits are the nonzero residues modulo 3, so the element determines the string.
- The walk costs ten gates per three bits, or nine with shared products, which makes 143 bits about three times cheaper than plain double-and-add.
- On Pasta the norm bound keeps strings distinct in both fields and rules out exceptional additions, and the toy curves show both failures.
- Extraction takes the low bits of the unique spelling of an in-range value, and a prover grinds
pre_betato stay in range. - A circuit lifts a string to get a field value in its own field and walks the string to act on foreign points, so one string serves both circuits with no foreign arithmetic.
- The primary circuit holds the nested side to the lifted values by walking generators, and the nested side ties its bits to through injectivity.
- Step circuits feed work back to the other curve, and the count settles at 23 native steps and 26 nested steps.
Several of these mechanisms depended on stages, including the challenge stage, the walk stage and the bridge stages. The next chapter explains how a circuit commits to its trace in parts and how the partial commitments are checked.