9 Commits
sdl ... dev

Author SHA1 Message Date
Duke Leto
9e4f80d453 Downgrade arrayvec to original version used in Cargo.lock 2022-03-07 10:17:37 -05:00
Duke Leto
dbbc38b9b8 update to latest blake2-rfc 2022-03-07 10:00:34 -05:00
Duke Leto
3329f46511 update aes, fpe and blake2-rfc deps 2022-03-03 19:34:21 -05:00
Duke Leto
9ef137d7e9 Update aes, aesni and authors 2022-03-03 18:59:02 -05:00
Duke Leto
c625034163 Add authors file 2020-11-19 21:59:46 -05:00
Duke Leto
85bfece0be Update license files 2020-11-19 21:58:39 -05:00
Duke Leto
51768798d3 Update readme and re-license to GPLv3 2020-11-19 21:58:01 -05:00
Duke Leto
9f7d341f9a Merge pull request #4 from yusufsahinhamza/improve-travis-ci
Improve Travis CI
2020-05-01 09:55:44 -04:00
Yusuf Şahin HAMZA
e5fe8d2591 Improve Travis CI 2020-05-01 16:26:15 +03:00
147 changed files with 6559 additions and 13069 deletions

View File

@@ -1,13 +1,18 @@
language: rust language: rust
rust: rust:
- 1.36.0 - 1.32.0
env:
global:
# See https://stackoverflow.com/a/43339593
- RUST_BACKTRACE=1
before_cache:
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
cache: cargo cache: cargo
before_script:
- rustup component add rustfmt
script: script:
- cargo build --verbose --release --all - cargo build --verbose --release --all
- cargo fmt --all -- --check
- cargo test --verbose --release --all - cargo test --verbose --release --all

3
AUTHORS Normal file
View File

@@ -0,0 +1,3 @@
The Hush Developers
Duke Leto, https://git.hush.is/duke

806
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,10 +5,11 @@ members = [
"group", "group",
"librustzcash", "librustzcash",
"pairing", "pairing",
"zcash_client_backend", "sapling-crypto",
"zcash_client_sqlite",
"zcash_primitives", "zcash_primitives",
"zcash_proofs", "zcash_proofs",
"zcash_wallet",
"zip32",
] ]
[profile.release] [profile.release]

619
LICENSE Normal file
View File

@@ -0,0 +1,619 @@
GENERAL GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GENERAL General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GENERAL General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GENERAL General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GENERAL GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GENERAL General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GENERAL Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GENERAL Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GENERAL Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GENERAL General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GENERAL General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GENERAL General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GENERAL General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

View File

@@ -1,7 +1,7 @@
# Zcash Rust crates # Hush Rust crates
This repository contains a (work-in-progress) set of Rust crates for This repository contains a set of Rust crates for
working with Zcash. working with low-level Hush stuff.
## Security Warnings ## Security Warnings
@@ -9,16 +9,5 @@ These libraries are currently under development and have not been fully-reviewed
## License ## License
All code in this workspace is licensed under either of GNU Public License 3
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

View File

@@ -9,8 +9,8 @@ repository = "https://github.com/ebfull/bellman"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
rand = "0.4"
bit-vec = "0.4.4" bit-vec = "0.4.4"
blake2s_simd = "0.5"
ff = { path = "../ff" } ff = { path = "../ff" }
futures = "0.1" futures = "0.1"
futures-cpupool = { version = "0.1", optional = true } futures-cpupool = { version = "0.1", optional = true }
@@ -18,15 +18,8 @@ group = { path = "../group" }
num_cpus = { version = "1", optional = true } num_cpus = { version = "1", optional = true }
crossbeam = { version = "0.3", optional = true } crossbeam = { version = "0.3", optional = true }
pairing = { path = "../pairing", optional = true } pairing = { path = "../pairing", optional = true }
rand_core = "0.5"
byteorder = "1" byteorder = "1"
[dev-dependencies]
hex-literal = "0.1"
rand = "0.7"
rand_xorshift = "0.2"
sha2 = "0.8"
[features] [features]
groth16 = ["pairing"] groth16 = ["pairing"]
multicore = ["futures-cpupool", "crossbeam", "num_cpus"] multicore = ["futures-cpupool", "crossbeam", "num_cpus"]

View File

@@ -13,7 +13,9 @@
use ff::{Field, PrimeField, ScalarEngine}; use ff::{Field, PrimeField, ScalarEngine};
use group::CurveProjective; use group::CurveProjective;
use super::SynthesisError; use super::{
SynthesisError
};
use super::multicore::Worker; use super::multicore::Worker;
@@ -23,7 +25,7 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
omega: E::Fr, omega: E::Fr,
omegainv: E::Fr, omegainv: E::Fr,
geninv: E::Fr, geninv: E::Fr,
minv: E::Fr, minv: E::Fr
} }
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> { impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
@@ -39,7 +41,8 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
self.coeffs self.coeffs
} }
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError> { pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError>
{
// Compute the size of our evaluation domain // Compute the size of our evaluation domain
let mut m = 1; let mut m = 1;
let mut exp = 0; let mut exp = 0;
@@ -50,7 +53,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
// The pairing-friendly curve may not be able to support // The pairing-friendly curve may not be able to support
// large enough (radix2) evaluation domains. // large enough (radix2) evaluation domains.
if exp >= E::Fr::S { if exp >= E::Fr::S {
return Err(SynthesisError::PolynomialDegreeTooLarge); return Err(SynthesisError::PolynomialDegreeTooLarge)
} }
} }
@@ -69,18 +72,17 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
omega: omega, omega: omega,
omegainv: omega.inverse().unwrap(), omegainv: omega.inverse().unwrap(),
geninv: E::Fr::multiplicative_generator().inverse().unwrap(), geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
minv: E::Fr::from_str(&format!("{}", m)) minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap()
.unwrap()
.inverse()
.unwrap(),
}) })
} }
pub fn fft(&mut self, worker: &Worker) { pub fn fft(&mut self, worker: &Worker)
{
best_fft(&mut self.coeffs, worker, &self.omega, self.exp); best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
} }
pub fn ifft(&mut self, worker: &Worker) { pub fn ifft(&mut self, worker: &Worker)
{
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp); best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
@@ -96,7 +98,8 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
}); });
} }
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) { pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr)
{
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
scope.spawn(move || { scope.spawn(move || {
@@ -110,12 +113,14 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
}); });
} }
pub fn coset_fft(&mut self, worker: &Worker) { pub fn coset_fft(&mut self, worker: &Worker)
{
self.distribute_powers(worker, E::Fr::multiplicative_generator()); self.distribute_powers(worker, E::Fr::multiplicative_generator());
self.fft(worker); self.fft(worker);
} }
pub fn icoset_fft(&mut self, worker: &Worker) { pub fn icoset_fft(&mut self, worker: &Worker)
{
let geninv = self.geninv; let geninv = self.geninv;
self.ifft(worker); self.ifft(worker);
@@ -134,11 +139,9 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
/// The target polynomial is the zero polynomial in our /// The target polynomial is the zero polynomial in our
/// evaluation domain, so we must perform division over /// evaluation domain, so we must perform division over
/// a coset. /// a coset.
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) { pub fn divide_by_z_on_coset(&mut self, worker: &Worker)
let i = self {
.z(&E::Fr::multiplicative_generator()) let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
.inverse()
.unwrap();
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {
@@ -156,11 +159,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len()); assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
.coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move || { scope.spawn(move || {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0); a.group_mul_assign(&b.0);
@@ -175,11 +174,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len()); assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
.coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move || { scope.spawn(move || {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_sub_assign(&b); a.group_sub_assign(&b);
@@ -259,7 +254,8 @@ impl<E: ScalarEngine> Group<E> for Scalar<E> {
} }
} }
fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) { fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32)
{
let log_cpus = worker.log_num_cpus(); let log_cpus = worker.log_num_cpus();
if log_n <= log_cpus { if log_n <= log_cpus {
@@ -269,7 +265,8 @@ fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &
} }
} }
fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32) { fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32)
{
fn bitreverse(mut n: u32, l: u32) -> u32 { fn bitreverse(mut n: u32, l: u32) -> u32 {
let mut r = 0; let mut r = 0;
for _ in 0..l { for _ in 0..l {
@@ -318,8 +315,9 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
worker: &Worker, worker: &Worker,
omega: &E::Fr, omega: &E::Fr,
log_n: u32, log_n: u32,
log_cpus: u32, log_cpus: u32
) { )
{
assert!(log_n >= log_cpus); assert!(log_n >= log_cpus);
let num_cpus = 1 << log_cpus; let num_cpus = 1 << log_cpus;
@@ -377,19 +375,16 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
#[test] #[test]
fn polynomial_arith() { fn polynomial_arith() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand::{self, Rand};
fn test_mul<E: ScalarEngine, R: RngCore>(rng: &mut R) { fn test_mul<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
let worker = Worker::new(); let worker = Worker::new();
for coeffs_a in 0..70 { for coeffs_a in 0..70 {
for coeffs_b in 0..70 { for coeffs_b in 0..70 {
let mut a: Vec<_> = (0..coeffs_a) let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
.map(|_| Scalar::<E>(E::Fr::random(rng))) let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
.collect();
let mut b: Vec<_> = (0..coeffs_b)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect();
// naive evaluation // naive evaluation
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b]; let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
@@ -428,9 +423,10 @@ fn polynomial_arith() {
#[test] #[test]
fn fft_composition() { fn fft_composition() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand;
fn test_comp<E: ScalarEngine, R: RngCore>(rng: &mut R) { fn test_comp<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
let worker = Worker::new(); let worker = Worker::new();
for coeffs in 0..10 { for coeffs in 0..10 {
@@ -438,7 +434,7 @@ fn fft_composition() {
let mut v = vec![]; let mut v = vec![];
for _ in 0..coeffs { for _ in 0..coeffs {
v.push(Scalar::<E>(E::Fr::random(rng))); v.push(Scalar::<E>(rng.gen()));
} }
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap(); let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
@@ -466,19 +462,18 @@ fn fft_composition() {
#[test] #[test]
fn parallel_fft_consistency() { fn parallel_fft_consistency() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand::{self, Rand};
use std::cmp::min; use std::cmp::min;
fn test_consistency<E: ScalarEngine, R: RngCore>(rng: &mut R) { fn test_consistency<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
let worker = Worker::new(); let worker = Worker::new();
for _ in 0..5 { for _ in 0..5 {
for log_d in 0..10 { for log_d in 0..10 {
let d = 1 << log_d; let d = 1 << log_d;
let v1 = (0..d) let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect::<Vec<_>>();
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect::<Vec<_>>();
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap(); let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap(); let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();

View File

@@ -1,110 +0,0 @@
use super::boolean::Boolean;
use super::num::Num;
use super::Assignment;
use crate::{ConstraintSystem, SynthesisError};
use ff::{Field, PrimeField};
use pairing::Engine;
/// Takes a sequence of booleans and exposes them as compact
/// public inputs
pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
where
E: Engine,
CS: ConstraintSystem<E>,
{
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
let mut num = Num::<E>::zero();
let mut coeff = E::Fr::one();
for bit in bits {
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
coeff.double();
}
let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?;
// num * 1 = input
cs.enforce(
|| format!("packing constraint {}", i),
|_| num.lc(E::Fr::one()),
|lc| lc + CS::one(),
|lc| lc + input,
);
}
Ok(())
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
bytes
.iter()
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
bytes
.iter()
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn compute_multipacking<E: Engine>(bits: &[bool]) -> Vec<E::Fr> {
let mut result = vec![];
for bits in bits.chunks(E::Fr::CAPACITY as usize) {
let mut cur = E::Fr::zero();
let mut coeff = E::Fr::one();
for bit in bits {
if *bit {
cur.add_assign(&coeff);
}
coeff.double();
}
result.push(cur);
}
result
}
#[test]
fn test_multipacking() {
use crate::ConstraintSystem;
use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use super::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::*;
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for num_bits in 0..1500 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let bits: Vec<bool> = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect();
let circuit_bits = bits
.iter()
.enumerate()
.map(|(i, &b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(b)).unwrap(),
)
})
.collect::<Vec<_>>();
let expected_inputs = compute_multipacking::<Bls12>(&bits);
pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap();
assert!(cs.is_satisfied());
assert!(cs.verify(&expected_inputs));
}
}

View File

@@ -1,4 +1,4 @@
use rand_core::RngCore; use rand::Rng;
use std::sync::Arc; use std::sync::Arc;
@@ -6,34 +6,55 @@ use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective, Wnaf}; use group::{CurveAffine, CurveProjective, Wnaf};
use pairing::Engine; use pairing::Engine;
use super::{Parameters, VerifyingKey}; use super::{
Parameters,
VerifyingKey
};
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use ::{
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use domain::{EvaluationDomain, Scalar}; use ::domain::{
EvaluationDomain,
Scalar
};
use multicore::Worker; use ::multicore::{
Worker
};
/// Generates a random common reference string for /// Generates a random common reference string for
/// a circuit. /// a circuit.
pub fn generate_random_parameters<E, C, R>( pub fn generate_random_parameters<E, C, R>(
circuit: C, circuit: C,
rng: &mut R, rng: &mut R
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where where E: Engine, C: Circuit<E>, R: Rng
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let g1 = E::G1::random(rng); let g1 = rng.gen();
let g2 = E::G2::random(rng); let g2 = rng.gen();
let alpha = E::Fr::random(rng); let alpha = rng.gen();
let beta = E::Fr::random(rng); let beta = rng.gen();
let gamma = E::Fr::random(rng); let gamma = rng.gen();
let delta = E::Fr::random(rng); let delta = rng.gen();
let tau = E::Fr::random(rng); let tau = rng.gen();
generate_parameters::<E, C>(circuit, g1, g2, alpha, beta, gamma, delta, tau) generate_parameters::<E, C>(
circuit,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
)
} }
/// This is our assembly structure that we'll use to synthesize the /// This is our assembly structure that we'll use to synthesize the
@@ -47,17 +68,18 @@ struct KeypairAssembly<E: Engine> {
ct_inputs: Vec<Vec<(E::Fr, usize)>>, ct_inputs: Vec<Vec<(E::Fr, usize)>>,
at_aux: Vec<Vec<(E::Fr, usize)>>, at_aux: Vec<Vec<(E::Fr, usize)>>,
bt_aux: Vec<Vec<(E::Fr, usize)>>, bt_aux: Vec<Vec<(E::Fr, usize)>>,
ct_aux: Vec<Vec<(E::Fr, usize)>>, ct_aux: Vec<Vec<(E::Fr, usize)>>
} }
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> { impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, _: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
// There is no assignment, so we don't even invoke the // There is no assignment, so we don't even invoke the
// function for obtaining one. // function for obtaining one.
@@ -72,11 +94,12 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
Ok(Variable(Index::Aux(index))) Ok(Variable(Index::Aux(index)))
} }
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, _: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
// There is no assignment, so we don't even invoke the // There is no assignment, so we don't even invoke the
// function for obtaining one. // function for obtaining one.
@@ -91,59 +114,48 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
Ok(Variable(Index::Input(index))) Ok(Variable(Index::Input(index)))
} }
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, _: A,
AR: Into<String>, a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
fn eval<E: Engine>( fn eval<E: Engine>(
l: LinearCombination<E>, l: LinearCombination<E>,
inputs: &mut [Vec<(E::Fr, usize)>], inputs: &mut [Vec<(E::Fr, usize)>],
aux: &mut [Vec<(E::Fr, usize)>], aux: &mut [Vec<(E::Fr, usize)>],
this_constraint: usize, this_constraint: usize
) { )
{
for (index, coeff) in l.0 { for (index, coeff) in l.0 {
match index { match index {
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)), Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)), Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint))
} }
} }
} }
eval( eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints);
a(LinearCombination::zero()), eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints);
&mut self.at_inputs, eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints);
&mut self.at_aux,
self.num_constraints,
);
eval(
b(LinearCombination::zero()),
&mut self.bt_inputs,
&mut self.bt_aux,
self.num_constraints,
);
eval(
c(LinearCombination::zero()),
&mut self.ct_inputs,
&mut self.ct_aux,
self.num_constraints,
);
self.num_constraints += 1; self.num_constraints += 1;
} }
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where where NR: Into<String>, N: FnOnce() -> NR
NR: Into<String>,
N: FnOnce() -> NR,
{ {
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
fn pop_namespace(&mut self) { fn pop_namespace(&mut self)
{
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
@@ -161,11 +173,9 @@ pub fn generate_parameters<E, C>(
beta: E::Fr, beta: E::Fr,
gamma: E::Fr, gamma: E::Fr,
delta: E::Fr, delta: E::Fr,
tau: E::Fr, tau: E::Fr
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where where E: Engine, C: Circuit<E>
E: Engine,
C: Circuit<E>,
{ {
let mut assembly = KeypairAssembly { let mut assembly = KeypairAssembly {
num_inputs: 0, num_inputs: 0,
@@ -176,7 +186,7 @@ where
ct_inputs: vec![], ct_inputs: vec![],
at_aux: vec![], at_aux: vec![],
bt_aux: vec![], bt_aux: vec![],
ct_aux: vec![], ct_aux: vec![]
}; };
// Allocate the "one" input variable // Allocate the "one" input variable
@@ -188,7 +198,11 @@ where
// Input constraints to ensure full density of IC query // Input constraints to ensure full density of IC query
// x * 0 = 0 // x * 0 = 0
for i in 0..assembly.num_inputs { for i in 0..assembly.num_inputs {
assembly.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc); assembly.enforce(|| "",
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
} }
// Create bases for blind evaluation of polynomials at tau // Create bases for blind evaluation of polynomials at tau
@@ -226,7 +240,8 @@ where
{ {
let powers_of_tau = powers_of_tau.as_mut(); let powers_of_tau = powers_of_tau.as_mut();
worker.scope(powers_of_tau.len(), |scope, chunk| { worker.scope(powers_of_tau.len(), |scope, chunk| {
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() { for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
{
scope.spawn(move || { scope.spawn(move || {
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]); let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
@@ -245,15 +260,14 @@ where
// Compute the H query with multiple threads // Compute the H query with multiple threads
worker.scope(h.len(), |scope, chunk| { worker.scope(h.len(), |scope, chunk| {
for (h, p) in h for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
.chunks_mut(chunk)
.zip(powers_of_tau.as_ref().chunks(chunk))
{ {
let mut g1_wnaf = g1_wnaf.shared(); let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || { scope.spawn(move || {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta} // Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (h, p) in h.iter_mut().zip(p.iter()) { for (h, p) in h.iter_mut().zip(p.iter())
{
// Compute final exponent // Compute final exponent
let mut exp = p.0; let mut exp = p.0;
exp.mul_assign(&coeff); exp.mul_assign(&coeff);
@@ -306,8 +320,9 @@ where
beta: &E::Fr, beta: &E::Fr,
// Worker // Worker
worker: &Worker, worker: &Worker
) { )
{
// Sanity check // Sanity check
assert_eq!(a.len(), at.len()); assert_eq!(a.len(), at.len());
assert_eq!(a.len(), bt.len()); assert_eq!(a.len(), bt.len());
@@ -318,8 +333,7 @@ where
// Evaluate polynomials in multiple threads // Evaluate polynomials in multiple threads
worker.scope(a.len(), |scope, chunk| { worker.scope(a.len(), |scope, chunk| {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
.chunks_mut(chunk)
.zip(b_g1.chunks_mut(chunk)) .zip(b_g1.chunks_mut(chunk))
.zip(b_g2.chunks_mut(chunk)) .zip(b_g2.chunks_mut(chunk))
.zip(ext.chunks_mut(chunk)) .zip(ext.chunks_mut(chunk))
@@ -331,8 +345,7 @@ where
let mut g2_wnaf = g2_wnaf.shared(); let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || { scope.spawn(move || {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
.iter_mut()
.zip(b_g1.iter_mut()) .zip(b_g1.iter_mut())
.zip(b_g2.iter_mut()) .zip(b_g2.iter_mut())
.zip(ext.iter_mut()) .zip(ext.iter_mut())
@@ -342,8 +355,9 @@ where
{ {
fn eval_at_tau<E: Engine>( fn eval_at_tau<E: Engine>(
powers_of_tau: &[Scalar<E>], powers_of_tau: &[Scalar<E>],
p: &[(E::Fr, usize)], p: &[(E::Fr, usize)]
) -> E::Fr { ) -> E::Fr
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(ref coeff, index) in p { for &(ref coeff, index) in p {
@@ -408,7 +422,7 @@ where
&gamma_inverse, &gamma_inverse,
&alpha, &alpha,
&beta, &beta,
&worker, &worker
); );
// Evaluate for auxiliary variables. // Evaluate for auxiliary variables.
@@ -426,7 +440,7 @@ where
&delta_inverse, &delta_inverse,
&alpha, &alpha,
&beta, &beta,
&worker, &worker
); );
// Don't allow any elements be unconstrained, so that // Don't allow any elements be unconstrained, so that
@@ -447,7 +461,7 @@ where
gamma_g2: g2.mul(gamma).into_affine(), gamma_g2: g2.mul(gamma).into_affine(),
delta_g1: g1.mul(delta).into_affine(), delta_g1: g1.mul(delta).into_affine(),
delta_g2: g2.mul(delta).into_affine(), delta_g2: g2.mul(delta).into_affine(),
ic: ic.into_iter().map(|e| e.into_affine()).collect(), ic: ic.into_iter().map(|e| e.into_affine()).collect()
}; };
Ok(Parameters { Ok(Parameters {
@@ -456,23 +470,8 @@ where
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
// Filter points at infinity away from A/B queries // Filter points at infinity away from A/B queries
a: Arc::new( a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
a.into_iter() b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
.filter(|e| !e.is_zero()) b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect())
.map(|e| e.into_affine())
.collect(),
),
b_g1: Arc::new(
b_g1.into_iter()
.filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
b_g2: Arc::new(
b_g2.into_iter()
.filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
}) })
} }

View File

@@ -1,12 +1,17 @@
use group::{CurveAffine, EncodedPoint}; use group::{CurveAffine, EncodedPoint};
use pairing::{Engine, PairingCurveAffine}; use pairing::{
Engine,
PairingCurveAffine,
};
use SynthesisError; use ::{
SynthesisError
};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use multiexp::SourceBuilder; use multiexp::SourceBuilder;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::sync::Arc; use std::sync::Arc;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -23,17 +28,23 @@ pub use self::verifier::*;
pub struct Proof<E: Engine> { pub struct Proof<E: Engine> {
pub a: E::G1Affine, pub a: E::G1Affine,
pub b: E::G2Affine, pub b: E::G2Affine,
pub c: E::G1Affine, pub c: E::G1Affine
} }
impl<E: Engine> PartialEq for Proof<E> { impl<E: Engine> PartialEq for Proof<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.a == other.a && self.b == other.b && self.c == other.c self.a == other.a &&
self.b == other.b &&
self.c == other.c
} }
} }
impl<E: Engine> Proof<E> { impl<E: Engine> Proof<E> {
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.a.into_compressed().as_ref())?; writer.write_all(self.a.into_compressed().as_ref())?;
writer.write_all(self.b.into_compressed().as_ref())?; writer.write_all(self.b.into_compressed().as_ref())?;
writer.write_all(self.c.into_compressed().as_ref())?; writer.write_all(self.c.into_compressed().as_ref())?;
@@ -41,7 +52,10 @@ impl<E: Engine> Proof<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> { pub fn read<R: Read>(
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty(); let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty(); let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
@@ -49,48 +63,37 @@ impl<E: Engine> Proof<E> {
let a = g1_repr let a = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
}
})?; })?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let b = g2_repr let b = g2_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
}
})?; })?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let c = g1_repr let c = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
}
})?; })?;
Ok(Proof { a: a, b: b, c: c }) Ok(Proof {
a: a,
b: b,
c: c
})
} }
} }
@@ -119,23 +122,27 @@ pub struct VerifyingKey<E: Engine> {
// for all public inputs. Because all public inputs have a dummy constraint, // for all public inputs. Because all public inputs have a dummy constraint,
// this is the same size as the number of inputs, and never contains points // this is the same size as the number of inputs, and never contains points
// at infinity. // at infinity.
pub ic: Vec<E::G1Affine>, pub ic: Vec<E::G1Affine>
} }
impl<E: Engine> PartialEq for VerifyingKey<E> { impl<E: Engine> PartialEq for VerifyingKey<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.alpha_g1 == other.alpha_g1 self.alpha_g1 == other.alpha_g1 &&
&& self.beta_g1 == other.beta_g1 self.beta_g1 == other.beta_g1 &&
&& self.beta_g2 == other.beta_g2 self.beta_g2 == other.beta_g2 &&
&& self.gamma_g2 == other.gamma_g2 self.gamma_g2 == other.gamma_g2 &&
&& self.delta_g1 == other.delta_g1 self.delta_g1 == other.delta_g1 &&
&& self.delta_g2 == other.delta_g2 self.delta_g2 == other.delta_g2 &&
&& self.ic == other.ic self.ic == other.ic
} }
} }
impl<E: Engine> VerifyingKey<E> { impl<E: Engine> VerifyingKey<E> {
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g1.into_uncompressed().as_ref())?; writer.write_all(self.beta_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
@@ -150,39 +157,30 @@ impl<E: Engine> VerifyingKey<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> { pub fn read<R: Read>(
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty(); let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty(); let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let alpha_g1 = g1_repr let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let beta_g1 = g1_repr let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let beta_g2 = g2_repr let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let gamma_g2 = g2_repr let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let delta_g1 = g1_repr let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let delta_g2 = g2_repr let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let ic_len = reader.read_u32::<BigEndian>()? as usize; let ic_len = reader.read_u32::<BigEndian>()? as usize;
@@ -193,15 +191,10 @@ impl<E: Engine> VerifyingKey<E> {
let g1 = g1_repr let g1 = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
}
})?; })?;
ic.push(g1); ic.push(g1);
@@ -214,7 +207,7 @@ impl<E: Engine> VerifyingKey<E> {
gamma_g2: gamma_g2, gamma_g2: gamma_g2,
delta_g1: delta_g1, delta_g1: delta_g1,
delta_g2: delta_g2, delta_g2: delta_g2,
ic: ic, ic: ic
}) })
} }
} }
@@ -241,22 +234,26 @@ pub struct Parameters<E: Engine> {
// G1 and G2 for C/B queries, respectively. Never contains points at // G1 and G2 for C/B queries, respectively. Never contains points at
// infinity for the same reason as the "A" polynomials. // infinity for the same reason as the "A" polynomials.
pub b_g1: Arc<Vec<E::G1Affine>>, pub b_g1: Arc<Vec<E::G1Affine>>,
pub b_g2: Arc<Vec<E::G2Affine>>, pub b_g2: Arc<Vec<E::G2Affine>>
} }
impl<E: Engine> PartialEq for Parameters<E> { impl<E: Engine> PartialEq for Parameters<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.vk == other.vk self.vk == other.vk &&
&& self.h == other.h self.h == other.h &&
&& self.l == other.l self.l == other.l &&
&& self.a == other.a self.a == other.a &&
&& self.b_g1 == other.b_g1 self.b_g1 == other.b_g1 &&
&& self.b_g2 == other.b_g2 self.b_g2 == other.b_g2
} }
} }
impl<E: Engine> Parameters<E> { impl<E: Engine> Parameters<E> {
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
self.vk.write(&mut writer)?; self.vk.write(&mut writer)?;
writer.write_u32::<BigEndian>(self.h.len() as u32)?; writer.write_u32::<BigEndian>(self.h.len() as u32)?;
@@ -287,26 +284,27 @@ impl<E: Engine> Parameters<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> { pub fn read<R: Read>(
mut reader: R,
checked: bool
) -> io::Result<Self>
{
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> { let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty(); let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?; reader.read_exact(repr.as_mut())?;
if checked { if checked {
repr.into_affine() repr
.into_affine()
} else { } else {
repr.into_affine_unchecked() repr
.into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
}
}) })
}; };
@@ -315,20 +313,17 @@ impl<E: Engine> Parameters<E> {
reader.read_exact(repr.as_mut())?; reader.read_exact(repr.as_mut())?;
if checked { if checked {
repr.into_affine() repr
.into_affine()
} else { } else {
repr.into_affine_unchecked() repr
.into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
}
}) })
}; };
@@ -381,7 +376,7 @@ impl<E: Engine> Parameters<E> {
l: Arc::new(l), l: Arc::new(l),
a: Arc::new(a), a: Arc::new(a),
b_g1: Arc::new(b_g1), b_g1: Arc::new(b_g1),
b_g2: Arc::new(b_g2), b_g2: Arc::new(b_g2)
}) })
} }
} }
@@ -394,30 +389,39 @@ pub struct PreparedVerifyingKey<E: Engine> {
/// -delta in G2 /// -delta in G2
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared, neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
/// Copy of IC from `VerifiyingKey`. /// Copy of IC from `VerifiyingKey`.
ic: Vec<E::G1Affine>, ic: Vec<E::G1Affine>
} }
pub trait ParameterSource<E: Engine> { pub trait ParameterSource<E: Engine> {
type G1Builder: SourceBuilder<E::G1Affine>; type G1Builder: SourceBuilder<E::G1Affine>;
type G2Builder: SourceBuilder<E::G2Affine>; type G2Builder: SourceBuilder<E::G2Affine>;
fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, SynthesisError>; fn get_vk(
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, SynthesisError>; &mut self,
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, SynthesisError>; num_ic: usize
) -> Result<VerifyingKey<E>, SynthesisError>;
fn get_h(
&mut self,
num_h: usize
) -> Result<Self::G1Builder, SynthesisError>;
fn get_l(
&mut self,
num_l: usize
) -> Result<Self::G1Builder, SynthesisError>;
fn get_a( fn get_a(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize, num_aux: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g1( fn get_b_g1(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize, num_aux: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g2( fn get_b_g2(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize, num_aux: usize
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>; ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
} }
@@ -425,39 +429,54 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
type G1Builder = (Arc<Vec<E::G1Affine>>, usize); type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
type G2Builder = (Arc<Vec<E::G2Affine>>, usize); type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
fn get_vk(&mut self, _: usize) -> Result<VerifyingKey<E>, SynthesisError> { fn get_vk(
&mut self,
_: usize
) -> Result<VerifyingKey<E>, SynthesisError>
{
Ok(self.vk.clone()) Ok(self.vk.clone())
} }
fn get_h(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> { fn get_h(
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.h.clone(), 0)) Ok((self.h.clone(), 0))
} }
fn get_l(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> { fn get_l(
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.l.clone(), 0)) Ok((self.l.clone(), 0))
} }
fn get_a( fn get_a(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize, _: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> { ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
{
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
} }
fn get_b_g1( fn get_b_g1(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize, _: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> { ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
{
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
} }
fn get_b_g2( fn get_b_g2(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize, _: usize
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> { ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>
{
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
} }
} }
@@ -465,38 +484,41 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
#[cfg(test)] #[cfg(test)]
mod test_with_bls12_381 { mod test_with_bls12_381 {
use super::*; use super::*;
use {Circuit, ConstraintSystem, SynthesisError}; use {Circuit, SynthesisError, ConstraintSystem};
use ff::Field; use ff::Field;
use rand::{Rand, thread_rng};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand::thread_rng;
#[test] #[test]
fn serialization() { fn serialization() {
struct MySillyCircuit<E: Engine> { struct MySillyCircuit<E: Engine> {
a: Option<E::Fr>, a: Option<E::Fr>,
b: Option<E::Fr>, b: Option<E::Fr>
} }
impl<E: Engine> Circuit<E> for MySillyCircuit<E> { impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(
self, self,
cs: &mut CS, cs: &mut CS
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError>
{
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
let c = cs.alloc_input( let c = cs.alloc_input(|| "c", || {
|| "c",
|| {
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
a.mul_assign(&b); a.mul_assign(&b);
Ok(a) Ok(a)
}, })?;
)?;
cs.enforce(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c); cs.enforce(
|| "a*b=c",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
Ok(()) Ok(())
} }
@@ -504,9 +526,10 @@ mod test_with_bls12_381 {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
let params = let params = generate_random_parameters::<Bls12, _, _>(
generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng) MySillyCircuit { a: None, b: None },
.unwrap(); rng
).unwrap();
{ {
let mut v = vec![]; let mut v = vec![];
@@ -524,20 +547,19 @@ mod test_with_bls12_381 {
let pvk = prepare_verifying_key::<Bls12>(&params.vk); let pvk = prepare_verifying_key::<Bls12>(&params.vk);
for _ in 0..100 { for _ in 0..100 {
let a = Fr::random(rng); let a = Fr::rand(rng);
let b = Fr::random(rng); let b = Fr::rand(rng);
let mut c = a; let mut c = a;
c.mul_assign(&b); c.mul_assign(&b);
let proof = create_random_proof( let proof = create_random_proof(
MySillyCircuit { MySillyCircuit {
a: Some(a), a: Some(a),
b: Some(b), b: Some(b)
}, },
&params, &params,
rng, rng
) ).unwrap();
.unwrap();
let mut v = vec![]; let mut v = vec![];
proof.write(&mut v).unwrap(); proof.write(&mut v).unwrap();

View File

@@ -1,4 +1,4 @@
use rand_core::RngCore; use rand::Rng;
use std::sync::Arc; use std::sync::Arc;
@@ -8,23 +8,43 @@ use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use pairing::Engine; use pairing::Engine;
use super::{ParameterSource, Proof}; use super::{
ParameterSource,
Proof
};
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use ::{
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use domain::{EvaluationDomain, Scalar}; use ::domain::{
EvaluationDomain,
Scalar
};
use multiexp::{multiexp, DensityTracker, FullDensity}; use ::multiexp::{
DensityTracker,
FullDensity,
multiexp
};
use multicore::Worker; use ::multicore::{
Worker
};
fn eval<E: Engine>( fn eval<E: Engine>(
lc: &LinearCombination<E>, lc: &LinearCombination<E>,
mut input_density: Option<&mut DensityTracker>, mut input_density: Option<&mut DensityTracker>,
mut aux_density: Option<&mut DensityTracker>, mut aux_density: Option<&mut DensityTracker>,
input_assignment: &[E::Fr], input_assignment: &[E::Fr],
aux_assignment: &[E::Fr], aux_assignment: &[E::Fr]
) -> E::Fr { ) -> E::Fr
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(index, coeff) in lc.0.iter() { for &(index, coeff) in lc.0.iter() {
@@ -36,7 +56,7 @@ fn eval<E: Engine>(
if let Some(ref mut v) = input_density { if let Some(ref mut v) = input_density {
v.inc(i); v.inc(i);
} }
} },
Variable(Index::Aux(i)) => { Variable(Index::Aux(i)) => {
tmp = aux_assignment[i]; tmp = aux_assignment[i];
if let Some(ref mut v) = aux_density { if let Some(ref mut v) = aux_density {
@@ -69,17 +89,18 @@ struct ProvingAssignment<E: Engine> {
// Assignments of variables // Assignments of variables
input_assignment: Vec<E::Fr>, input_assignment: Vec<E::Fr>,
aux_assignment: Vec<E::Fr>, aux_assignment: Vec<E::Fr>
} }
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> { impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.aux_assignment.push(f()?); self.aux_assignment.push(f()?);
self.a_aux_density.add_element(); self.a_aux_density.add_element();
@@ -88,11 +109,12 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
} }
fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.input_assignment.push(f()?); self.input_assignment.push(f()?);
self.b_input_density.add_element(); self.b_input_density.add_element();
@@ -100,13 +122,17 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
Ok(Variable(Index::Input(self.input_assignment.len() - 1))) Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
} }
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, _: A,
AR: Into<String>, a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
let a = a(LinearCombination::zero()); let a = a(LinearCombination::zero());
let b = b(LinearCombination::zero()); let b = b(LinearCombination::zero());
@@ -120,14 +146,14 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
None, None,
Some(&mut self.a_aux_density), Some(&mut self.a_aux_density),
&self.input_assignment, &self.input_assignment,
&self.aux_assignment, &self.aux_assignment
))); )));
self.b.push(Scalar(eval( self.b.push(Scalar(eval(
&b, &b,
Some(&mut self.b_input_density), Some(&mut self.b_input_density),
Some(&mut self.b_aux_density), Some(&mut self.b_aux_density),
&self.input_assignment, &self.input_assignment,
&self.aux_assignment, &self.aux_assignment
))); )));
self.c.push(Scalar(eval( self.c.push(Scalar(eval(
&c, &c,
@@ -138,19 +164,18 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
None, None,
None, None,
&self.input_assignment, &self.input_assignment,
&self.aux_assignment, &self.aux_assignment
))); )));
} }
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where where NR: Into<String>, N: FnOnce() -> NR
NR: Into<String>,
N: FnOnce() -> NR,
{ {
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
fn pop_namespace(&mut self) { fn pop_namespace(&mut self)
{
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
@@ -162,15 +187,12 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>( pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
circuit: C, circuit: C,
params: P, params: P,
rng: &mut R, rng: &mut R
) -> Result<Proof<E>, SynthesisError> ) -> Result<Proof<E>, SynthesisError>
where where E: Engine, C: Circuit<E>, R: Rng
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let r = E::Fr::random(rng); let r = rng.gen();
let s = E::Fr::random(rng); let s = rng.gen();
create_proof::<E, C, P>(circuit, params, r, s) create_proof::<E, C, P>(circuit, params, r, s)
} }
@@ -179,11 +201,9 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit: C, circuit: C,
mut params: P, mut params: P,
r: E::Fr, r: E::Fr,
s: E::Fr, s: E::Fr
) -> Result<Proof<E>, SynthesisError> ) -> Result<Proof<E>, SynthesisError>
where where E: Engine, C: Circuit<E>
E: Engine,
C: Circuit<E>,
{ {
let mut prover = ProvingAssignment { let mut prover = ProvingAssignment {
a_aux_density: DensityTracker::new(), a_aux_density: DensityTracker::new(),
@@ -193,7 +213,7 @@ where
b: vec![], b: vec![],
c: vec![], c: vec![],
input_assignment: vec![], input_assignment: vec![],
aux_assignment: vec![], aux_assignment: vec![]
}; };
prover.alloc_input(|| "", || Ok(E::Fr::one()))?; prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
@@ -201,7 +221,11 @@ where
circuit.synthesize(&mut prover)?; circuit.synthesize(&mut prover)?;
for i in 0..prover.input_assignment.len() { for i in 0..prover.input_assignment.len() {
prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc); prover.enforce(|| "",
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
} }
let worker = Worker::new(); let worker = Worker::new();
@@ -235,76 +259,31 @@ where
}; };
// TODO: parallelize if it's even helpful // TODO: parallelize if it's even helpful
let input_assignment = Arc::new( let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
prover let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
.input_assignment
.into_iter()
.map(|s| s.into_repr())
.collect::<Vec<_>>(),
);
let aux_assignment = Arc::new(
prover
.aux_assignment
.into_iter()
.map(|s| s.into_repr())
.collect::<Vec<_>>(),
);
let l = multiexp( let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone());
&worker,
params.get_l(aux_assignment.len())?,
FullDensity,
aux_assignment.clone(),
);
let a_aux_density_total = prover.a_aux_density.get_total_density(); let a_aux_density_total = prover.a_aux_density.get_total_density();
let (a_inputs_source, a_aux_source) = let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?;
params.get_a(input_assignment.len(), a_aux_density_total)?;
let a_inputs = multiexp( let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone());
&worker, let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone());
a_inputs_source,
FullDensity,
input_assignment.clone(),
);
let a_aux = multiexp(
&worker,
a_aux_source,
Arc::new(prover.a_aux_density),
aux_assignment.clone(),
);
let b_input_density = Arc::new(prover.b_input_density); let b_input_density = Arc::new(prover.b_input_density);
let b_input_density_total = b_input_density.get_total_density(); let b_input_density_total = b_input_density.get_total_density();
let b_aux_density = Arc::new(prover.b_aux_density); let b_aux_density = Arc::new(prover.b_aux_density);
let b_aux_density_total = b_aux_density.get_total_density(); let b_aux_density_total = b_aux_density.get_total_density();
let (b_g1_inputs_source, b_g1_aux_source) = let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?;
params.get_b_g1(b_input_density_total, b_aux_density_total)?;
let b_g1_inputs = multiexp( let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone());
&worker, let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone());
b_g1_inputs_source,
b_input_density.clone(),
input_assignment.clone(),
);
let b_g1_aux = multiexp(
&worker,
b_g1_aux_source,
b_aux_density.clone(),
aux_assignment.clone(),
);
let (b_g2_inputs_source, b_g2_aux_source) = let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?;
params.get_b_g2(b_input_density_total, b_aux_density_total)?;
let b_g2_inputs = multiexp( let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment);
&worker,
b_g2_inputs_source,
b_input_density,
input_assignment,
);
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() { if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
@@ -346,6 +325,6 @@ where
Ok(Proof { Ok(Proof {
a: g_a.into_affine(), a: g_a.into_affine(),
b: g_b.into_affine(), b: g_b.into_affine(),
c: g_c.into_affine(), c: g_c.into_affine()
}) })
} }

View File

@@ -1,13 +1,12 @@
use ff::{ use ff::{
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError,
SqrtField, PrimeFieldRepr, ScalarEngine, SqrtField};
};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
use rand_core::RngCore;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use rand::{Rand, Rng};
use std::num::Wrapping; use std::num::Wrapping;
const MODULUS_R: Wrapping<u32> = Wrapping(64513); const MODULUS_R: Wrapping<u32> = Wrapping(64513);
@@ -21,11 +20,13 @@ impl fmt::Display for Fr {
} }
} }
impl Field for Fr { impl Rand for Fr {
fn random<R: RngCore>(rng: &mut R) -> Self { fn rand<R: Rng>(rng: &mut R) -> Self {
Fr(Wrapping(rng.next_u32()) % MODULUS_R) Fr(Wrapping(rng.gen()) % MODULUS_R)
}
} }
impl Field for Fr {
fn zero() -> Self { fn zero() -> Self {
Fr(Wrapping(0)) Fr(Wrapping(0))
} }
@@ -81,13 +82,9 @@ impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol { fn legendre(&self) -> LegendreSymbol {
// s = self^((r - 1) // 2) // s = self^((r - 1) // 2)
let s = self.pow([32256]); let s = self.pow([32256]);
if s == <Fr as Field>::zero() { if s == <Fr as Field>::zero() { LegendreSymbol::Zero }
LegendreSymbol::Zero else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue }
} else if s == <Fr as Field>::one() { else { LegendreSymbol::QuadraticNonResidue }
LegendreSymbol::QuadraticResidue
} else {
LegendreSymbol::QuadraticNonResidue
}
} }
fn sqrt(&self) -> Option<Self> { fn sqrt(&self) -> Option<Self> {
@@ -148,6 +145,12 @@ impl PartialOrd for FrRepr {
} }
} }
impl Rand for FrRepr {
fn rand<R: Rng>(rng: &mut R) -> Self {
FrRepr([rng.gen()])
}
}
impl fmt::Display for FrRepr { impl fmt::Display for FrRepr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0)[0]) write!(f, "{}", (self.0)[0])
@@ -268,13 +271,10 @@ impl Engine for DummyEngine {
type Fqk = Fr; type Fqk = Fr;
fn miller_loop<'a, I>(i: I) -> Self::Fqk fn miller_loop<'a, I>(i: I) -> Self::Fqk
where where I: IntoIterator<Item=&'a (
I: IntoIterator<
Item = &'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared, &'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared, &'a <Self::G2Affine as PairingCurveAffine>::Prepared
), )>
>,
{ {
let mut acc = <Fr as Field>::zero(); let mut acc = <Fr as Field>::zero();
@@ -288,7 +288,8 @@ impl Engine for DummyEngine {
} }
/// Perform final exponentiation of the result of a miller loop. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> { fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk>
{
Some(*this) Some(*this)
} }
} }
@@ -299,10 +300,6 @@ impl CurveProjective for Fr {
type Scalar = Fr; type Scalar = Fr;
type Engine = DummyEngine; type Engine = DummyEngine;
fn random<R: RngCore>(rng: &mut R) -> Self {
<Fr as Field>::random(rng)
}
fn zero() -> Self { fn zero() -> Self {
<Fr as Field>::zero() <Fr as Field>::zero()
} }
@@ -315,7 +312,9 @@ impl CurveProjective for Fr {
<Fr as Field>::is_zero(self) <Fr as Field>::is_zero(self)
} }
fn batch_normalization(_: &mut [Self]) {} fn batch_normalization(_: &mut [Self]) {
}
fn is_normalized(&self) -> bool { fn is_normalized(&self) -> bool {
true true
@@ -337,7 +336,8 @@ impl CurveProjective for Fr {
<Fr as Field>::negate(self); <Fr as Field>::negate(self);
} }
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) { fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S)
{
let tmp = Fr::from_repr(other.into()).unwrap(); let tmp = Fr::from_repr(other.into()).unwrap();
<Fr as Field>::mul_assign(self, &tmp); <Fr as Field>::mul_assign(self, &tmp);
@@ -419,7 +419,8 @@ impl CurveAffine for Fr {
<Fr as Field>::negate(self); <Fr as Field>::negate(self);
} }
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective { fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective
{
let mut res = *self; let mut res = *self;
let tmp = Fr::from_repr(other.into()).unwrap(); let tmp = Fr::from_repr(other.into()).unwrap();

View File

@@ -6,21 +6,32 @@ use self::dummy_engine::*;
use std::marker::PhantomData; use std::marker::PhantomData;
use {Circuit, ConstraintSystem, SynthesisError}; use ::{
Circuit,
ConstraintSystem,
SynthesisError
};
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof}; use super::{
generate_parameters,
prepare_verifying_key,
create_proof,
verify_proof
};
struct XORDemo<E: Engine> { struct XORDemo<E: Engine> {
a: Option<bool>, a: Option<bool>,
b: Option<bool>, b: Option<bool>,
_marker: PhantomData<E>, _marker: PhantomData<E>
} }
impl<E: Engine> Circuit<E> for XORDemo<E> { impl<E: Engine> Circuit<E> for XORDemo<E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(
let a_var = cs.alloc( self,
|| "a", cs: &mut CS
|| { ) -> Result<(), SynthesisError>
{
let a_var = cs.alloc(|| "a", || {
if self.a.is_some() { if self.a.is_some() {
if self.a.unwrap() { if self.a.unwrap() {
Ok(E::Fr::one()) Ok(E::Fr::one())
@@ -30,19 +41,16 @@ impl<E: Engine> Circuit<E> for XORDemo<E> {
} else { } else {
Err(SynthesisError::AssignmentMissing) Err(SynthesisError::AssignmentMissing)
} }
}, })?;
)?;
cs.enforce( cs.enforce(
|| "a_boolean_constraint", || "a_boolean_constraint",
|lc| lc + CS::one() - a_var, |lc| lc + CS::one() - a_var,
|lc| lc + a_var, |lc| lc + a_var,
|lc| lc, |lc| lc
); );
let b_var = cs.alloc( let b_var = cs.alloc(|| "b", || {
|| "b",
|| {
if self.b.is_some() { if self.b.is_some() {
if self.b.unwrap() { if self.b.unwrap() {
Ok(E::Fr::one()) Ok(E::Fr::one())
@@ -52,19 +60,16 @@ impl<E: Engine> Circuit<E> for XORDemo<E> {
} else { } else {
Err(SynthesisError::AssignmentMissing) Err(SynthesisError::AssignmentMissing)
} }
}, })?;
)?;
cs.enforce( cs.enforce(
|| "b_boolean_constraint", || "b_boolean_constraint",
|lc| lc + CS::one() - b_var, |lc| lc + CS::one() - b_var,
|lc| lc + b_var, |lc| lc + b_var,
|lc| lc, |lc| lc
); );
let c_var = cs.alloc_input( let c_var = cs.alloc_input(|| "c", || {
|| "c",
|| {
if self.a.is_some() && self.b.is_some() { if self.a.is_some() && self.b.is_some() {
if self.a.unwrap() ^ self.b.unwrap() { if self.a.unwrap() ^ self.b.unwrap() {
Ok(E::Fr::one()) Ok(E::Fr::one())
@@ -74,14 +79,13 @@ impl<E: Engine> Circuit<E> for XORDemo<E> {
} else { } else {
Err(SynthesisError::AssignmentMissing) Err(SynthesisError::AssignmentMissing)
} }
}, })?;
)?;
cs.enforce( cs.enforce(
|| "c_xor_constraint", || "c_xor_constraint",
|lc| lc + a_var + a_var, |lc| lc + a_var + a_var,
|lc| lc + b_var, |lc| lc + b_var,
|lc| lc + a_var + b_var - c_var, |lc| lc + a_var + b_var - c_var
); );
Ok(()) Ok(())
@@ -102,10 +106,19 @@ fn test_xordemo() {
let c = XORDemo::<DummyEngine> { let c = XORDemo::<DummyEngine> {
a: None, a: None,
b: None, b: None,
_marker: PhantomData, _marker: PhantomData
}; };
generate_parameters(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap() generate_parameters(
c,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
).unwrap()
}; };
// This will synthesize the constraint system: // This will synthesize the constraint system:
@@ -213,33 +226,30 @@ fn test_xordemo() {
59158 59158
*/ */
let u_i = [59158, 48317, 21767, 10402] let u_i = [59158, 48317, 21767, 10402].iter().map(|e| {
.iter() Fr::from_str(&format!("{}", e)).unwrap()
.map(|e| Fr::from_str(&format!("{}", e)).unwrap()) }).collect::<Vec<Fr>>();
.collect::<Vec<Fr>>(); let v_i = [0, 0, 60619, 30791].iter().map(|e| {
let v_i = [0, 0, 60619, 30791] Fr::from_str(&format!("{}", e)).unwrap()
.iter() }).collect::<Vec<Fr>>();
.map(|e| Fr::from_str(&format!("{}", e)).unwrap()) let w_i = [0, 23320, 41193, 41193].iter().map(|e| {
.collect::<Vec<Fr>>(); Fr::from_str(&format!("{}", e)).unwrap()
let w_i = [0, 23320, 41193, 41193] }).collect::<Vec<Fr>>();
.iter()
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
.collect::<Vec<Fr>>();
for (u, a) in u_i.iter().zip(&params.a[..]) { for (u, a) in u_i.iter()
.zip(&params.a[..])
{
assert_eq!(u, a); assert_eq!(u, a);
} }
for (v, b) in v_i for (v, b) in v_i.iter()
.iter()
.filter(|&&e| e != Fr::zero()) .filter(|&&e| e != Fr::zero())
.zip(&params.b_g1[..]) .zip(&params.b_g1[..])
{ {
assert_eq!(v, b); assert_eq!(v, b);
} }
for (v, b) in v_i for (v, b) in v_i.iter()
.iter()
.filter(|&&e| e != Fr::zero()) .filter(|&&e| e != Fr::zero())
.zip(&params.b_g2[..]) .zip(&params.b_g2[..])
{ {
@@ -286,10 +296,15 @@ fn test_xordemo() {
let c = XORDemo { let c = XORDemo {
a: Some(true), a: Some(true),
b: Some(false), b: Some(false),
_marker: PhantomData, _marker: PhantomData
}; };
create_proof(c, &params, r, s).unwrap() create_proof(
c,
&params,
r,
s
).unwrap()
}; };
// A(x) = // A(x) =
@@ -363,10 +378,7 @@ fn test_xordemo() {
expected_c.add_assign(&params.l[0]); expected_c.add_assign(&params.l[0]);
// H query answer // H query answer
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739] for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() {
.iter()
.enumerate()
{
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap(); let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
let mut tmp = params.h[i]; let mut tmp = params.h[i];
@@ -377,5 +389,9 @@ fn test_xordemo() {
assert_eq!(expected_c, proof.c); assert_eq!(expected_c, proof.c);
} }
assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap()); assert!(verify_proof(
&pvk,
&proof,
&[Fr::one()]
).unwrap());
} }

View File

@@ -2,11 +2,20 @@ use ff::PrimeField;
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
use super::{PreparedVerifyingKey, Proof, VerifyingKey}; use super::{
Proof,
VerifyingKey,
PreparedVerifyingKey
};
use SynthesisError; use ::{
SynthesisError
};
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> { pub fn prepare_verifying_key<E: Engine>(
vk: &VerifyingKey<E>
) -> PreparedVerifyingKey<E>
{
let mut gamma = vk.gamma_g2; let mut gamma = vk.gamma_g2;
gamma.negate(); gamma.negate();
let mut delta = vk.delta_g2; let mut delta = vk.delta_g2;
@@ -16,15 +25,16 @@ pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyi
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
neg_gamma_g2: gamma.prepare(), neg_gamma_g2: gamma.prepare(),
neg_delta_g2: delta.prepare(), neg_delta_g2: delta.prepare(),
ic: vk.ic.clone(), ic: vk.ic.clone()
} }
} }
pub fn verify_proof<'a, E: Engine>( pub fn verify_proof<'a, E: Engine>(
pvk: &'a PreparedVerifyingKey<E>, pvk: &'a PreparedVerifyingKey<E>,
proof: &Proof<E>, proof: &Proof<E>,
public_inputs: &[E::Fr], public_inputs: &[E::Fr]
) -> Result<bool, SynthesisError> { ) -> Result<bool, SynthesisError>
{
if (public_inputs.len() + 1) != pvk.ic.len() { if (public_inputs.len() + 1) != pvk.ic.len() {
return Err(SynthesisError::MalformedVerifyingKey); return Err(SynthesisError::MalformedVerifyingKey);
} }
@@ -43,14 +53,11 @@ pub fn verify_proof<'a, E: Engine>(
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
// which allows us to do a single final exponentiation. // which allows us to do a single final exponentiation.
Ok(E::final_exponentiation(&E::miller_loop( Ok(E::final_exponentiation(
[ &E::miller_loop([
(&proof.a.prepare(), &proof.b.prepare()), (&proof.a.prepare(), &proof.b.prepare()),
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2), (&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
(&proof.c.prepare(), &pvk.neg_delta_g2), (&proof.c.prepare(), &pvk.neg_delta_g2)
] ].into_iter())
.into_iter(), ).unwrap() == pvk.alpha_g1_beta_g2)
))
.unwrap()
== pvk.alpha_g1_beta_g2)
} }

View File

@@ -2,12 +2,11 @@ extern crate ff;
extern crate group; extern crate group;
#[cfg(feature = "pairing")] #[cfg(feature = "pairing")]
extern crate pairing; extern crate pairing;
extern crate rand_core; extern crate rand;
extern crate bit_vec;
extern crate blake2s_simd;
extern crate byteorder;
extern crate futures; extern crate futures;
extern crate bit_vec;
extern crate byteorder;
#[cfg(feature = "multicore")] #[cfg(feature = "multicore")]
extern crate crossbeam; extern crate crossbeam;
@@ -16,33 +15,19 @@ extern crate futures_cpupool;
#[cfg(feature = "multicore")] #[cfg(feature = "multicore")]
extern crate num_cpus; extern crate num_cpus;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[cfg(test)]
extern crate rand;
#[cfg(test)]
extern crate rand_xorshift;
#[cfg(test)]
extern crate sha2;
pub mod domain;
pub mod gadgets;
#[cfg(feature = "groth16")]
pub mod groth16;
pub mod multicore; pub mod multicore;
mod multiexp; mod multiexp;
pub mod domain;
#[cfg(feature = "groth16")]
pub mod groth16;
use ff::{Field, ScalarEngine}; use ff::{Field, ScalarEngine};
use std::error::Error; use std::ops::{Add, Sub};
use std::fmt; use std::fmt;
use std::error::Error;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{Add, Sub};
/// Computations are expressed in terms of arithmetic circuits, in particular /// Computations are expressed in terms of arithmetic circuits, in particular
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a /// rank-1 quadratic constraint systems. The `Circuit` trait represents a
@@ -50,7 +35,10 @@ use std::ops::{Add, Sub};
/// CRS generation and during proving. /// CRS generation and during proving.
pub trait Circuit<E: ScalarEngine> { pub trait Circuit<E: ScalarEngine> {
/// Synthesize the circuit into a rank-1 quadratic constraint system /// Synthesize the circuit into a rank-1 quadratic constraint system
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>; fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>;
} }
/// Represents a variable in our constraint system. /// Represents a variable in our constraint system.
@@ -76,7 +64,7 @@ impl Variable {
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum Index { pub enum Index {
Input(usize), Input(usize),
Aux(usize), Aux(usize)
} }
/// This represents a linear combination of some variables, with coefficients /// This represents a linear combination of some variables, with coefficients
@@ -203,7 +191,7 @@ pub enum SynthesisError {
/// During verification, our verifying key was malformed. /// During verification, our verifying key was malformed.
MalformedVerifyingKey, MalformedVerifyingKey,
/// During CRS generation, we observed an unconstrained auxiliary variable /// During CRS generation, we observed an unconstrained auxiliary variable
UnconstrainedVariable, UnconstrainedVariable
} }
impl From<io::Error> for SynthesisError { impl From<io::Error> for SynthesisError {
@@ -215,16 +203,14 @@ impl From<io::Error> for SynthesisError {
impl Error for SynthesisError { impl Error for SynthesisError {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
SynthesisError::AssignmentMissing => { SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed",
"an assignment for a variable could not be computed"
}
SynthesisError::DivisionByZero => "division by zero", SynthesisError::DivisionByZero => "division by zero",
SynthesisError::Unsatisfiable => "unsatisfiable constraint system", SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
SynthesisError::IoError(_) => "encountered an I/O error", SynthesisError::IoError(_) => "encountered an I/O error",
SynthesisError::MalformedVerifyingKey => "malformed verifying key", SynthesisError::MalformedVerifyingKey => "malformed verifying key",
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained", SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained"
} }
} }
} }
@@ -256,26 +242,32 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// determine the assignment of the variable. The given `annotation` function is invoked /// determine the assignment of the variable. The given `annotation` function is invoked
/// in testing contexts in order to derive a unique name for this variable in the current /// in testing contexts in order to derive a unique name for this variable in the current
/// namespace. /// namespace.
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>; ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
/// Allocate a public variable in the constraint system. The provided function is used to /// Allocate a public variable in the constraint system. The provided function is used to
/// determine the assignment of the variable. /// determine the assignment of the variable.
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>; ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
/// in order to derive a unique name for the constraint in the current namespace. /// in order to derive a unique name for the constraint in the current namespace.
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>; LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
@@ -283,9 +275,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// Create a new (sub)namespace and enter into it. Not intended /// Create a new (sub)namespace and enter into it. Not intended
/// for downstream use; use `namespace` instead. /// for downstream use; use `namespace` instead.
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where where NR: Into<String>, N: FnOnce() -> NR;
NR: Into<String>,
N: FnOnce() -> NR;
/// Exit out of the existing namespace. Not intended for /// Exit out of the existing namespace. Not intended for
/// downstream use; use `namespace` instead. /// downstream use; use `namespace` instead.
@@ -296,10 +286,11 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
fn get_root(&mut self) -> &mut Self::Root; fn get_root(&mut self) -> &mut Self::Root;
/// Begin a namespace for this constraint system. /// Begin a namespace for this constraint system.
fn namespace<'a, NR, N>(&'a mut self, name_fn: N) -> Namespace<'a, E, Self::Root> fn namespace<'a, NR, N>(
where &'a mut self,
NR: Into<String>, name_fn: N
N: FnOnce() -> NR, ) -> Namespace<'a, E, Self::Root>
where NR: Into<String>, N: FnOnce() -> NR
{ {
self.get_root().push_namespace(name_fn); self.get_root().push_namespace(name_fn);
@@ -318,31 +309,37 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
CS::one() CS::one()
} }
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.0.alloc(annotation, f) self.0.alloc(annotation, f)
} }
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.0.alloc_input(annotation, f) self.0.alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
self.0.enforce(annotation, a, b, c) self.0.enforce(annotation, a, b, c)
} }
@@ -352,18 +349,18 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
// never a root constraint system. // never a root constraint system.
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where where NR: Into<String>, N: FnOnce() -> NR
NR: Into<String>,
N: FnOnce() -> NR,
{ {
panic!("only the root's push_namespace should be called"); panic!("only the root's push_namespace should be called");
} }
fn pop_namespace(&mut self) { fn pop_namespace(&mut self)
{
panic!("only the root's pop_namespace should be called"); panic!("only the root's pop_namespace should be called");
} }
fn get_root(&mut self) -> &mut Self::Root { fn get_root(&mut self) -> &mut Self::Root
{
self.0.get_root() self.0.get_root()
} }
} }
@@ -383,48 +380,54 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs
CS::one() CS::one()
} }
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
(**self).alloc(annotation, f) (**self).alloc(annotation, f)
} }
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
(**self).alloc_input(annotation, f) (**self).alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
(**self).enforce(annotation, a, b, c) (**self).enforce(annotation, a, b, c)
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where where NR: Into<String>, N: FnOnce() -> NR
NR: Into<String>,
N: FnOnce() -> NR,
{ {
(**self).push_namespace(name_fn) (**self).push_namespace(name_fn)
} }
fn pop_namespace(&mut self) { fn pop_namespace(&mut self)
{
(**self).pop_namespace() (**self).pop_namespace()
} }
fn get_root(&mut self) -> &mut Self::Root { fn get_root(&mut self) -> &mut Self::Root
{
(**self).get_root() (**self).get_root()
} }
} }

View File

@@ -6,15 +6,15 @@
#[cfg(feature = "multicore")] #[cfg(feature = "multicore")]
mod implementation { mod implementation {
use crossbeam::{self, Scope};
use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuFuture, CpuPool};
use num_cpus; use num_cpus;
use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuPool, CpuFuture};
use crossbeam::{self, Scope};
#[derive(Clone)] #[derive(Clone)]
pub struct Worker { pub struct Worker {
cpus: usize, cpus: usize,
pool: CpuPool, pool: CpuPool
} }
impl Worker { impl Worker {
@@ -24,7 +24,7 @@ mod implementation {
pub(crate) fn new_with_cpus(cpus: usize) -> Worker { pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
Worker { Worker {
cpus: cpus, cpus: cpus,
pool: CpuPool::new(cpus), pool: CpuPool::new(cpus)
} }
} }
@@ -36,22 +36,26 @@ mod implementation {
log2_floor(self.cpus) log2_floor(self.cpus)
} }
pub fn compute<F, R>(&self, f: F) -> WorkerFuture<R::Item, R::Error> pub fn compute<F, R>(
where &self, f: F
F: FnOnce() -> R + Send + 'static, ) -> WorkerFuture<R::Item, R::Error>
where F: FnOnce() -> R + Send + 'static,
R: IntoFuture + 'static, R: IntoFuture + 'static,
R::Future: Send + 'static, R::Future: Send + 'static,
R::Item: Send + 'static, R::Item: Send + 'static,
R::Error: Send + 'static, R::Error: Send + 'static
{ {
WorkerFuture { WorkerFuture {
future: self.pool.spawn_fn(f), future: self.pool.spawn_fn(f)
} }
} }
pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R pub fn scope<'a, F, R>(
where &self,
F: FnOnce(&Scope<'a>, usize) -> R, elements: usize,
f: F
) -> R
where F: FnOnce(&Scope<'a>, usize) -> R
{ {
let chunk_size = if elements < self.cpus { let chunk_size = if elements < self.cpus {
1 1
@@ -59,19 +63,22 @@ mod implementation {
elements / self.cpus elements / self.cpus
}; };
crossbeam::scope(|scope| f(scope, chunk_size)) crossbeam::scope(|scope| {
f(scope, chunk_size)
})
} }
} }
pub struct WorkerFuture<T, E> { pub struct WorkerFuture<T, E> {
future: CpuFuture<T, E>, future: CpuFuture<T, E>
} }
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> { impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
type Item = T; type Item = T;
type Error = E; type Error = E;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error>
{
self.future.poll() self.future.poll()
} }
} }

View File

@@ -1,11 +1,11 @@
use super::multicore::Worker;
use bit_vec::{self, BitVec};
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use futures::Future;
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use std::io;
use std::iter;
use std::sync::Arc; use std::sync::Arc;
use std::io;
use bit_vec::{self, BitVec};
use std::iter;
use futures::{Future};
use super::multicore::Worker;
use super::SynthesisError; use super::SynthesisError;
@@ -19,10 +19,7 @@ pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
/// A source of bases, like an iterator. /// A source of bases, like an iterator.
pub trait Source<G: CurveAffine> { pub trait Source<G: CurveAffine> {
/// Parses the element from the source. Fails if the point is at infinity. /// Parses the element from the source. Fails if the point is at infinity.
fn add_assign_mixed( fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError>;
&mut self,
to: &mut <G as CurveAffine>::Projective,
) -> Result<(), SynthesisError>;
/// Skips `amt` elements from the source, avoiding deserialization. /// Skips `amt` elements from the source, avoiding deserialization.
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
@@ -37,20 +34,13 @@ impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
} }
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) { impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
fn add_assign_mixed( fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> {
&mut self,
to: &mut <G as CurveAffine>::Projective,
) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 { if self.0.len() <= self.1 {
return Err(io::Error::new( return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
io::ErrorKind::UnexpectedEof,
"expected more bases from source",
)
.into());
} }
if self.0[self.1].is_zero() { if self.0[self.1].is_zero() {
return Err(SynthesisError::UnexpectedIdentity); return Err(SynthesisError::UnexpectedIdentity)
} }
to.add_assign_mixed(&self.0[self.1]); to.add_assign_mixed(&self.0[self.1]);
@@ -62,11 +52,7 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 { if self.0.len() <= self.1 {
return Err(io::Error::new( return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
io::ErrorKind::UnexpectedEof,
"expected more bases from source",
)
.into());
} }
self.1 += amt; self.1 += amt;
@@ -106,7 +92,7 @@ impl<'a> QueryDensity for &'a FullDensity {
pub struct DensityTracker { pub struct DensityTracker {
bv: BitVec, bv: BitVec,
total_density: usize, total_density: usize
} }
impl<'a> QueryDensity for &'a DensityTracker { impl<'a> QueryDensity for &'a DensityTracker {
@@ -125,7 +111,7 @@ impl DensityTracker {
pub fn new() -> DensityTracker { pub fn new() -> DensityTracker {
DensityTracker { DensityTracker {
bv: BitVec::new(), bv: BitVec::new(),
total_density: 0, total_density: 0
} }
} }
@@ -152,13 +138,12 @@ fn multiexp_inner<Q, D, G, S>(
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>, exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
mut skip: u32, mut skip: u32,
c: u32, c: u32,
handle_trivial: bool, handle_trivial: bool
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>> ) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where where for<'a> &'a Q: QueryDensity,
for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>, D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine, G: CurveAffine,
S: SourceBuilder<G>, S: SourceBuilder<G>
{ {
// Perform this region of the multiexp // Perform this region of the multiexp
let this = { let this = {
@@ -227,15 +212,7 @@ where
// There's another region more significant. Calculate and join it with // There's another region more significant. Calculate and join it with
// this region recursively. // this region recursively.
Box::new( Box::new(
this.join(multiexp_inner( this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false))
pool,
bases,
density_map,
exponents,
skip,
c,
false,
))
.map(move |(this, mut higher)| { .map(move |(this, mut higher)| {
for _ in 0..c { for _ in 0..c {
higher.double(); higher.double();
@@ -244,7 +221,7 @@ where
higher.add_assign(&this); higher.add_assign(&this);
higher higher
}), })
) )
} }
} }
@@ -255,13 +232,12 @@ pub fn multiexp<Q, D, G, S>(
pool: &Worker, pool: &Worker,
bases: S, bases: S,
density_map: D, density_map: D,
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>, exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>> ) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where where for<'a> &'a Q: QueryDensity,
for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>, D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine, G: CurveAffine,
S: SourceBuilder<G>, S: SourceBuilder<G>
{ {
let c = if exponents.len() < 32 { let c = if exponents.len() < 32 {
3u32 3u32
@@ -284,8 +260,9 @@ where
fn test_with_bls12() { fn test_with_bls12() {
fn naive_multiexp<G: CurveAffine>( fn naive_multiexp<G: CurveAffine>(
bases: Arc<Vec<G>>, bases: Arc<Vec<G>>,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>, exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>
) -> G::Projective { ) -> G::Projective
{
assert_eq!(bases.len(), exponents.len()); assert_eq!(bases.len(), exponents.len());
let mut acc = G::Projective::zero(); let mut acc = G::Projective::zero();
@@ -297,28 +274,25 @@ fn test_with_bls12() {
acc acc
} }
use rand::{self, Rand};
use pairing::{bls12_381::Bls12, Engine}; use pairing::{bls12_381::Bls12, Engine};
use rand;
const SAMPLES: usize = 1 << 14; const SAMPLES: usize = 1 << 14;
let rng = &mut rand::thread_rng(); let rng = &mut rand::thread_rng();
let v = Arc::new( let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as ScalarEngine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
(0..SAMPLES) let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>());
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng).into_repr())
.collect::<Vec<_>>(),
);
let g = Arc::new(
(0..SAMPLES)
.map(|_| <Bls12 as Engine>::G1::random(rng).into_affine())
.collect::<Vec<_>>(),
);
let naive = naive_multiexp(g.clone(), v.clone()); let naive = naive_multiexp(g.clone(), v.clone());
let pool = Worker::new(); let pool = Worker::new();
let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap(); let fast = multiexp(
&pool,
(g, 0),
FullDensity,
v
).wait().unwrap();
assert_eq!(naive, fast); assert_eq!(naive, fast);
} }

View File

@@ -4,24 +4,34 @@ extern crate pairing;
extern crate rand; extern crate rand;
// For randomness (during paramgen and proof generation) // For randomness (during paramgen and proof generation)
use rand::thread_rng; use rand::{thread_rng, Rng};
// For benchmarking // For benchmarking
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
// Bring in some tools for using pairing-friendly curves // Bring in some tools for using pairing-friendly curves
use ff::{Field, ScalarEngine}; use ff::Field;
use pairing::Engine; use pairing::Engine;
// We're going to use the BLS12-381 pairing-friendly elliptic curve. // We're going to use the BLS12-381 pairing-friendly elliptic curve.
use pairing::bls12_381::Bls12; use pairing::bls12_381::{
Bls12
};
// We'll use these interfaces to construct our circuit. // We'll use these interfaces to construct our circuit.
use bellman::{Circuit, ConstraintSystem, SynthesisError}; use bellman::{
Circuit,
ConstraintSystem,
SynthesisError
};
// We're going to use the Groth16 proving system. // We're going to use the Groth16 proving system.
use bellman::groth16::{ use bellman::groth16::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof, Proof,
generate_random_parameters,
prepare_verifying_key,
create_random_proof,
verify_proof,
}; };
const MIMC_ROUNDS: usize = 322; const MIMC_ROUNDS: usize = 322;
@@ -39,7 +49,12 @@ const MIMC_ROUNDS: usize = 322;
/// return xL /// return xL
/// } /// }
/// ``` /// ```
fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr { fn mimc<E: Engine>(
mut xl: E::Fr,
mut xr: E::Fr,
constants: &[E::Fr]
) -> E::Fr
{
assert_eq!(constants.len(), MIMC_ROUNDS); assert_eq!(constants.len(), MIMC_ROUNDS);
for i in 0..MIMC_ROUNDS { for i in 0..MIMC_ROUNDS {
@@ -61,29 +76,31 @@ fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
struct MiMCDemo<'a, E: Engine> { struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>, xl: Option<E::Fr>,
xr: Option<E::Fr>, xr: Option<E::Fr>,
constants: &'a [E::Fr], constants: &'a [E::Fr]
} }
/// Our demo circuit implements this `Circuit` trait which /// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to /// is used during paramgen and proving in order to
/// synthesize the constraint system. /// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> { impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.constants.len(), MIMC_ROUNDS); assert_eq!(self.constants.len(), MIMC_ROUNDS);
// Allocate the first component of the preimage. // Allocate the first component of the preimage.
let mut xl_value = self.xl; let mut xl_value = self.xl;
let mut xl = cs.alloc( let mut xl = cs.alloc(|| "preimage xl", || {
|| "preimage xl", xl_value.ok_or(SynthesisError::AssignmentMissing)
|| xl_value.ok_or(SynthesisError::AssignmentMissing), })?;
)?;
// Allocate the second component of the preimage. // Allocate the second component of the preimage.
let mut xr_value = self.xr; let mut xr_value = self.xr;
let mut xr = cs.alloc( let mut xr = cs.alloc(|| "preimage xr", || {
|| "preimage xr", xr_value.ok_or(SynthesisError::AssignmentMissing)
|| xr_value.ok_or(SynthesisError::AssignmentMissing), })?;
)?;
for i in 0..MIMC_ROUNDS { for i in 0..MIMC_ROUNDS {
// xL, xR := xR + (xL + Ci)^3, xL // xL, xR := xR + (xL + Ci)^3, xL
@@ -95,16 +112,15 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
e.square(); e.square();
e e
}); });
let mut tmp = cs.alloc( let mut tmp = cs.alloc(|| "tmp", || {
|| "tmp", tmp_value.ok_or(SynthesisError::AssignmentMissing)
|| tmp_value.ok_or(SynthesisError::AssignmentMissing), })?;
)?;
cs.enforce( cs.enforce(
|| "tmp = (xL + Ci)^2", || "tmp = (xL + Ci)^2",
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + tmp, |lc| lc + tmp
); );
// new_xL = xR + (xL + Ci)^3 // new_xL = xR + (xL + Ci)^3
@@ -120,22 +136,20 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
let mut new_xl = if i == (MIMC_ROUNDS-1) { let mut new_xl = if i == (MIMC_ROUNDS-1) {
// This is the last round, xL is our image and so // This is the last round, xL is our image and so
// we allocate a public input. // we allocate a public input.
cs.alloc_input( cs.alloc_input(|| "image", || {
|| "image", new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing), })?
)?
} else { } else {
cs.alloc( cs.alloc(|| "new_xl", || {
|| "new_xl", new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing), })?
)?
}; };
cs.enforce( cs.enforce(
|| "new_xL = xR + (xL + Ci)^3", || "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp, |lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr, |lc| lc + new_xl - xr
); );
// xR = xL // xR = xL
@@ -158,9 +172,7 @@ fn test_mimc() {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
// Generate the MiMC round constants // Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS) let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
.collect::<Vec<_>>();
println!("Creating parameters..."); println!("Creating parameters...");
@@ -169,7 +181,7 @@ fn test_mimc() {
let c = MiMCDemo::<Bls12> { let c = MiMCDemo::<Bls12> {
xl: None, xl: None,
xr: None, xr: None,
constants: &constants, constants: &constants
}; };
generate_random_parameters(c, rng).unwrap() generate_random_parameters(c, rng).unwrap()
@@ -191,8 +203,8 @@ fn test_mimc() {
for _ in 0..SAMPLES { for _ in 0..SAMPLES {
// Generate a random preimage and compute the image // Generate a random preimage and compute the image
let xl = <Bls12 as ScalarEngine>::Fr::random(rng); let xl = rng.gen();
let xr = <Bls12 as ScalarEngine>::Fr::random(rng); let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants); let image = mimc::<Bls12>(xl, xr, &constants);
proof_vec.truncate(0); proof_vec.truncate(0);
@@ -204,7 +216,7 @@ fn test_mimc() {
let c = MiMCDemo { let c = MiMCDemo {
xl: Some(xl), xl: Some(xl),
xr: Some(xr), xr: Some(xr),
constants: &constants, constants: &constants
}; };
// Create a groth16 proof with our parameters. // Create a groth16 proof with our parameters.
@@ -218,16 +230,20 @@ fn test_mimc() {
let start = Instant::now(); let start = Instant::now();
let proof = Proof::read(&proof_vec[..]).unwrap(); let proof = Proof::read(&proof_vec[..]).unwrap();
// Check the proof // Check the proof
assert!(verify_proof(&pvk, &proof, &[image]).unwrap()); assert!(verify_proof(
&pvk,
&proof,
&[image]
).unwrap());
total_verifying += start.elapsed(); total_verifying += start.elapsed();
} }
let proving_avg = total_proving / SAMPLES; let proving_avg = total_proving / SAMPLES;
let proving_avg = let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64
proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64); + (proving_avg.as_secs() as f64);
let verifying_avg = total_verifying / SAMPLES; let verifying_avg = total_verifying / SAMPLES;
let verifying_avg = let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64
verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64); + (verifying_avg.as_secs() as f64);
println!("Average proving time: {:?} seconds", proving_avg); println!("Average proving time: {:?} seconds", proving_avg);
println!("Average verifying time: {:?} seconds", verifying_avg); println!("Average verifying time: {:?} seconds", verifying_avg);

View File

@@ -10,8 +10,8 @@ repository = "https://github.com/ebfull/ff"
[dependencies] [dependencies]
byteorder = "1" byteorder = "1"
rand = "0.4"
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true } ff_derive = { version = "0.3.0", path = "ff_derive", optional = true }
rand_core = "0.5"
[features] [features]
default = [] default = []

View File

@@ -52,8 +52,13 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut gen = proc_macro2::TokenStream::new(); let mut gen = proc_macro2::TokenStream::new();
let (constants_impl, sqrt_impl) = let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt(
prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator); &ast.ident,
&repr_ident,
modulus,
limbs,
generator,
);
gen.extend(constants_impl); gen.extend(constants_impl);
gen.extend(prime_field_repr_impl(&repr_ident, limbs)); gen.extend(prime_field_repr_impl(&repr_ident, limbs));
@@ -131,6 +136,13 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
} }
} }
impl ::rand::Rand for #repr {
#[inline(always)]
fn rand<R: ::rand::Rng>(rng: &mut R) -> Self {
#repr(rng.gen())
}
}
impl ::std::fmt::Display for #repr { impl ::std::fmt::Display for #repr {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x")); try!(write!(f, "0x"));
@@ -354,8 +366,7 @@ fn biguint_num_bits(mut v: BigUint) -> u32 {
fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
let mut ret = BigUint::one(); let mut ret = BigUint::one();
for i in exp for i in exp.to_bytes_be()
.to_bytes_be()
.into_iter() .into_iter()
.flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd())) .flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd()))
{ {
@@ -376,13 +387,11 @@ fn test_exp() {
&BigUint::from_str("5489673498567349856734895").unwrap(), &BigUint::from_str("5489673498567349856734895").unwrap(),
&BigUint::from_str( &BigUint::from_str(
"52435875175126190479447740508185965837690552500527637822603658699938581184513" "52435875175126190479447740508185965837690552500527637822603658699938581184513"
) ).unwrap()
.unwrap()
), ),
BigUint::from_str( BigUint::from_str(
"4371221214068404307866768905142520595925044802278091865033317963560480051536" "4371221214068404307866768905142520595925044802278091865033317963560480051536"
) ).unwrap()
.unwrap()
); );
} }
@@ -534,8 +543,7 @@ fn prime_field_constants_and_sqrt(
} }
inv = inv.wrapping_neg(); inv = inv.wrapping_neg();
( (quote! {
quote! {
/// This is the modulus m of the prime field /// This is the modulus m of the prime field
const MODULUS: #repr = #repr([#(#modulus,)*]); const MODULUS: #repr = #repr([#(#modulus,)*]);
@@ -564,9 +572,7 @@ fn prime_field_constants_and_sqrt(
/// 2^s root of unity computed by GENERATOR^t /// 2^s root of unity computed by GENERATOR^t
const ROOT_OF_UNITY: #repr = #repr(#root_of_unity); const ROOT_OF_UNITY: #repr = #repr(#root_of_unity);
}, }, sqrt_impl)
sqrt_impl,
)
} }
/// Implement PrimeField for the derived type. /// Implement PrimeField for the derived type.
@@ -833,6 +839,22 @@ fn prime_field_impl(
} }
} }
impl ::rand::Rand for #name {
/// Computes a uniformly random element using rejection sampling.
fn rand<R: ::rand::Rng>(rng: &mut R) -> Self {
loop {
let mut tmp = #name(#repr::rand(rng));
// Mask away the unused bits at the beginning.
tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS;
if tmp.is_valid() {
return tmp
}
}
}
}
impl From<#name> for #repr { impl From<#name> for #repr {
fn from(e: #name) -> #repr { fn from(e: #name) -> #repr {
e.into_repr() e.into_repr()
@@ -882,26 +904,6 @@ fn prime_field_impl(
} }
impl ::ff::Field for #name { impl ::ff::Field for #name {
/// Computes a uniformly random element using rejection sampling.
fn random<R: ::rand_core::RngCore>(rng: &mut R) -> Self {
loop {
let mut tmp = {
let mut repr = [0u64; #limbs];
for i in 0..#limbs {
repr[i] = rng.next_u64();
}
#name(#repr(repr))
};
// Mask away the unused most-significant bits.
tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS;
if tmp.is_valid() {
return tmp
}
}
}
#[inline] #[inline]
fn zero() -> Self { fn zero() -> Self {
#name(#repr::from(0)) #name(#repr::from(0))

View File

@@ -1,7 +1,7 @@
#![allow(unused_imports)] #![allow(unused_imports)]
extern crate byteorder; extern crate byteorder;
extern crate rand_core; extern crate rand;
#[cfg(feature = "derive")] #[cfg(feature = "derive")]
#[macro_use] #[macro_use]
@@ -10,18 +10,14 @@ extern crate ff_derive;
#[cfg(feature = "derive")] #[cfg(feature = "derive")]
pub use ff_derive::*; pub use ff_derive::*;
use rand_core::RngCore;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
/// This trait represents an element of a field. /// This trait represents an element of a field.
pub trait Field: pub trait Field:
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + rand::Rand
{ {
/// Returns an element chosen uniformly at random using a user-provided RNG.
fn random<R: RngCore>(rng: &mut R) -> Self;
/// Returns the zero element of the field, the additive identity. /// Returns the zero element of the field, the additive identity.
fn zero() -> Self; fn zero() -> Self;
@@ -104,6 +100,7 @@ pub trait PrimeFieldRepr:
+ fmt::Debug + fmt::Debug
+ fmt::Display + fmt::Display
+ 'static + 'static
+ rand::Rand
+ AsRef<[u64]> + AsRef<[u64]>
+ AsMut<[u64]> + AsMut<[u64]>
+ From<u64> + From<u64>

View File

@@ -2,6 +2,7 @@
name = "group" name = "group"
version = "0.1.0" version = "0.1.0"
authors = [ authors = [
"The Hush Developers",
"Sean Bowe <ewillbefull@gmail.com>", "Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
] ]
@@ -14,5 +15,4 @@ repository = "https://github.com/ebfull/group"
[dependencies] [dependencies]
ff = { path = "../ff" } ff = { path = "../ff" }
rand = "0.7" rand = "0.4"
rand_xorshift = "0.2"

View File

@@ -1,9 +1,7 @@
extern crate ff; extern crate ff;
extern crate rand; extern crate rand;
extern crate rand_xorshift;
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField}; use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
use rand::RngCore;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
@@ -15,16 +13,23 @@ pub use self::wnaf::Wnaf;
/// Projective representation of an elliptic curve point guaranteed to be /// Projective representation of an elliptic curve point guaranteed to be
/// in the correct prime order subgroup. /// in the correct prime order subgroup.
pub trait CurveProjective: pub trait CurveProjective:
PartialEq + Eq + Sized + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static PartialEq
+ Eq
+ Sized
+ Copy
+ Clone
+ Send
+ Sync
+ fmt::Debug
+ fmt::Display
+ rand::Rand
+ 'static
{ {
type Engine: ScalarEngine<Fr = Self::Scalar>; type Engine: ScalarEngine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField; type Scalar: PrimeField + SqrtField;
type Base: SqrtField; type Base: SqrtField;
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>; type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
/// Returns an element chosen uniformly at random using a user-provided RNG.
fn random<R: RngCore>(rng: &mut R) -> Self;
/// Returns the additive identity. /// Returns the additive identity.
fn zero() -> Self; fn zero() -> Self;

View File

@@ -1,14 +1,9 @@
use ff::{Field, PrimeField}; use rand::{Rand, Rng, SeedableRng, XorShiftRng};
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use {CurveAffine, CurveProjective, EncodedPoint}; use {CurveAffine, CurveProjective, EncodedPoint};
pub fn curve_tests<G: CurveProjective>() { pub fn curve_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// Negation edge case with zero. // Negation edge case with zero.
{ {
@@ -26,7 +21,7 @@ pub fn curve_tests<G: CurveProjective>() {
// Addition edge cases with zero // Addition edge cases with zero
{ {
let mut r = G::random(&mut rng); let mut r = G::rand(&mut rng);
let rcopy = r; let rcopy = r;
r.add_assign(&G::zero()); r.add_assign(&G::zero());
assert_eq!(r, rcopy); assert_eq!(r, rcopy);
@@ -50,10 +45,9 @@ pub fn curve_tests<G: CurveProjective>() {
// Transformations // Transformations
{ {
let a = G::random(&mut rng); let a = G::rand(&mut rng);
let b = a.into_affine().into_projective(); let b = a.into_affine().into_projective();
let c = a let c = a.into_affine()
.into_affine()
.into_projective() .into_projective()
.into_affine() .into_affine()
.into_projective(); .into_projective();
@@ -71,12 +65,11 @@ pub fn curve_tests<G: CurveProjective>() {
} }
fn random_wnaf_tests<G: CurveProjective>() { fn random_wnaf_tests<G: CurveProjective>() {
use ff::PrimeField;
use wnaf::*; use wnaf::*;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
{ {
let mut table = vec![]; let mut table = vec![];
@@ -84,8 +77,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
for w in 2..14 { for w in 2..14 {
for _ in 0..100 { for _ in 0..100 {
let g = G::random(&mut rng); let g = G::rand(&mut rng);
let s = G::Scalar::random(&mut rng).into_repr(); let s = G::Scalar::rand(&mut rng).into_repr();
let mut g1 = g; let mut g1 = g;
g1.mul_assign(s); g1.mul_assign(s);
@@ -102,8 +95,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
fn only_compiles_if_send<S: Send>(_: &S) {} fn only_compiles_if_send<S: Send>(_: &S) {}
for _ in 0..100 { for _ in 0..100 {
let g = G::random(&mut rng); let g = G::rand(&mut rng);
let s = G::Scalar::random(&mut rng).into_repr(); let s = G::Scalar::rand(&mut rng).into_repr();
let mut g1 = g; let mut g1 = g;
g1.mul_assign(s); g1.mul_assign(s);
@@ -136,8 +129,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
wnaf.base(g, 1).scalar(s) wnaf.base(g, 1).scalar(s)
}; };
@@ -145,8 +137,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
wnaf.scalar(s).base(g) wnaf.scalar(s).base(g)
}; };
@@ -154,8 +145,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
let mut shared = wnaf.base(g, 1).shared(); let mut shared = wnaf.base(g, 1).shared();
@@ -167,8 +157,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
let mut shared = wnaf.scalar(s).shared(); let mut shared = wnaf.scalar(s).shared();
@@ -190,15 +179,14 @@ fn random_wnaf_tests<G: CurveProjective>() {
} }
fn random_negation_tests<G: CurveProjective>() { fn random_negation_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ use ff::Field;
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5, let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
]);
for _ in 0..1000 { for _ in 0..1000 {
let r = G::random(&mut rng); let r = G::rand(&mut rng);
let s = G::Scalar::random(&mut rng); let s = G::Scalar::rand(&mut rng);
let mut sneg = s; let mut sneg = s;
sneg.negate(); sneg.negate();
@@ -222,14 +210,11 @@ fn random_negation_tests<G: CurveProjective>() {
} }
fn random_doubling_tests<G: CurveProjective>() { fn random_doubling_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut a = G::random(&mut rng); let mut a = G::rand(&mut rng);
let mut b = G::random(&mut rng); let mut b = G::rand(&mut rng);
// 2(a + b) // 2(a + b)
let mut tmp1 = a; let mut tmp1 = a;
@@ -252,18 +237,15 @@ fn random_doubling_tests<G: CurveProjective>() {
} }
fn random_multiplication_tests<G: CurveProjective>() { fn random_multiplication_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut a = G::random(&mut rng); let mut a = G::rand(&mut rng);
let mut b = G::random(&mut rng); let mut b = G::rand(&mut rng);
let a_affine = a.into_affine(); let a_affine = a.into_affine();
let b_affine = b.into_affine(); let b_affine = b.into_affine();
let s = G::Scalar::random(&mut rng); let s = G::Scalar::rand(&mut rng);
// s ( a + b ) // s ( a + b )
let mut tmp1 = a; let mut tmp1 = a;
@@ -287,15 +269,12 @@ fn random_multiplication_tests<G: CurveProjective>() {
} }
fn random_addition_tests<G: CurveProjective>() { fn random_addition_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = G::random(&mut rng); let a = G::rand(&mut rng);
let b = G::random(&mut rng); let b = G::rand(&mut rng);
let c = G::random(&mut rng); let c = G::rand(&mut rng);
let a_affine = a.into_affine(); let a_affine = a.into_affine();
let b_affine = b.into_affine(); let b_affine = b.into_affine();
let c_affine = c.into_affine(); let c_affine = c.into_affine();
@@ -368,13 +347,10 @@ fn random_addition_tests<G: CurveProjective>() {
} }
fn random_transformation_tests<G: CurveProjective>() { fn random_transformation_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let g = G::random(&mut rng); let g = G::rand(&mut rng);
let g_affine = g.into_affine(); let g_affine = g.into_affine();
let g_projective = g_affine.into_projective(); let g_projective = g_affine.into_projective();
assert_eq!(g, g_projective); assert_eq!(g, g_projective);
@@ -382,25 +358,24 @@ fn random_transformation_tests<G: CurveProjective>() {
// Batch normalization // Batch normalization
for _ in 0..10 { for _ in 0..10 {
let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::<Vec<_>>(); let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::<Vec<_>>();
for i in &v { for i in &v {
assert!(!i.is_normalized()); assert!(!i.is_normalized());
} }
use rand::distributions::{Distribution, Uniform}; use rand::distributions::{IndependentSample, Range};
let between = Uniform::new(0, 1000); let between = Range::new(0, 1000);
// Sprinkle in some normalized points // Sprinkle in some normalized points
for _ in 0..5 { for _ in 0..5 {
v[between.sample(&mut rng)] = G::zero(); v[between.ind_sample(&mut rng)] = G::zero();
} }
for _ in 0..5 { for _ in 0..5 {
let s = between.sample(&mut rng); let s = between.ind_sample(&mut rng);
v[s] = v[s].into_affine().into_projective(); v[s] = v[s].into_affine().into_projective();
} }
let expected_v = v let expected_v = v.iter()
.iter()
.map(|v| v.into_affine().into_projective()) .map(|v| v.into_affine().into_projective())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
G::batch_normalization(&mut v); G::batch_normalization(&mut v);
@@ -414,10 +389,7 @@ fn random_transformation_tests<G: CurveProjective>() {
} }
fn random_encoding_tests<G: CurveAffine>() { fn random_encoding_tests<G: CurveAffine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!( assert_eq!(
G::zero().into_uncompressed().into_affine().unwrap(), G::zero().into_uncompressed().into_affine().unwrap(),
@@ -430,7 +402,7 @@ fn random_encoding_tests<G: CurveAffine>() {
); );
for _ in 0..1000 { for _ in 0..1000 {
let mut r = G::Projective::random(&mut rng).into_affine(); let mut r = G::Projective::rand(&mut rng).into_affine();
let uncompressed = r.into_uncompressed(); let uncompressed = r.into_uncompressed();
let de_uncompressed = uncompressed.into_affine().unwrap(); let de_uncompressed = uncompressed.into_affine().unwrap();

View File

@@ -2,6 +2,7 @@
name = "librustzcash" name = "librustzcash"
version = "0.1.0" version = "0.1.0"
authors = [ authors = [
"The Hush Developers",
"Sean Bowe <ewillbefull@gmail.com>", "Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
"Jay Graber <jay@z.cash>", "Jay Graber <jay@z.cash>",
@@ -15,14 +16,17 @@ crate-type = ["staticlib"]
[dependencies] [dependencies]
bellman = { path = "../bellman" } bellman = { path = "../bellman" }
blake2b_simd = "0.5"
blake2s_simd = "0.5"
ff = { path = "../ff" } ff = { path = "../ff" }
libc = "0.2" libc = "0.2"
pairing = { path = "../pairing" } pairing = { path = "../pairing" }
lazy_static = "1" lazy_static = "1"
byteorder = "1" byteorder = "1"
rand_core = "0.5" rand = "0.4"
rand_os = "0.2" sapling-crypto = { path = "../sapling-crypto" }
zcash_primitives = { path = "../zcash_primitives" } zcash_primitives = { path = "../zcash_primitives" }
zcash_proofs = { path = "../zcash_proofs" } zcash_proofs = { path = "../zcash_proofs" }
zip32 = { path = "../zip32" }
[dependencies.blake2-rfc]
git = "https://git.hush.is/hush/blake2-rfc"
rev = "a3adc1d6859b887457c6ae4821ddad1802dad784"

View File

@@ -1,4 +1,4 @@
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState}; use blake2_rfc::blake2b::{Blake2b, Blake2bResult};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::Cursor; use std::io::Cursor;
use std::mem::size_of; use std::mem::size_of;
@@ -33,7 +33,7 @@ impl Params {
} }
impl Node { impl Node {
fn new(p: &Params, state: &Blake2bState, i: u32) -> Self { fn new(p: &Params, state: &Blake2b, i: u32) -> Self {
let hash = generate_hash(state, i / p.indices_per_hash_output()); let hash = generate_hash(state, i / p.indices_per_hash_output());
let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize; let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize;
let end = start + (p.n as usize) / 8; let end = start + (p.n as usize) / 8;
@@ -99,18 +99,15 @@ impl Node {
} }
} }
fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2b {
let mut personalization: Vec<u8> = Vec::from("ZcashPoW"); let mut personalization: Vec<u8> = Vec::from("ZcashPoW");
personalization.write_u32::<LittleEndian>(n).unwrap(); personalization.write_u32::<LittleEndian>(n).unwrap();
personalization.write_u32::<LittleEndian>(k).unwrap(); personalization.write_u32::<LittleEndian>(k).unwrap();
Blake2bParams::new() Blake2b::with_params(digest_len as usize, &[], &[], &personalization)
.hash_length(digest_len as usize)
.personal(&personalization)
.to_state()
} }
fn generate_hash(base_state: &Blake2bState, i: u32) -> Blake2bHash { fn generate_hash(base_state: &Blake2b, i: u32) -> Blake2bResult {
let mut lei = [0u8; 4]; let mut lei = [0u8; 4];
(&mut lei[..]).write_u32::<LittleEndian>(i).unwrap(); (&mut lei[..]).write_u32::<LittleEndian>(i).unwrap();
@@ -252,7 +249,7 @@ pub fn is_valid_solution_iterative(
return rows[0].is_zero(hash_len); return rows[0].is_zero(hash_len);
} }
fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option<Node> { fn tree_validator(p: &Params, state: &Blake2b, indices: &[u32]) -> Option<Node> {
if indices.len() > 1 { if indices.len() > 1 {
let end = indices.len(); let end = indices.len();
let mid = end / 2; let mid = end / 2;

View File

@@ -1,46 +1,47 @@
extern crate bellman; extern crate bellman;
extern crate blake2b_simd; extern crate blake2_rfc;
extern crate blake2s_simd;
extern crate byteorder; extern crate byteorder;
extern crate ff; extern crate ff;
extern crate libc; extern crate libc;
extern crate pairing; extern crate pairing;
extern crate rand_core; extern crate rand;
extern crate rand_os; extern crate sapling_crypto;
extern crate zcash_primitives; extern crate zcash_primitives;
extern crate zcash_proofs; extern crate zcash_proofs;
extern crate zip32;
extern crate lazy_static; extern crate lazy_static;
use ff::{PrimeField, PrimeFieldRepr}; use ff::{BitIterator, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr}; use pairing::bls12_381::{Bls12, Fr, FrRepr};
use zcash_primitives::{ use sapling_crypto::{
circuit::multipack,
constants::CRH_IVK_PERSONALIZATION, constants::CRH_IVK_PERSONALIZATION,
jubjub::{ jubjub::{
edwards, edwards,
fs::{Fs, FsRepr}, fs::{Fs, FsRepr},
FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown,
}, },
pedersen_hash::{pedersen_hash, Personalization},
redjubjub::{self, Signature},
}; };
use zcash_proofs::circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH; use sapling_crypto::circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH;
use zcash_proofs::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}; use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH};
use bellman::gadgets::multipack;
use bellman::groth16::{ use bellman::groth16::{
create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof, create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof,
}; };
use blake2s_simd::Params as Blake2sParams; use blake2_rfc::blake2s::Blake2s;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use rand_core::RngCore; use rand::{OsRng, Rng};
use rand_os::OsRng;
use std::io::BufReader; use std::io::BufReader;
use libc::{c_char, c_uchar, size_t}; use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t};
use std::ffi::CStr; use std::ffi::CStr;
use std::fs::File; use std::fs::File;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -56,18 +57,11 @@ use std::ffi::OsString;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use std::os::windows::ffi::OsStringExt; use std::os::windows::ffi::OsStringExt;
use zcash_primitives::{ use sapling_crypto::primitives::{ProofGenerationKey, ViewingKey};
merkle_tree::CommitmentTreeWitness, use zcash_primitives::{note_encryption::sapling_ka_agree, sapling::spend_sig, JUBJUB};
note_encryption::sapling_ka_agree,
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey},
redjubjub::{self, Signature},
sapling::{merkle_hash, spend_sig},
transaction::components::Amount,
zip32, JUBJUB,
};
use zcash_proofs::{ use zcash_proofs::{
load_parameters, load_parameters,
sapling::{SaplingProvingContext, SaplingVerificationContext}, sapling::{CommitmentTreeWitness, SaplingProvingContext, SaplingVerificationContext},
}; };
pub mod equihash; pub mod equihash;
@@ -235,7 +229,7 @@ fn init_zksnark_params(
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { pub extern "system" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) {
let tmp = Note::<Bls12>::uncommitted().into_repr(); let tmp = sapling_crypto::primitives::Note::<Bls12>::uncommitted().into_repr();
// Should be okay, caller is responsible for ensuring the pointer // Should be okay, caller is responsible for ensuring the pointer
// is a valid pointer to 32 bytes that can be mutated. // is a valid pointer to 32 bytes that can be mutated.
@@ -261,7 +255,28 @@ pub extern "system" fn librustzcash_merkle_hash(
// size of the representation // size of the representation
let b_repr = read_le(unsafe { &(&*b)[..] }); let b_repr = read_le(unsafe { &(&*b)[..] });
let tmp = merkle_hash(depth, &a_repr, &b_repr); let mut lhs = [false; 256];
let mut rhs = [false; 256];
for (a, b) in lhs.iter_mut().rev().zip(BitIterator::new(a_repr)) {
*a = b;
}
for (a, b) in rhs.iter_mut().rev().zip(BitIterator::new(b_repr)) {
*a = b;
}
let tmp = pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(depth),
lhs.iter()
.map(|&x| x)
.take(Fr::NUM_BITS as usize)
.chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)),
&JUBJUB,
)
.into_xy()
.0
.into_repr();
// Should be okay, caller is responsible for ensuring the pointer // Should be okay, caller is responsible for ensuring the pointer
// is a valid pointer to 32 bytes that can be mutated. // is a valid pointer to 32 bytes that can be mutated.
@@ -322,10 +337,7 @@ pub extern "system" fn librustzcash_crh_ivk(
let ak = unsafe { &*ak }; let ak = unsafe { &*ak };
let nk = unsafe { &*nk }; let nk = unsafe { &*nk };
let mut h = Blake2sParams::new() let mut h = Blake2s::with_params(32, &[], &[], CRH_IVK_PERSONALIZATION);
.hash_length(32)
.personal(CRH_IVK_PERSONALIZATION)
.to_state();
h.update(ak); h.update(ak);
h.update(nk); h.update(nk);
let mut h = h.finalize().as_ref().to_vec(); let mut h = h.finalize().as_ref().to_vec();
@@ -340,7 +352,7 @@ pub extern "system" fn librustzcash_crh_ivk(
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool { pub extern "system" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool {
let diversifier = Diversifier(unsafe { *diversifier }); let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
diversifier.g_d::<Bls12>(&JUBJUB).is_some() diversifier.g_d::<Bls12>(&JUBJUB).is_some()
} }
@@ -351,7 +363,7 @@ pub extern "system" fn librustzcash_ivk_to_pkd(
result: *mut [c_uchar; 32], result: *mut [c_uchar; 32],
) -> bool { ) -> bool {
let ivk = read_fs(unsafe { &*ivk }); let ivk = read_fs(unsafe { &*ivk });
let diversifier = Diversifier(unsafe { *diversifier }); let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
if let Some(g_d) = diversifier.g_d::<Bls12>(&JUBJUB) { if let Some(g_d) = diversifier.g_d::<Bls12>(&JUBJUB) {
let pk_d = g_d.mul(ivk, &JUBJUB); let pk_d = g_d.mul(ivk, &JUBJUB);
@@ -388,9 +400,11 @@ fn test_gen_r() {
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) {
// create random 64 byte buffer // create random 64 byte buffer
let mut rng = OsRng; let mut rng = OsRng::new().expect("should be able to construct RNG");
let mut buffer = [0u8; 64]; let mut buffer = [0u8; 64];
rng.fill_bytes(&mut buffer); for i in 0..buffer.len() {
buffer[i] = rng.gen();
}
// reduce to uniform value // reduce to uniform value
let r = <Bls12 as JubjubEngine>::Fs::to_uniform(&buffer[..]); let r = <Bls12 as JubjubEngine>::Fs::to_uniform(&buffer[..]);
@@ -404,10 +418,10 @@ pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32
fn priv_get_note( fn priv_get_note(
diversifier: *const [c_uchar; 11], diversifier: *const [c_uchar; 11],
pk_d: *const [c_uchar; 32], pk_d: *const [c_uchar; 32],
value: u64, value: uint64_t,
r: *const [c_uchar; 32], r: *const [c_uchar; 32],
) -> Result<Note<Bls12>, ()> { ) -> Result<sapling_crypto::primitives::Note<Bls12>, ()> {
let diversifier = Diversifier(unsafe { *diversifier }); let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
let g_d = match diversifier.g_d::<Bls12>(&JUBJUB) { let g_d = match diversifier.g_d::<Bls12>(&JUBJUB) {
Some(g_d) => g_d, Some(g_d) => g_d,
None => return Err(()), None => return Err(()),
@@ -429,7 +443,7 @@ fn priv_get_note(
Err(_) => return Err(()), Err(_) => return Err(()),
}; };
let note = Note { let note = sapling_crypto::primitives::Note {
value, value,
g_d, g_d,
pk_d, pk_d,
@@ -444,11 +458,11 @@ fn priv_get_note(
pub extern "system" fn librustzcash_sapling_compute_nf( pub extern "system" fn librustzcash_sapling_compute_nf(
diversifier: *const [c_uchar; 11], diversifier: *const [c_uchar; 11],
pk_d: *const [c_uchar; 32], pk_d: *const [c_uchar; 32],
value: u64, value: uint64_t,
r: *const [c_uchar; 32], r: *const [c_uchar; 32],
ak: *const [c_uchar; 32], ak: *const [c_uchar; 32],
nk: *const [c_uchar; 32], nk: *const [c_uchar; 32],
position: u64, position: uint64_t,
result: *mut [c_uchar; 32], result: *mut [c_uchar; 32],
) -> bool { ) -> bool {
let note = match priv_get_note(diversifier, pk_d, value, r) { let note = match priv_get_note(diversifier, pk_d, value, r) {
@@ -489,7 +503,7 @@ pub extern "system" fn librustzcash_sapling_compute_nf(
pub extern "system" fn librustzcash_sapling_compute_cm( pub extern "system" fn librustzcash_sapling_compute_cm(
diversifier: *const [c_uchar; 11], diversifier: *const [c_uchar; 11],
pk_d: *const [c_uchar; 32], pk_d: *const [c_uchar; 32],
value: u64, value: uint64_t,
r: *const [c_uchar; 32], r: *const [c_uchar; 32],
result: *mut [c_uchar; 32], result: *mut [c_uchar; 32],
) -> bool { ) -> bool {
@@ -538,7 +552,7 @@ pub extern "system" fn librustzcash_sapling_ka_derivepublic(
esk: *const [c_uchar; 32], esk: *const [c_uchar; 32],
result: *mut [c_uchar; 32], result: *mut [c_uchar; 32],
) -> bool { ) -> bool {
let diversifier = Diversifier(unsafe { *diversifier }); let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
// Compute g_d from the diversifier // Compute g_d from the diversifier
let g_d = match diversifier.g_d::<Bls12>(&JUBJUB) { let g_d = match diversifier.g_d::<Bls12>(&JUBJUB) {
@@ -562,8 +576,8 @@ pub extern "system" fn librustzcash_sapling_ka_derivepublic(
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_eh_isvalid( pub extern "system" fn librustzcash_eh_isvalid(
n: u32, n: uint32_t,
k: u32, k: uint32_t,
input: *const c_uchar, input: *const c_uchar,
input_len: size_t, input_len: size_t,
nonce: *const c_uchar, nonce: *const c_uchar,
@@ -700,15 +714,10 @@ pub extern "system" fn librustzcash_sapling_check_output(
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_sapling_final_check( pub extern "system" fn librustzcash_sapling_final_check(
ctx: *mut SaplingVerificationContext, ctx: *mut SaplingVerificationContext,
value_balance: i64, value_balance: int64_t,
binding_sig: *const [c_uchar; 64], binding_sig: *const [c_uchar; 64],
sighash_value: *const [c_uchar; 32], sighash_value: *const [c_uchar; 32],
) -> bool { ) -> bool {
let value_balance = match Amount::from_i64(value_balance) {
Ok(vb) => vb,
Err(()) => return false,
};
// Deserialize the signature // Deserialize the signature
let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) { let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) {
Ok(sig) => sig, Ok(sig) => sig,
@@ -733,31 +742,31 @@ pub extern "system" fn librustzcash_sprout_prove(
// First input // First input
in_sk1: *const [c_uchar; 32], in_sk1: *const [c_uchar; 32],
in_value1: u64, in_value1: uint64_t,
in_rho1: *const [c_uchar; 32], in_rho1: *const [c_uchar; 32],
in_r1: *const [c_uchar; 32], in_r1: *const [c_uchar; 32],
in_auth1: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], in_auth1: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8],
// Second input // Second input
in_sk2: *const [c_uchar; 32], in_sk2: *const [c_uchar; 32],
in_value2: u64, in_value2: uint64_t,
in_rho2: *const [c_uchar; 32], in_rho2: *const [c_uchar; 32],
in_r2: *const [c_uchar; 32], in_r2: *const [c_uchar; 32],
in_auth2: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], in_auth2: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8],
// First output // First output
out_pk1: *const [c_uchar; 32], out_pk1: *const [c_uchar; 32],
out_value1: u64, out_value1: uint64_t,
out_r1: *const [c_uchar; 32], out_r1: *const [c_uchar; 32],
// Second output // Second output
out_pk2: *const [c_uchar; 32], out_pk2: *const [c_uchar; 32],
out_value2: u64, out_value2: uint64_t,
out_r2: *const [c_uchar; 32], out_r2: *const [c_uchar; 32],
// Public value // Public value
vpub_old: u64, vpub_old: uint64_t,
vpub_new: u64, vpub_new: uint64_t,
) { ) {
let phi = unsafe { *phi }; let phi = unsafe { *phi };
let rt = unsafe { *rt }; let rt = unsafe { *rt };
@@ -863,7 +872,7 @@ pub extern "system" fn librustzcash_sprout_prove(
drop(sprout_fs); drop(sprout_fs);
// Initialize secure RNG // Initialize secure RNG
let mut rng = OsRng; let mut rng = OsRng::new().expect("should be able to construct RNG");
let proof = create_random_proof(js, &params, &mut rng).expect("proving should not fail"); let proof = create_random_proof(js, &params, &mut rng).expect("proving should not fail");
@@ -883,8 +892,8 @@ pub extern "system" fn librustzcash_sprout_verify(
nf2: *const [c_uchar; 32], nf2: *const [c_uchar; 32],
cm1: *const [c_uchar; 32], cm1: *const [c_uchar; 32],
cm2: *const [c_uchar; 32], cm2: *const [c_uchar; 32],
vpub_old: u64, vpub_old: uint64_t,
vpub_new: u64, vpub_new: uint64_t,
) -> bool { ) -> bool {
// Prepare the public input for the verifier // Prepare the public input for the verifier
let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2)); let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
@@ -928,7 +937,7 @@ pub extern "system" fn librustzcash_sapling_output_proof(
diversifier: *const [c_uchar; 11], diversifier: *const [c_uchar; 11],
pk_d: *const [c_uchar; 32], pk_d: *const [c_uchar; 32],
rcm: *const [c_uchar; 32], rcm: *const [c_uchar; 32],
value: u64, value: uint64_t,
cv: *mut [c_uchar; 32], cv: *mut [c_uchar; 32],
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
) -> bool { ) -> bool {
@@ -939,7 +948,7 @@ pub extern "system" fn librustzcash_sapling_output_proof(
}; };
// Grab the diversifier from the caller. // Grab the diversifier from the caller.
let diversifier = Diversifier(unsafe { *diversifier }); let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
// Grab pk_d from the caller. // Grab pk_d from the caller.
let pk_d = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*pk_d })[..], &JUBJUB) { let pk_d = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*pk_d })[..], &JUBJUB) {
@@ -954,7 +963,7 @@ pub extern "system" fn librustzcash_sapling_output_proof(
}; };
// Construct a payment address // Construct a payment address
let payment_address = PaymentAddress { let payment_address = sapling_crypto::primitives::PaymentAddress {
pk_d: pk_d, pk_d: pk_d,
diversifier: diversifier, diversifier: diversifier,
}; };
@@ -1007,11 +1016,8 @@ pub extern "system" fn librustzcash_sapling_spend_sig(
Err(_) => return false, Err(_) => return false,
}; };
// Initialize secure RNG
let mut rng = OsRng;
// Do the signing // Do the signing
let sig = spend_sig(ask, ar, unsafe { &*sighash }, &mut rng, &JUBJUB); let sig = spend_sig(ask, ar, unsafe { &*sighash }, &JUBJUB);
// Write out the signature // Write out the signature
sig.write(&mut (unsafe { &mut *result })[..]) sig.write(&mut (unsafe { &mut *result })[..])
@@ -1023,15 +1029,10 @@ pub extern "system" fn librustzcash_sapling_spend_sig(
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_sapling_binding_sig( pub extern "system" fn librustzcash_sapling_binding_sig(
ctx: *const SaplingProvingContext, ctx: *const SaplingProvingContext,
value_balance: i64, value_balance: int64_t,
sighash: *const [c_uchar; 32], sighash: *const [c_uchar; 32],
result: *mut [c_uchar; 64], result: *mut [c_uchar; 64],
) -> bool { ) -> bool {
let value_balance = match Amount::from_i64(value_balance) {
Ok(vb) => vb,
Err(()) => return false,
};
// Sign // Sign
let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) { let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) {
Ok(s) => s, Ok(s) => s,
@@ -1053,7 +1054,7 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
diversifier: *const [c_uchar; 11], diversifier: *const [c_uchar; 11],
rcm: *const [c_uchar; 32], rcm: *const [c_uchar; 32],
ar: *const [c_uchar; 32], ar: *const [c_uchar; 32],
value: u64, value: uint64_t,
anchor: *const [c_uchar; 32], anchor: *const [c_uchar; 32],
witness: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8], witness: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8],
cv: *mut [c_uchar; 32], cv: *mut [c_uchar; 32],
@@ -1085,7 +1086,7 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
}; };
// Grab the diversifier from the caller // Grab the diversifier from the caller
let diversifier = Diversifier(unsafe { *diversifier }); let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
// The caller chooses the note randomness // The caller chooses the note randomness
let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) {
@@ -1174,7 +1175,7 @@ pub extern "system" fn librustzcash_zip32_xsk_master(
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_zip32_xsk_derive( pub extern "system" fn librustzcash_zip32_xsk_derive(
xsk_parent: *const [c_uchar; 169], xsk_parent: *const [c_uchar; 169],
i: u32, i: uint32_t,
xsk_i: *mut [c_uchar; 169], xsk_i: *mut [c_uchar; 169],
) { ) {
let xsk_parent = zip32::ExtendedSpendingKey::read(&unsafe { *xsk_parent }[..]) let xsk_parent = zip32::ExtendedSpendingKey::read(&unsafe { *xsk_parent }[..])
@@ -1190,7 +1191,7 @@ pub extern "system" fn librustzcash_zip32_xsk_derive(
#[no_mangle] #[no_mangle]
pub extern "system" fn librustzcash_zip32_xfvk_derive( pub extern "system" fn librustzcash_zip32_xfvk_derive(
xfvk_parent: *const [c_uchar; 169], xfvk_parent: *const [c_uchar; 169],
i: u32, i: uint32_t,
xfvk_i: *mut [c_uchar; 169], xfvk_i: *mut [c_uchar; 169],
) -> bool { ) -> bool {
let xfvk_parent = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk_parent }[..]) let xfvk_parent = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk_parent }[..])

View File

@@ -1,9 +1,8 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand::{OsRng, Rng};
use rand_os::OsRng; use sapling_crypto::jubjub::{edwards, JubjubBls12};
use zcash_primitives::jubjub::{edwards, JubjubBls12}; use sapling_crypto::primitives::{Diversifier, ViewingKey};
use zcash_primitives::primitives::{Diversifier, ViewingKey};
use { use {
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
@@ -13,7 +12,7 @@ use {
#[test] #[test]
fn test_key_agreement() { fn test_key_agreement() {
let params = JubjubBls12::new(); let params = JubjubBls12::new();
let mut rng = OsRng; let mut rng = OsRng::new().unwrap();
// Create random viewing key // Create random viewing key
let vk = ViewingKey::<Bls12> { let vk = ViewingKey::<Bls12> {
@@ -23,9 +22,7 @@ fn test_key_agreement() {
// Create a random address with the viewing key // Create a random address with the viewing key
let addr = loop { let addr = loop {
let mut d = [0; 11]; match vk.into_payment_address(Diversifier(rng.gen()), &params) {
rng.fill_bytes(&mut d);
match vk.into_payment_address(Diversifier(d), &params) {
Some(a) => break a, Some(a) => break a,
None => {} None => {}
} }

View File

@@ -1,6 +1,6 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use zcash_primitives::{ use sapling_crypto::{
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams}, jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
primitives::{Diversifier, ProofGenerationKey}, primitives::{Diversifier, ProofGenerationKey},
}; };
@@ -28,8 +28,6 @@ fn key_components() {
note_v: u64, note_v: u64,
note_r: [u8; 32], note_r: [u8; 32],
note_cm: [u8; 32], note_cm: [u8; 32],
note_pos: u64,
note_nf: [u8; 32],
}; };
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
@@ -89,12 +87,6 @@ fn key_components() {
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
0xdd, 0x07, 0x64, 0x39, 0xdd, 0x07, 0x64, 0x39,
], ],
note_pos: 0,
note_nf: [
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
0x47, 0xab, 0x40, 0x63,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -151,12 +143,6 @@ fn key_components() {
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
0xca, 0xd4, 0x69, 0x50, 0xca, 0xd4, 0x69, 0x50,
], ],
note_pos: 763714296,
note_nf: [
0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2,
0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17,
0x4c, 0x2e, 0x9d, 0x93,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -213,12 +199,6 @@ fn key_components() {
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
0x4e, 0x55, 0xf1, 0x51, 0x4e, 0x55, 0xf1, 0x51,
], ],
note_pos: 1527428592,
note_nf: [
0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f,
0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f,
0x59, 0xae, 0x1d, 0x14,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -275,12 +255,6 @@ fn key_components() {
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
0x60, 0xd1, 0x9b, 0x6c, 0x60, 0xd1, 0x9b, 0x6c,
], ],
note_pos: 2291142888,
note_nf: [
0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56,
0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75,
0x8b, 0xec, 0x47, 0xa1,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -337,12 +311,6 @@ fn key_components() {
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
0xc7, 0x1b, 0x7f, 0x36, 0xc7, 0x1b, 0x7f, 0x36,
], ],
note_pos: 3054857184,
note_nf: [
0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f,
0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03,
0x01, 0xbf, 0x3d, 0x13,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -399,12 +367,6 @@ fn key_components() {
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
0xde, 0x1a, 0x5b, 0x4c, 0xde, 0x1a, 0x5b, 0x4c,
], ],
note_pos: 3818571480,
note_nf: [
0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2,
0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20,
0xb6, 0xc9, 0xa7, 0xe1,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -461,12 +423,6 @@ fn key_components() {
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
0x78, 0x3a, 0x1e, 0x55, 0x78, 0x3a, 0x1e, 0x55,
], ],
note_pos: 287318480,
note_nf: [
0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae,
0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4,
0x07, 0xd5, 0x1e, 0x11,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -523,12 +479,6 @@ fn key_components() {
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
0xf7, 0x8c, 0x2b, 0x23, 0xf7, 0x8c, 0x2b, 0x23,
], ],
note_pos: 1051032776,
note_nf: [
0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f,
0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc,
0x8b, 0xb6, 0x98, 0x90,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -585,12 +535,6 @@ fn key_components() {
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
0x8f, 0xea, 0x4d, 0x04, 0x8f, 0xea, 0x4d, 0x04,
], ],
note_pos: 1814747072,
note_nf: [
0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20,
0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe,
0xb1, 0x7c, 0xd6, 0x20,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -647,12 +591,6 @@ fn key_components() {
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
0xe4, 0x1d, 0xeb, 0x37, 0xe4, 0x1d, 0xeb, 0x37,
], ],
note_pos: 2578461368,
note_nf: [
0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe,
0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71,
0x0d, 0x93, 0xc9, 0xe9,
],
}, },
]; ];
@@ -725,7 +663,5 @@ fn key_components() {
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap(); note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
assert_eq!(&vec, &tv.note_cm); assert_eq!(&vec, &tv.note_cm);
} }
assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf);
} }
} }

View File

@@ -1,4 +1,4 @@
use zcash_primitives::jubjub::{FixedGenerators, JubjubParams}; use sapling_crypto::jubjub::{FixedGenerators, JubjubParams};
use super::JUBJUB; use super::JUBJUB;

View File

@@ -1,7 +1,9 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; use sapling_crypto::{
use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature}; jubjub::{FixedGenerators, JubjubEngine},
redjubjub::{PrivateKey, PublicKey, Signature},
};
use super::JUBJUB; use super::JUBJUB;

View File

@@ -4,6 +4,7 @@ name = "pairing"
# Remember to change version string in README.md. # Remember to change version string in README.md.
version = "0.14.2" version = "0.14.2"
authors = [ authors = [
"The Hush Developers",
"Sean Bowe <ewillbefull@gmail.com>", "Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
] ]
@@ -15,13 +16,10 @@ homepage = "https://github.com/ebfull/pairing"
repository = "https://github.com/ebfull/pairing" repository = "https://github.com/ebfull/pairing"
[dependencies] [dependencies]
rand = "0.4"
byteorder = "1" byteorder = "1"
ff = { path = "../ff", features = ["derive"] } ff = { path = "../ff", features = ["derive"] }
group = { path = "../group" } group = { path = "../group" }
rand_core = "0.5"
[dev-dependencies]
rand_xorshift = "0.2"
[features] [features]
unstable-features = ["expose-arith"] unstable-features = ["expose-arith"]

View File

@@ -14,10 +14,11 @@ macro_rules! curve_impl {
pub struct $affine { pub struct $affine {
pub(crate) x: $basefield, pub(crate) x: $basefield,
pub(crate) y: $basefield, pub(crate) y: $basefield,
pub(crate) infinity: bool, pub(crate) infinity: bool
} }
impl ::std::fmt::Display for $affine { impl ::std::fmt::Display for $affine
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
if self.infinity { if self.infinity {
write!(f, "{}(Infinity)", $name) write!(f, "{}(Infinity)", $name)
@@ -31,10 +32,11 @@ macro_rules! curve_impl {
pub struct $projective { pub struct $projective {
pub(crate) x: $basefield, pub(crate) x: $basefield,
pub(crate) y: $basefield, pub(crate) y: $basefield,
pub(crate) z: $basefield, pub(crate) z: $basefield
} }
impl ::std::fmt::Display for $projective { impl ::std::fmt::Display for $projective
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.into_affine()) write!(f, "{}", self.into_affine())
} }
@@ -87,9 +89,7 @@ macro_rules! curve_impl {
let mut res = $projective::zero(); let mut res = $projective::zero();
for i in bits { for i in bits {
res.double(); res.double();
if i { if i { res.add_assign_mixed(self) }
res.add_assign_mixed(self)
}
} }
res res
} }
@@ -112,8 +112,12 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: if (y < negy) ^ greatest { y } else { negy }, y: if (y < negy) ^ greatest {
infinity: false, y
} else {
negy
},
infinity: false
} }
}) })
} }
@@ -152,7 +156,7 @@ macro_rules! curve_impl {
$affine { $affine {
x: $basefield::zero(), x: $basefield::zero(),
y: $basefield::one(), y: $basefield::one(),
infinity: true, infinity: true
} }
} }
@@ -178,6 +182,7 @@ macro_rules! curve_impl {
fn into_projective(&self) -> $projective { fn into_projective(&self) -> $projective {
(*self).into() (*self).into()
} }
} }
impl PairingCurveAffine for $affine { impl PairingCurveAffine for $affine {
@@ -192,18 +197,14 @@ macro_rules! curve_impl {
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
self.perform_pairing(other) self.perform_pairing(other)
} }
} }
impl CurveProjective for $projective { impl Rand for $projective {
type Engine = Bls12; fn rand<R: Rng>(rng: &mut R) -> Self {
type Scalar = $scalarfield;
type Base = $basefield;
type Affine = $affine;
fn random<R: RngCore>(rng: &mut R) -> Self {
loop { loop {
let x = $basefield::random(rng); let x = rng.gen();
let greatest = rng.next_u32() % 2 != 0; let greatest = rng.gen();
if let Some(p) = $affine::get_point_from_x(x, greatest) { if let Some(p) = $affine::get_point_from_x(x, greatest) {
let p = p.scale_by_cofactor(); let p = p.scale_by_cofactor();
@@ -214,6 +215,13 @@ macro_rules! curve_impl {
} }
} }
} }
}
impl CurveProjective for $projective {
type Engine = Bls12;
type Scalar = $scalarfield;
type Base = $basefield;
type Affine = $affine;
// The point at infinity is always represented by // The point at infinity is always represented by
// Z = 0. // Z = 0.
@@ -221,7 +229,7 @@ macro_rules! curve_impl {
$projective { $projective {
x: $basefield::zero(), x: $basefield::zero(),
y: $basefield::one(), y: $basefield::one(),
z: $basefield::zero(), z: $basefield::zero()
} }
} }
@@ -239,7 +247,8 @@ macro_rules! curve_impl {
self.is_zero() || self.z == $basefield::one() self.is_zero() || self.z == $basefield::one()
} }
fn batch_normalization(v: &mut [Self]) { fn batch_normalization(v: &mut [Self])
{
// Montgomerys Trick and Fast Implementation of Masked AES // Montgomerys Trick and Fast Implementation of Masked AES
// Genelle, Prouff and Quisquater // Genelle, Prouff and Quisquater
// Section 3.2 // Section 3.2
@@ -247,8 +256,7 @@ macro_rules! curve_impl {
// First pass: compute [a, ab, abc, ...] // First pass: compute [a, ab, abc, ...]
let mut prod = Vec::with_capacity(v.len()); let mut prod = Vec::with_capacity(v.len());
let mut tmp = $basefield::one(); let mut tmp = $basefield::one();
for g in v for g in v.iter_mut()
.iter_mut()
// Ignore normalized elements // Ignore normalized elements
.filter(|g| !g.is_normalized()) .filter(|g| !g.is_normalized())
{ {
@@ -260,19 +268,13 @@ macro_rules! curve_impl {
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
// Second pass: iterate backwards to compute inverses // Second pass: iterate backwards to compute inverses
for (g, s) in v for (g, s) in v.iter_mut()
.iter_mut()
// Backwards // Backwards
.rev() .rev()
// Ignore normalized elements // Ignore normalized elements
.filter(|g| !g.is_normalized()) .filter(|g| !g.is_normalized())
// Backwards, skip last element, fill in one for last term. // Backwards, skip last element, fill in one for last term.
.zip( .zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one())))
prod.into_iter()
.rev()
.skip(1)
.chain(Some($basefield::one())),
)
{ {
// tmp := tmp * g.z; g.z := tmp * s = 1/z // tmp := tmp * g.z; g.z := tmp * s = 1/z
let mut newtmp = tmp; let mut newtmp = tmp;
@@ -283,7 +285,9 @@ macro_rules! curve_impl {
} }
// Perform affine transformations // Perform affine transformations
for g in v.iter_mut().filter(|g| !g.is_normalized()) { for g in v.iter_mut()
.filter(|g| !g.is_normalized())
{
let mut z = g.z; // 1/z let mut z = g.z; // 1/z
z.square(); // 1/z^2 z.square(); // 1/z^2
g.x.mul_assign(&z); // x/z^2 g.x.mul_assign(&z); // x/z^2
@@ -536,7 +540,8 @@ macro_rules! curve_impl {
let mut found_one = false; let mut found_one = false;
for i in BitIterator::new(other.into()) { for i in BitIterator::new(other.into())
{
if found_one { if found_one {
res.double(); res.double();
} else { } else {
@@ -574,7 +579,7 @@ macro_rules! curve_impl {
$projective { $projective {
x: p.x, x: p.x,
y: p.y, y: p.y,
z: $basefield::one(), z: $basefield::one()
} }
} }
} }
@@ -591,7 +596,7 @@ macro_rules! curve_impl {
$affine { $affine {
x: p.x, x: p.x,
y: p.y, y: p.y,
infinity: false, infinity: false
} }
} else { } else {
// Z is nonzero, so it must have an inverse in a field. // Z is nonzero, so it must have an inverse in a field.
@@ -611,12 +616,12 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: y, y: y,
infinity: false, infinity: false
}
} }
} }
} }
} }
};
} }
pub mod g1 { pub mod g1 {
@@ -624,7 +629,7 @@ pub mod g1 {
use super::g2::G2Affine; use super::g2::G2Affine;
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand_core::RngCore; use rand::{Rand, Rng};
use std::fmt; use std::fmt;
use {Engine, PairingCurveAffine}; use {Engine, PairingCurveAffine};
@@ -952,7 +957,7 @@ pub mod g1 {
let negyrepr = negy.into_repr(); let negyrepr = negy.into_repr();
let p = G1Affine { let p = G1Affine {
x, x: x,
y: if yrepr < negyrepr { y } else { negy }, y: if yrepr < negyrepr { y } else { negy },
infinity: false, infinity: false,
}; };
@@ -987,8 +992,7 @@ pub mod g1 {
0x9fe83b1b4a5d648d, 0x9fe83b1b4a5d648d,
0xf583cc5a508f6a40, 0xf583cc5a508f6a40,
0xc3ad2aefde0bb13, 0xc3ad2aefde0bb13,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x60aa6f9552f03aae, 0x60aa6f9552f03aae,
0xecd01d5181300d35, 0xecd01d5181300d35,
@@ -996,8 +1000,7 @@ pub mod g1 {
0xe760f57922998c9d, 0xe760f57922998c9d,
0x953703f5795a39e5, 0x953703f5795a39e5,
0xfe3ae0922df702c, 0xfe3ae0922df702c,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(!p.is_on_curve()); assert!(!p.is_on_curve());
@@ -1014,8 +1017,7 @@ pub mod g1 {
0xea034ee2928b30a8, 0xea034ee2928b30a8,
0xbd8833dc7c79a7f7, 0xbd8833dc7c79a7f7,
0xe45c9f0c0438675, 0xe45c9f0c0438675,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x3b450eb1ab7b5dad, 0x3b450eb1ab7b5dad,
0xa65cb81e975e8675, 0xa65cb81e975e8675,
@@ -1023,8 +1025,7 @@ pub mod g1 {
0x753ddf21a2601d20, 0x753ddf21a2601d20,
0x532d0b640bd3ff8b, 0x532d0b640bd3ff8b,
0x118d2c543f031102, 0x118d2c543f031102,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(!p.is_on_curve()); assert!(!p.is_on_curve());
@@ -1042,8 +1043,7 @@ pub mod g1 {
0xf35de9ce0d6b4e84, 0xf35de9ce0d6b4e84,
0x265bddd23d1dec54, 0x265bddd23d1dec54,
0x12a8778088458308, 0x12a8778088458308,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x8a22defa0d526256, 0x8a22defa0d526256,
0xc57ca55456fcb9ae, 0xc57ca55456fcb9ae,
@@ -1051,8 +1051,7 @@ pub mod g1 {
0x921beef89d4f29df, 0x921beef89d4f29df,
0x5b6fda44ad85fa78, 0x5b6fda44ad85fa78,
0xed74ab9f302cbe0, 0xed74ab9f302cbe0,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(p.is_on_curve()); assert!(p.is_on_curve());
@@ -1070,8 +1069,7 @@ pub mod g1 {
0x485e77d50a5df10d, 0x485e77d50a5df10d,
0x4c6fcac4b55fd479, 0x4c6fcac4b55fd479,
0x86ed4d9906fb064, 0x86ed4d9906fb064,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd25ee6461538c65, 0xd25ee6461538c65,
0x9f3bbb2ecd3719b9, 0x9f3bbb2ecd3719b9,
@@ -1079,8 +1077,7 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])) ])).unwrap(),
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@@ -1092,8 +1089,7 @@ pub mod g1 {
0x5f44314ec5e3fb03, 0x5f44314ec5e3fb03,
0x24e8538737c6e675, 0x24e8538737c6e675,
0x8abd623a594fba8, 0x8abd623a594fba8,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x6b0528f088bb7044, 0x6b0528f088bb7044,
0x2fdeb5c82917ff9e, 0x2fdeb5c82917ff9e,
@@ -1101,8 +1097,7 @@ pub mod g1 {
0xd65104c6f95a872a, 0xd65104c6f95a872a,
0x1f2998a5a9c61253, 0x1f2998a5a9c61253,
0xe74846154a9e44, 0xe74846154a9e44,
])) ])).unwrap(),
.unwrap(),
z: Fq::one(), z: Fq::one(),
}); });
@@ -1118,8 +1113,7 @@ pub mod g1 {
0xc4f9a52a428e23bb, 0xc4f9a52a428e23bb,
0xd178b28dd4f407ef, 0xd178b28dd4f407ef,
0x17fb8905e9183c69 0x17fb8905e9183c69
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd0de9d65292b7710, 0xd0de9d65292b7710,
0xf6a05f2bcf1d9ca7, 0xf6a05f2bcf1d9ca7,
@@ -1127,8 +1121,7 @@ pub mod g1 {
0xeec8d1a5b7466c58, 0xeec8d1a5b7466c58,
0x4bc362649dce6376, 0x4bc362649dce6376,
0x430cbdc5455b00a 0x430cbdc5455b00a
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
} }
); );
@@ -1144,8 +1137,7 @@ pub mod g1 {
0x485e77d50a5df10d, 0x485e77d50a5df10d,
0x4c6fcac4b55fd479, 0x4c6fcac4b55fd479,
0x86ed4d9906fb064, 0x86ed4d9906fb064,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd25ee6461538c65, 0xd25ee6461538c65,
0x9f3bbb2ecd3719b9, 0x9f3bbb2ecd3719b9,
@@ -1153,8 +1145,7 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])) ])).unwrap(),
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@@ -1172,8 +1163,7 @@ pub mod g1 {
0x4b914c16687dcde0, 0x4b914c16687dcde0,
0x66c8baf177d20533, 0x66c8baf177d20533,
0xaf960cff3d83833 0xaf960cff3d83833
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x3f0675695f5177a8, 0x3f0675695f5177a8,
0x2b6d82ae178a1ba0, 0x2b6d82ae178a1ba0,
@@ -1181,8 +1171,7 @@ pub mod g1 {
0x1771a65b60572f4e, 0x1771a65b60572f4e,
0x8b547c1313b27555, 0x8b547c1313b27555,
0x135075589a687b1e 0x135075589a687b1e
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
} }
); );
@@ -1205,8 +1194,7 @@ pub mod g1 {
0x71ffa8021531705, 0x71ffa8021531705,
0x7418d484386d267, 0x7418d484386d267,
0xd5108d8ff1fbd6, 0xd5108d8ff1fbd6,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766, 0xa776ccbfe9981766,
0x255632964ff40f4a, 0x255632964ff40f4a,
@@ -1214,8 +1202,7 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1227,8 +1214,7 @@ pub mod g1 {
0xc6e05201e5f83991, 0xc6e05201e5f83991,
0xf7c75910816f207c, 0xf7c75910816f207c,
0x18d4043e78103106, 0x18d4043e78103106,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766, 0xa776ccbfe9981766,
0x255632964ff40f4a, 0x255632964ff40f4a,
@@ -1236,8 +1222,7 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1252,8 +1237,7 @@ pub mod g1 {
0x9676ff02ec39c227, 0x9676ff02ec39c227,
0x4c12c15d7e55b9f3, 0x4c12c15d7e55b9f3,
0x57fd1e317db9bd, 0x57fd1e317db9bd,
])) ])).unwrap(),
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x1288334016679345, 0x1288334016679345,
0xf955cd68615ff0b5, 0xf955cd68615ff0b5,
@@ -1261,8 +1245,7 @@ pub mod g1 {
0x1267d70db51049fb, 0x1267d70db51049fb,
0x4696deb9ab2ba3e7, 0x4696deb9ab2ba3e7,
0xb1e4e11177f59d4, 0xb1e4e11177f59d4,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1293,7 +1276,7 @@ pub mod g2 {
use super::g1::G1Affine; use super::g1::G1Affine;
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand_core::RngCore; use rand::{Rand, Rng};
use std::fmt; use std::fmt;
use {Engine, PairingCurveAffine}; use {Engine, PairingCurveAffine};
@@ -1656,7 +1639,7 @@ pub mod g2 {
negy.negate(); negy.negate();
let p = G2Affine { let p = G2Affine {
x, x: x,
y: if y < negy { y } else { negy }, y: if y < negy { y } else { negy },
infinity: false, infinity: false,
}; };
@@ -1692,8 +1675,7 @@ pub mod g2 {
0x7a17a004747e3dbe, 0x7a17a004747e3dbe,
0xcc65406a7c2e5a73, 0xcc65406a7c2e5a73,
0x10b8c03d64db4d0c, 0x10b8c03d64db4d0c,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xd30e70fe2f029778, 0xd30e70fe2f029778,
0xda30772df0f5212e, 0xda30772df0f5212e,
@@ -1701,8 +1683,7 @@ pub mod g2 {
0xfb777e5b9b568608, 0xfb777e5b9b568608,
0x789bac1fec71a2b9, 0x789bac1fec71a2b9,
0x1342f02e2da54405, 0x1342f02e2da54405,
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1712,8 +1693,7 @@ pub mod g2 {
0x663015d9410eb608, 0x663015d9410eb608,
0x78e82a79d829a544, 0x78e82a79d829a544,
0x40a00545bb3c1e, 0x40a00545bb3c1e,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4709802348e79377, 0x4709802348e79377,
0xb5ac4dc9204bcfbd, 0xb5ac4dc9204bcfbd,
@@ -1721,8 +1701,7 @@ pub mod g2 {
0x15008b1dc399e8df, 0x15008b1dc399e8df,
0x68128fd0548a3829, 0x68128fd0548a3829,
0x16a613db5c873aaa, 0x16a613db5c873aaa,
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1741,8 +1720,7 @@ pub mod g2 {
0x41abba710d6c692c, 0x41abba710d6c692c,
0xffcc4b2b62ce8484, 0xffcc4b2b62ce8484,
0x6993ec01b8934ed, 0x6993ec01b8934ed,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xb94e92d5f874e26, 0xb94e92d5f874e26,
0x44516408bc115d95, 0x44516408bc115d95,
@@ -1750,8 +1728,7 @@ pub mod g2 {
0xa5a0c2b7131f3555, 0xa5a0c2b7131f3555,
0x83800965822367e7, 0x83800965822367e7,
0x10cf1d3ad8d90bfa, 0x10cf1d3ad8d90bfa,
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1761,8 +1738,7 @@ pub mod g2 {
0x5a9171720e73eb51, 0x5a9171720e73eb51,
0x38eb4fd8d658adb7, 0x38eb4fd8d658adb7,
0xb649051bbc1164d, 0xb649051bbc1164d,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x9225814253d7df75, 0x9225814253d7df75,
0xc196c2513477f887, 0xc196c2513477f887,
@@ -1770,8 +1746,7 @@ pub mod g2 {
0x55f2b8efad953e04, 0x55f2b8efad953e04,
0x7379345eda55265e, 0x7379345eda55265e,
0x377f2e6208fd4cb, 0x377f2e6208fd4cb,
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1791,8 +1766,7 @@ pub mod g2 {
0x2199bc19c48c393d, 0x2199bc19c48c393d,
0x4a151b732a6075bf, 0x4a151b732a6075bf,
0x17762a3b9108c4a7, 0x17762a3b9108c4a7,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x26f461e944bbd3d1, 0x26f461e944bbd3d1,
0x298f3189a9cf6ed6, 0x298f3189a9cf6ed6,
@@ -1800,8 +1774,7 @@ pub mod g2 {
0x7e147f3f9e6e241, 0x7e147f3f9e6e241,
0x72a9b63583963fff, 0x72a9b63583963fff,
0x158b0083c000462, 0x158b0083c000462,
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1811,8 +1784,7 @@ pub mod g2 {
0x68cad19430706b4d, 0x68cad19430706b4d,
0x3ccfb97b924dcea8, 0x3ccfb97b924dcea8,
0x1660f93434588f8d, 0x1660f93434588f8d,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xaaed3985b6dcb9c7, 0xaaed3985b6dcb9c7,
0xc1e985d6d898d9f4, 0xc1e985d6d898d9f4,
@@ -1820,8 +1792,7 @@ pub mod g2 {
0x3940a2dbb914b529, 0x3940a2dbb914b529,
0xbeb88137cf34f3e7, 0xbeb88137cf34f3e7,
0x1699ee577c61b694, 0x1699ee577c61b694,
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1841,8 +1812,7 @@ pub mod g2 {
0x72556c999f3707ac, 0x72556c999f3707ac,
0x4617f2e6774e9711, 0x4617f2e6774e9711,
0x100b2fe5bffe030b, 0x100b2fe5bffe030b,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x7a33555977ec608, 0x7a33555977ec608,
0xe23039d1fe9c0881, 0xe23039d1fe9c0881,
@@ -1850,8 +1820,7 @@ pub mod g2 {
0x4637c4f417667e2e, 0x4637c4f417667e2e,
0x93ebe7c3e41f6acc, 0x93ebe7c3e41f6acc,
0xde884f89a9a371b, 0xde884f89a9a371b,
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1861,8 +1830,7 @@ pub mod g2 {
0x25fd427b4122f231, 0x25fd427b4122f231,
0xd83112aace35cae, 0xd83112aace35cae,
0x191b2432407cbb7f, 0x191b2432407cbb7f,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5, 0xf68ae82fe97662f5,
0xe986057068b50b7d, 0xe986057068b50b7d,
@@ -1870,8 +1838,7 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])) ])).unwrap(),
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@@ -1885,8 +1852,7 @@ pub mod g2 {
0x8e73a96b329ad190, 0x8e73a96b329ad190,
0x27c546f75ee1f3ab, 0x27c546f75ee1f3ab,
0xa33d27add5e7e82, 0xa33d27add5e7e82,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x93b1ebcd54870dfe, 0x93b1ebcd54870dfe,
0xf1578300e1342e11, 0xf1578300e1342e11,
@@ -1894,8 +1860,7 @@ pub mod g2 {
0x2089faf462438296, 0x2089faf462438296,
0x828e5848cd48ea66, 0x828e5848cd48ea66,
0x141ecbac1deb038b, 0x141ecbac1deb038b,
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1905,8 +1870,7 @@ pub mod g2 {
0x2767032fc37cc31d, 0x2767032fc37cc31d,
0xd5ee2aba84fd10fe, 0xd5ee2aba84fd10fe,
0x16576ccd3dd0a4e8, 0x16576ccd3dd0a4e8,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4da9b6f6a96d1dd2, 0x4da9b6f6a96d1dd2,
0x9657f7da77f1650e, 0x9657f7da77f1650e,
@@ -1914,8 +1878,7 @@ pub mod g2 {
0x31898db63f87363a, 0x31898db63f87363a,
0xabab040ddbd097cc, 0xabab040ddbd097cc,
0x11ad236b9ba02990, 0x11ad236b9ba02990,
])) ])).unwrap(),
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}); });
@@ -1933,8 +1896,7 @@ pub mod g2 {
0xf1273e6406eef9cc, 0xf1273e6406eef9cc,
0xababd760ff05cb92, 0xababd760ff05cb92,
0xd7c20456617e89 0xd7c20456617e89
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xd1a50b8572cbd2b8, 0xd1a50b8572cbd2b8,
0x238f0ac6119d07df, 0x238f0ac6119d07df,
@@ -1942,8 +1904,7 @@ pub mod g2 {
0x8b203284c51edf6b, 0x8b203284c51edf6b,
0xc8a0b730bbb21f5e, 0xc8a0b730bbb21f5e,
0x1a3b59d29a31274 0x1a3b59d29a31274
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1953,8 +1914,7 @@ pub mod g2 {
0x64528ab3863633dc, 0x64528ab3863633dc,
0x159384333d7cba97, 0x159384333d7cba97,
0x4cb84741f3cafe8 0x4cb84741f3cafe8
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x242af0dc3640e1a4, 0x242af0dc3640e1a4,
0xe90a73ad65c66919, 0xe90a73ad65c66919,
@@ -1962,8 +1922,7 @@ pub mod g2 {
0x38528f92b689644d, 0x38528f92b689644d,
0xb6884deec59fb21f, 0xb6884deec59fb21f,
0x3c075d3ec52ba90 0x3c075d3ec52ba90
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
} }
@@ -1981,8 +1940,7 @@ pub mod g2 {
0x72556c999f3707ac, 0x72556c999f3707ac,
0x4617f2e6774e9711, 0x4617f2e6774e9711,
0x100b2fe5bffe030b, 0x100b2fe5bffe030b,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x7a33555977ec608, 0x7a33555977ec608,
0xe23039d1fe9c0881, 0xe23039d1fe9c0881,
@@ -1990,8 +1948,7 @@ pub mod g2 {
0x4637c4f417667e2e, 0x4637c4f417667e2e,
0x93ebe7c3e41f6acc, 0x93ebe7c3e41f6acc,
0xde884f89a9a371b, 0xde884f89a9a371b,
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -2001,8 +1958,7 @@ pub mod g2 {
0x25fd427b4122f231, 0x25fd427b4122f231,
0xd83112aace35cae, 0xd83112aace35cae,
0x191b2432407cbb7f, 0x191b2432407cbb7f,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5, 0xf68ae82fe97662f5,
0xe986057068b50b7d, 0xe986057068b50b7d,
@@ -2010,8 +1966,7 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])) ])).unwrap(),
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@@ -2031,8 +1986,7 @@ pub mod g2 {
0xbcedcfce1e52d986, 0xbcedcfce1e52d986,
0x9755d4a3926e9862, 0x9755d4a3926e9862,
0x18bab73760fd8024 0x18bab73760fd8024
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4e7c5e0a2ae5b99e, 0x4e7c5e0a2ae5b99e,
0x96e582a27f028961, 0x96e582a27f028961,
@@ -2040,8 +1994,7 @@ pub mod g2 {
0xeb0cf5e610ef4fe7, 0xeb0cf5e610ef4fe7,
0x7b4c2bae8db6e70b, 0x7b4c2bae8db6e70b,
0xf136e43909fca0 0xf136e43909fca0
])) ])).unwrap(),
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -2051,8 +2004,7 @@ pub mod g2 {
0xa5a2a51f7fde787b, 0xa5a2a51f7fde787b,
0x8b92866bc6384188, 0x8b92866bc6384188,
0x81a53fe531d64ef 0x81a53fe531d64ef
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4c5d607666239b34, 0x4c5d607666239b34,
0xeddb5f48304d14b3, 0xeddb5f48304d14b3,
@@ -2060,8 +2012,7 @@ pub mod g2 {
0xb271f52f12ead742, 0xb271f52f12ead742,
0x244e6c2015c83348, 0x244e6c2015c83348,
0x19e2deae6eb9b441 0x19e2deae6eb9b441
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
} }

View File

@@ -1173,9 +1173,7 @@ fn test_neg_one() {
} }
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq_repr_ordering() { fn test_fq_repr_ordering() {
@@ -1398,10 +1396,7 @@ fn test_fq_repr_num_bits() {
#[test] #[test]
fn test_fq_repr_sub_noborrow() { fn test_fq_repr_sub_noborrow() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FqRepr([ let mut t = FqRepr([
0x827a4a08041ebd9, 0x827a4a08041ebd9,
@@ -1431,7 +1426,7 @@ fn test_fq_repr_sub_noborrow() {
); );
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq::random(&mut rng).into_repr(); let mut a = FqRepr::rand(&mut rng);
a.0[5] >>= 30; a.0[5] >>= 30;
let mut b = a; let mut b = a;
for _ in 0..10 { for _ in 0..10 {
@@ -1488,10 +1483,7 @@ fn test_fq_repr_sub_noborrow() {
#[test] #[test]
fn test_fq_repr_add_nocarry() { fn test_fq_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FqRepr([ let mut t = FqRepr([
0x827a4a08041ebd9, 0x827a4a08041ebd9,
@@ -1522,9 +1514,9 @@ fn test_fq_repr_add_nocarry() {
// Test for the associativity of addition. // Test for the associativity of addition.
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq::random(&mut rng).into_repr(); let mut a = FqRepr::rand(&mut rng);
let mut b = Fq::random(&mut rng).into_repr(); let mut b = FqRepr::rand(&mut rng);
let mut c = Fq::random(&mut rng).into_repr(); let mut c = FqRepr::rand(&mut rng);
// Unset the first few bits, so that overflow won't occur. // Unset the first few bits, so that overflow won't occur.
a.0[5] >>= 3; a.0[5] >>= 3;
@@ -1582,32 +1574,31 @@ fn test_fq_is_valid() {
a.0.sub_noborrow(&FqRepr::from(1)); a.0.sub_noborrow(&FqRepr::from(1));
assert!(a.is_valid()); assert!(a.is_valid());
assert!(Fq(FqRepr::from(0)).is_valid()); assert!(Fq(FqRepr::from(0)).is_valid());
assert!(Fq(FqRepr([ assert!(
Fq(FqRepr([
0xdf4671abd14dab3e, 0xdf4671abd14dab3e,
0xe2dc0c9f534fbd33, 0xe2dc0c9f534fbd33,
0x31ca6c880cc444a6, 0x31ca6c880cc444a6,
0x257a67e70ef33359, 0x257a67e70ef33359,
0xf9b29e493f899b36, 0xf9b29e493f899b36,
0x17c8be1800b9f059 0x17c8be1800b9f059
])) ])).is_valid()
.is_valid()); );
assert!(!Fq(FqRepr([ assert!(
!Fq(FqRepr([
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff 0xffffffffffffffff
])) ])).is_valid()
.is_valid()); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
assert!(a.is_valid()); assert!(a.is_valid());
} }
} }
@@ -1717,16 +1708,13 @@ fn test_fq_add_assign() {
// Test associativity // Test associativity
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Generate a, b, c and ensure (a + b) + c == a + (b + c). // Generate a, b, c and ensure (a + b) + c == a + (b + c).
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
let b = Fq::random(&mut rng); let b = Fq::rand(&mut rng);
let c = Fq::random(&mut rng); let c = Fq::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -1830,15 +1818,12 @@ fn test_fq_sub_assign() {
); );
} }
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that (a - b) + (b - a) = 0. // Ensure that (a - b) + (b - a) = 0.
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
let b = Fq::random(&mut rng); let b = Fq::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.sub_assign(&b); tmp1.sub_assign(&b);
@@ -1880,16 +1865,13 @@ fn test_fq_mul_assign() {
])) ]))
); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * b) * c = a * (b * c) // Ensure that (a * b) * c = a * (b * c)
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
let b = Fq::random(&mut rng); let b = Fq::rand(&mut rng);
let c = Fq::random(&mut rng); let c = Fq::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.mul_assign(&b); tmp1.mul_assign(&b);
@@ -1905,10 +1887,10 @@ fn test_fq_mul_assign() {
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that r * (a + b + c) = r*a + r*b + r*c // Ensure that r * (a + b + c) = r*a + r*b + r*c
let r = Fq::random(&mut rng); let r = Fq::rand(&mut rng);
let mut a = Fq::random(&mut rng); let mut a = Fq::rand(&mut rng);
let mut b = Fq::random(&mut rng); let mut b = Fq::rand(&mut rng);
let mut c = Fq::random(&mut rng); let mut c = Fq::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -1947,18 +1929,14 @@ fn test_fq_squaring() {
0xdc05c659b4e15b27, 0xdc05c659b4e15b27,
0x79361e5a802c6a23, 0x79361e5a802c6a23,
0x24bcbe5d51b9a6f 0x24bcbe5d51b9a6f
])) ])).unwrap()
.unwrap()
); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * a) = a^2 // Ensure that (a * a) = a^2
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
let mut tmp = a; let mut tmp = a;
tmp.square(); tmp.square();
@@ -1974,16 +1952,13 @@ fn test_fq_squaring() {
fn test_fq_inverse() { fn test_fq_inverse() {
assert!(Fq::zero().inverse().is_none()); assert!(Fq::zero().inverse().is_none());
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let one = Fq::one(); let one = Fq::one();
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that a * a^-1 = 1 // Ensure that a * a^-1 = 1
let mut a = Fq::random(&mut rng); let mut a = Fq::rand(&mut rng);
let ainv = a.inverse().unwrap(); let ainv = a.inverse().unwrap();
a.mul_assign(&ainv); a.mul_assign(&ainv);
assert_eq!(a, one); assert_eq!(a, one);
@@ -1992,14 +1967,11 @@ fn test_fq_inverse() {
#[test] #[test]
fn test_fq_double() { fn test_fq_double() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure doubling a is equivalent to adding a to itself. // Ensure doubling a is equivalent to adding a to itself.
let mut a = Fq::random(&mut rng); let mut a = Fq::rand(&mut rng);
let mut b = a; let mut b = a;
b.add_assign(&a); b.add_assign(&a);
a.double(); a.double();
@@ -2016,14 +1988,11 @@ fn test_fq_negate() {
assert!(a.is_zero()); assert!(a.is_zero());
} }
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure (a - (-a)) = 0. // Ensure (a - (-a)) = 0.
let mut a = Fq::random(&mut rng); let mut a = Fq::rand(&mut rng);
let mut b = a; let mut b = a;
b.negate(); b.negate();
a.add_assign(&b); a.add_assign(&b);
@@ -2034,15 +2003,12 @@ fn test_fq_negate() {
#[test] #[test]
fn test_fq_pow() { fn test_fq_pow() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for i in 0..1000 { for i in 0..1000 {
// Exponentiate by various small numbers and ensure it consists with repeated // Exponentiate by various small numbers and ensure it consists with repeated
// multiplication. // multiplication.
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
let target = a.pow(&[i]); let target = a.pow(&[i]);
let mut c = Fq::one(); let mut c = Fq::one();
for _ in 0..i { for _ in 0..i {
@@ -2053,7 +2019,7 @@ fn test_fq_pow() {
for _ in 0..1000 { for _ in 0..1000 {
// Exponentiating by the modulus should have no effect in a prime field. // Exponentiating by the modulus should have no effect in a prime field.
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
assert_eq!(a, a.pow(Fq::char())); assert_eq!(a, a.pow(Fq::char()));
} }
@@ -2063,16 +2029,13 @@ fn test_fq_pow() {
fn test_fq_sqrt() { fn test_fq_sqrt() {
use ff::SqrtField; use ff::SqrtField;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero()); assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero());
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a^2) = a or -a // Ensure sqrt(a^2) = a or -a
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
let mut nega = a; let mut nega = a;
nega.negate(); nega.negate();
let mut b = a; let mut b = a;
@@ -2085,7 +2048,7 @@ fn test_fq_sqrt() {
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a)^2 = a for random a // Ensure sqrt(a)^2 = a for random a
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
if let Some(mut tmp) = a.sqrt() { if let Some(mut tmp) = a.sqrt() {
tmp.square(); tmp.square();
@@ -2098,15 +2061,16 @@ fn test_fq_sqrt() {
#[test] #[test]
fn test_fq_from_into_repr() { fn test_fq_from_into_repr() {
// q + 1 should not be in the field // q + 1 should not be in the field
assert!(Fq::from_repr(FqRepr([ assert!(
Fq::from_repr(FqRepr([
0xb9feffffffffaaac, 0xb9feffffffffaaac,
0x1eabfffeb153ffff, 0x1eabfffeb153ffff,
0x6730d2a0f6b0f624, 0x6730d2a0f6b0f624,
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])) ])).is_err()
.is_err()); );
// q should not be in the field // q should not be in the field
assert!(Fq::from_repr(Fq::char()).is_err()); assert!(Fq::from_repr(Fq::char()).is_err());
@@ -2144,14 +2108,11 @@ fn test_fq_from_into_repr() {
// Zero should be in the field. // Zero should be in the field.
assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero()); assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero());
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Try to turn Fq elements into representations and back again, and compare. // Try to turn Fq elements into representations and back again, and compare.
let a = Fq::random(&mut rng); let a = Fq::rand(&mut rng);
let a_repr = a.into_repr(); let a_repr = a.into_repr();
let b_repr = FqRepr::from(a); let b_repr = FqRepr::from(a);
assert_eq!(a_repr, b_repr); assert_eq!(a_repr, b_repr);
@@ -2244,7 +2205,7 @@ fn test_fq_ordering() {
#[test] #[test]
fn fq_repr_tests() { fn fq_repr_tests() {
::tests::repr::random_repr_tests::<Fq>(); ::tests::repr::random_repr_tests::<FqRepr>();
} }
#[test] #[test]

View File

@@ -2,7 +2,7 @@ use super::fq::FROBENIUS_COEFF_FQ12_C1;
use super::fq2::Fq2; use super::fq2::Fq2;
use super::fq6::Fq6; use super::fq6::Fq6;
use ff::Field; use ff::Field;
use rand_core::RngCore; use rand::{Rand, Rng};
/// An element of Fq12, represented by c0 + c1 * w. /// An element of Fq12, represented by c0 + c1 * w.
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -17,6 +17,15 @@ impl ::std::fmt::Display for Fq12 {
} }
} }
impl Rand for Fq12 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq12 {
c0: rng.gen(),
c1: rng.gen(),
}
}
}
impl Fq12 { impl Fq12 {
pub fn conjugate(&mut self) { pub fn conjugate(&mut self) {
self.c1.negate(); self.c1.negate();
@@ -40,13 +49,6 @@ impl Fq12 {
} }
impl Field for Fq12 { impl Field for Fq12 {
fn random<R: RngCore>(rng: &mut R) -> Self {
Fq12 {
c0: Fq6::random(rng),
c1: Fq6::random(rng),
}
}
fn zero() -> Self { fn zero() -> Self {
Fq12 { Fq12 {
c0: Fq6::zero(), c0: Fq6::zero(),
@@ -147,29 +149,24 @@ impl Field for Fq12 {
} }
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq12_mul_by_014() { fn test_fq12_mul_by_014() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c0 = Fq2::random(&mut rng); let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::random(&mut rng); let c1 = Fq2::rand(&mut rng);
let c5 = Fq2::random(&mut rng); let c5 = Fq2::rand(&mut rng);
let mut a = Fq12::random(&mut rng); let mut a = Fq12::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_014(&c0, &c1, &c5); a.mul_by_014(&c0, &c1, &c5);
b.mul_assign(&Fq12 { b.mul_assign(&Fq12 {
c0: Fq6 { c0: Fq6 {
c0, c0: c0,
c1, c1: c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}, },
c1: Fq6 { c1: Fq6 {

View File

@@ -1,6 +1,6 @@
use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE};
use ff::{Field, SqrtField}; use ff::{Field, SqrtField};
use rand_core::RngCore; use rand::{Rand, Rng};
use std::cmp::Ordering; use std::cmp::Ordering;
@@ -56,14 +56,16 @@ impl Fq2 {
} }
} }
impl Field for Fq2 { impl Rand for Fq2 {
fn random<R: RngCore>(rng: &mut R) -> Self { fn rand<R: Rng>(rng: &mut R) -> Self {
Fq2 { Fq2 {
c0: Fq::random(rng), c0: rng.gen(),
c1: Fq::random(rng), c1: rng.gen(),
}
} }
} }
impl Field for Fq2 {
fn zero() -> Self { fn zero() -> Self {
Fq2 { Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
@@ -261,11 +263,12 @@ fn test_fq2_basics() {
); );
assert!(Fq2::zero().is_zero()); assert!(Fq2::zero().is_zero());
assert!(!Fq2::one().is_zero()); assert!(!Fq2::one().is_zero());
assert!(!Fq2 { assert!(
!Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
c1: Fq::one(), c1: Fq::one(),
} }.is_zero()
.is_zero()); );
} }
#[test] #[test]
@@ -308,8 +311,7 @@ fn test_fq2_squaring() {
0xf7f295a94e58ae7c, 0xf7f295a94e58ae7c,
0x41b76dcc1c3fbe5e, 0x41b76dcc1c3fbe5e,
0x7080c5fa1d8e042, 0x7080c5fa1d8e042,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x38f473b3c870a4ab, 0x38f473b3c870a4ab,
0x6ad3291177c8c7e5, 0x6ad3291177c8c7e5,
@@ -317,8 +319,7 @@ fn test_fq2_squaring() {
0xbfb99020604137a0, 0xbfb99020604137a0,
0xfc58a7b7be815407, 0xfc58a7b7be815407,
0x10d1615e75250a21, 0x10d1615e75250a21,
])) ])).unwrap(),
.unwrap(),
}; };
a.square(); a.square();
assert_eq!( assert_eq!(
@@ -331,8 +332,7 @@ fn test_fq2_squaring() {
0xcb674157618da176, 0xcb674157618da176,
0x4cf17b5893c3d327, 0x4cf17b5893c3d327,
0x7eac81369c43361 0x7eac81369c43361
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xc1579cf58e980cf8, 0xc1579cf58e980cf8,
0xa23eb7e12dd54d98, 0xa23eb7e12dd54d98,
@@ -340,8 +340,7 @@ fn test_fq2_squaring() {
0x38d0d7275a9689e1, 0x38d0d7275a9689e1,
0x739c983042779a65, 0x739c983042779a65,
0x1542a61c8a8db994 0x1542a61c8a8db994
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -359,8 +358,7 @@ fn test_fq2_mul() {
0x9ee53e7e84d7532e, 0x9ee53e7e84d7532e,
0x1c202d8ed97afb45, 0x1c202d8ed97afb45,
0x51d3f9253e2516f, 0x51d3f9253e2516f,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf, 0xa7348a8b511aedcf,
0x143c215d8176b319, 0x143c215d8176b319,
@@ -368,8 +366,7 @@ fn test_fq2_mul() {
0x9533e4a9a5158be, 0x9533e4a9a5158be,
0x7a5e1ecb676d65f9, 0x7a5e1ecb676d65f9,
0x180c3ee46656b008, 0x180c3ee46656b008,
])) ])).unwrap(),
.unwrap(),
}; };
a.mul_assign(&Fq2 { a.mul_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -379,8 +376,7 @@ fn test_fq2_mul() {
0xcd460f9f0c23e430, 0xcd460f9f0c23e430,
0x6c9110292bfa409, 0x6c9110292bfa409,
0x2c93a72eb8af83e, 0x2c93a72eb8af83e,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4b1c3f936d8992d4, 0x4b1c3f936d8992d4,
0x1d2a72916dba4c8a, 0x1d2a72916dba4c8a,
@@ -388,8 +384,7 @@ fn test_fq2_mul() {
0x57a06d3135a752ae, 0x57a06d3135a752ae,
0x634cd3c6c565096d, 0x634cd3c6c565096d,
0x19e17334d4e93558, 0x19e17334d4e93558,
])) ])).unwrap(),
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -401,8 +396,7 @@ fn test_fq2_mul() {
0x5511fe4d84ee5f78, 0x5511fe4d84ee5f78,
0x5310a202d92f9963, 0x5310a202d92f9963,
0x1751afbe166e5399 0x1751afbe166e5399
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x84af0e1bd630117a, 0x84af0e1bd630117a,
0x6c63cd4da2c2aa7, 0x6c63cd4da2c2aa7,
@@ -410,8 +404,7 @@ fn test_fq2_mul() {
0xc975106579c275ee, 0xc975106579c275ee,
0x33a9ac82ce4c5083, 0x33a9ac82ce4c5083,
0x1ef1a36c201589d 0x1ef1a36c201589d
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -431,8 +424,7 @@ fn test_fq2_inverse() {
0x9ee53e7e84d7532e, 0x9ee53e7e84d7532e,
0x1c202d8ed97afb45, 0x1c202d8ed97afb45,
0x51d3f9253e2516f, 0x51d3f9253e2516f,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf, 0xa7348a8b511aedcf,
0x143c215d8176b319, 0x143c215d8176b319,
@@ -440,8 +432,7 @@ fn test_fq2_inverse() {
0x9533e4a9a5158be, 0x9533e4a9a5158be,
0x7a5e1ecb676d65f9, 0x7a5e1ecb676d65f9,
0x180c3ee46656b008, 0x180c3ee46656b008,
])) ])).unwrap(),
.unwrap(),
}; };
let a = a.inverse().unwrap(); let a = a.inverse().unwrap();
assert_eq!( assert_eq!(
@@ -454,8 +445,7 @@ fn test_fq2_inverse() {
0xdfba703293941c30, 0xdfba703293941c30,
0xa6c3d8f9586f2636, 0xa6c3d8f9586f2636,
0x1351ef01941b70c4 0x1351ef01941b70c4
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x8c39fd76a8312cb4, 0x8c39fd76a8312cb4,
0x15d7b6b95defbff0, 0x15d7b6b95defbff0,
@@ -463,8 +453,7 @@ fn test_fq2_inverse() {
0xcbf651a0f367afb2, 0xcbf651a0f367afb2,
0xdf4e54f0d3ef15a6, 0xdf4e54f0d3ef15a6,
0x103bdf241afb0019 0x103bdf241afb0019
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -482,8 +471,7 @@ fn test_fq2_addition() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -491,8 +479,7 @@ fn test_fq2_addition() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])) ])).unwrap(),
.unwrap(),
}; };
a.add_assign(&Fq2 { a.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -502,8 +489,7 @@ fn test_fq2_addition() {
0x3b88899a42a6318f, 0x3b88899a42a6318f,
0x986a4a62fa82a49d, 0x986a4a62fa82a49d,
0x13ce433fa26027f5, 0x13ce433fa26027f5,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9, 0x66323bf80b58b9b9,
0xa1379b6facf6e596, 0xa1379b6facf6e596,
@@ -511,8 +497,7 @@ fn test_fq2_addition() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])) ])).unwrap(),
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -524,8 +509,7 @@ fn test_fq2_addition() {
0xf4ef57d604b6bca2, 0xf4ef57d604b6bca2,
0x65309427b3d5d090, 0x65309427b3d5d090,
0x14c715d5553f01d2 0x14c715d5553f01d2
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xfdb032e7d9079a94, 0xfdb032e7d9079a94,
0x35a2809d15468d83, 0x35a2809d15468d83,
@@ -533,8 +517,7 @@ fn test_fq2_addition() {
0xd62fa51334f560fa, 0xd62fa51334f560fa,
0x9ad265eb46e01984, 0x9ad265eb46e01984,
0x1303f3465112c8bc 0x1303f3465112c8bc
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -552,8 +535,7 @@ fn test_fq2_subtraction() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -561,8 +543,7 @@ fn test_fq2_subtraction() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])) ])).unwrap(),
.unwrap(),
}; };
a.sub_assign(&Fq2 { a.sub_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -572,8 +553,7 @@ fn test_fq2_subtraction() {
0x3b88899a42a6318f, 0x3b88899a42a6318f,
0x986a4a62fa82a49d, 0x986a4a62fa82a49d,
0x13ce433fa26027f5, 0x13ce433fa26027f5,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9, 0x66323bf80b58b9b9,
0xa1379b6facf6e596, 0xa1379b6facf6e596,
@@ -581,8 +561,7 @@ fn test_fq2_subtraction() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])) ])).unwrap(),
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -594,8 +573,7 @@ fn test_fq2_subtraction() {
0xe255902672ef6c43, 0xe255902672ef6c43,
0x7f77a718021c342d, 0x7f77a718021c342d,
0x72ba14049fe9881 0x72ba14049fe9881
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xeb4abaf7c255d1cd, 0xeb4abaf7c255d1cd,
0x11df49bc6cacc256, 0x11df49bc6cacc256,
@@ -603,8 +581,7 @@ fn test_fq2_subtraction() {
0xf63905f39ad8cb1f, 0xf63905f39ad8cb1f,
0x4cd5dd9fb40b3b8f, 0x4cd5dd9fb40b3b8f,
0x957411359ba6e4c 0x957411359ba6e4c
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -622,8 +599,7 @@ fn test_fq2_negation() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -631,8 +607,7 @@ fn test_fq2_negation() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])) ])).unwrap(),
.unwrap(),
}; };
a.negate(); a.negate();
assert_eq!( assert_eq!(
@@ -645,8 +620,7 @@ fn test_fq2_negation() {
0xab107d49317487ab, 0xab107d49317487ab,
0x7e555df189f880e3, 0x7e555df189f880e3,
0x19083f5486a10cbd 0x19083f5486a10cbd
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x228109103250c9d0, 0x228109103250c9d0,
0x8a411ad149045812, 0x8a411ad149045812,
@@ -654,8 +628,7 @@ fn test_fq2_negation() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -673,8 +646,7 @@ fn test_fq2_doubling() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -682,8 +654,7 @@ fn test_fq2_doubling() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])) ])).unwrap(),
.unwrap(),
}; };
a.double(); a.double();
assert_eq!( assert_eq!(
@@ -696,8 +667,7 @@ fn test_fq2_doubling() {
0x72cd9c7784211627, 0x72cd9c7784211627,
0x998c938972a657e7, 0x998c938972a657e7,
0x1f1a52b65bdb3b9 0x1f1a52b65bdb3b9
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x2efbeddf9b5dc1b6, 0x2efbeddf9b5dc1b6,
0x28d5ca5ad09f4fdb, 0x28d5ca5ad09f4fdb,
@@ -705,8 +675,7 @@ fn test_fq2_doubling() {
0x67f15f81dc49195b, 0x67f15f81dc49195b,
0x9c8c9bd4b79fa83d, 0x9c8c9bd4b79fa83d,
0x25a226f714d506e 0x25a226f714d506e
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -724,8 +693,7 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -733,8 +701,7 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])) ])).unwrap(),
.unwrap(),
}; };
a.frobenius_map(0); a.frobenius_map(0);
assert_eq!( assert_eq!(
@@ -747,8 +714,7 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -756,8 +722,7 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])) ])).unwrap(),
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@@ -771,8 +736,7 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x228109103250c9d0, 0x228109103250c9d0,
0x8a411ad149045812, 0x8a411ad149045812,
@@ -780,8 +744,7 @@ fn test_fq2_frobenius_map() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])) ])).unwrap(),
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@@ -795,8 +758,7 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -804,8 +766,7 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])) ])).unwrap(),
.unwrap(),
} }
); );
a.frobenius_map(2); a.frobenius_map(2);
@@ -819,8 +780,7 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -828,8 +788,7 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -848,8 +807,7 @@ fn test_fq2_sqrt() {
0xdb4a116b5bf74aa1, 0xdb4a116b5bf74aa1,
0x1e58b2159dfe10e2, 0x1e58b2159dfe10e2,
0x7ca7da1f13606ac 0x7ca7da1f13606ac
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xfa8de88b7516d2c3, 0xfa8de88b7516d2c3,
0x371a75ed14f41629, 0x371a75ed14f41629,
@@ -857,10 +815,8 @@ fn test_fq2_sqrt() {
0x212611bca4e99121, 0x212611bca4e99121,
0x8ee5394d77afb3d, 0x8ee5394d77afb3d,
0xec92336650e49d5 0xec92336650e49d5
])) ])).unwrap(),
.unwrap(), }.sqrt()
}
.sqrt()
.unwrap(), .unwrap(),
Fq2 { Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -870,8 +826,7 @@ fn test_fq2_sqrt() {
0x8d7f1f723d02c1d3, 0x8d7f1f723d02c1d3,
0x881b3e01b611c070, 0x881b3e01b611c070,
0x10f6963bbad2ebc5 0x10f6963bbad2ebc5
])) ])).unwrap(),
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xc099534fc209e752, 0xc099534fc209e752,
0x7670594665676447, 0x7670594665676447,
@@ -879,8 +834,7 @@ fn test_fq2_sqrt() {
0x6b852aeaf2afcb1b, 0x6b852aeaf2afcb1b,
0xa4c93b08105d71a9, 0xa4c93b08105d71a9,
0x8d7cfff94216330 0x8d7cfff94216330
])) ])).unwrap(),
.unwrap(),
} }
); );
@@ -893,11 +847,9 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])) ])).unwrap(),
.unwrap(),
c1: Fq::zero(), c1: Fq::zero(),
} }.sqrt()
.sqrt()
.unwrap(), .unwrap(),
Fq2 { Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
@@ -908,8 +860,7 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -928,16 +879,11 @@ fn test_fq2_legendre() {
} }
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq2_mul_nonresidue() { fn test_fq2_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq2 { let nqr = Fq2 {
c0: Fq::one(), c0: Fq::one(),
@@ -945,7 +891,7 @@ fn test_fq2_mul_nonresidue() {
}; };
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq2::random(&mut rng); let mut a = Fq2::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_nonresidue(); a.mul_by_nonresidue();
b.mul_assign(&nqr); b.mul_assign(&nqr);

View File

@@ -1,7 +1,7 @@
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
use super::fq2::Fq2; use super::fq2::Fq2;
use ff::Field; use ff::Field;
use rand_core::RngCore; use rand::{Rand, Rng};
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). /// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -17,6 +17,16 @@ impl ::std::fmt::Display for Fq6 {
} }
} }
impl Rand for Fq6 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq6 {
c0: rng.gen(),
c1: rng.gen(),
c2: rng.gen(),
}
}
}
impl Fq6 { impl Fq6 {
/// Multiply by quadratic nonresidue v. /// Multiply by quadratic nonresidue v.
pub fn mul_by_nonresidue(&mut self) { pub fn mul_by_nonresidue(&mut self) {
@@ -100,14 +110,6 @@ impl Fq6 {
} }
impl Field for Fq6 { impl Field for Fq6 {
fn random<R: RngCore>(rng: &mut R) -> Self {
Fq6 {
c0: Fq2::random(rng),
c1: Fq2::random(rng),
c2: Fq2::random(rng),
}
}
fn zero() -> Self { fn zero() -> Self {
Fq6 { Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
@@ -300,16 +302,11 @@ impl Field for Fq6 {
} }
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq6_mul_nonresidue() { fn test_fq6_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq6 { let nqr = Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
@@ -318,7 +315,7 @@ fn test_fq6_mul_nonresidue() {
}; };
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq6::random(&mut rng); let mut a = Fq6::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_nonresidue(); a.mul_by_nonresidue();
b.mul_assign(&nqr); b.mul_assign(&nqr);
@@ -329,20 +326,17 @@ fn test_fq6_mul_nonresidue() {
#[test] #[test]
fn test_fq6_mul_by_1() { fn test_fq6_mul_by_1() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c1 = Fq2::random(&mut rng); let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::random(&mut rng); let mut a = Fq6::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_1(&c1); a.mul_by_1(&c1);
b.mul_assign(&Fq6 { b.mul_assign(&Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
c1, c1: c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}); });
@@ -352,21 +346,18 @@ fn test_fq6_mul_by_1() {
#[test] #[test]
fn test_fq6_mul_by_01() { fn test_fq6_mul_by_01() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c0 = Fq2::random(&mut rng); let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::random(&mut rng); let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::random(&mut rng); let mut a = Fq6::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_01(&c0, &c1); a.mul_by_01(&c0, &c1);
b.mul_assign(&Fq6 { b.mul_assign(&Fq6 {
c0, c0: c0,
c1, c1: c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}); });

View File

@@ -6,9 +6,7 @@ use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr};
pub struct Fr(FrRepr); pub struct Fr(FrRepr);
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fr_repr_ordering() { fn test_fr_repr_ordering() {
@@ -199,10 +197,7 @@ fn test_fr_repr_num_bits() {
#[test] #[test]
fn test_fr_repr_sub_noborrow() { fn test_fr_repr_sub_noborrow() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FrRepr([ let mut t = FrRepr([
0x8e62a7e85264e2c3, 0x8e62a7e85264e2c3,
@@ -226,7 +221,7 @@ fn test_fr_repr_sub_noborrow() {
); );
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fr::random(&mut rng).into_repr(); let mut a = FrRepr::rand(&mut rng);
a.0[3] >>= 30; a.0[3] >>= 30;
let mut b = a; let mut b = a;
for _ in 0..10 { for _ in 0..10 {
@@ -301,10 +296,7 @@ fn test_fr_legendre() {
#[test] #[test]
fn test_fr_repr_add_nocarry() { fn test_fr_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FrRepr([ let mut t = FrRepr([
0xd64f669809cbc6a4, 0xd64f669809cbc6a4,
@@ -330,9 +322,9 @@ fn test_fr_repr_add_nocarry() {
// Test for the associativity of addition. // Test for the associativity of addition.
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fr::random(&mut rng).into_repr(); let mut a = FrRepr::rand(&mut rng);
let mut b = Fr::random(&mut rng).into_repr(); let mut b = FrRepr::rand(&mut rng);
let mut c = Fr::random(&mut rng).into_repr(); let mut c = FrRepr::rand(&mut rng);
// Unset the first few bits, so that overflow won't occur. // Unset the first few bits, so that overflow won't occur.
a.0[3] >>= 3; a.0[3] >>= 3;
@@ -388,28 +380,27 @@ fn test_fr_is_valid() {
a.0.sub_noborrow(&FrRepr::from(1)); a.0.sub_noborrow(&FrRepr::from(1));
assert!(a.is_valid()); assert!(a.is_valid());
assert!(Fr(FrRepr::from(0)).is_valid()); assert!(Fr(FrRepr::from(0)).is_valid());
assert!(Fr(FrRepr([ assert!(
Fr(FrRepr([
0xffffffff00000000, 0xffffffff00000000,
0x53bda402fffe5bfe, 0x53bda402fffe5bfe,
0x3339d80809a1d805, 0x3339d80809a1d805,
0x73eda753299d7d48 0x73eda753299d7d48
])) ])).is_valid()
.is_valid()); );
assert!(!Fr(FrRepr([ assert!(
!Fr(FrRepr([
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff 0xffffffffffffffff
])) ])).is_valid()
.is_valid()); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
assert!(a.is_valid()); assert!(a.is_valid());
} }
} }
@@ -501,16 +492,13 @@ fn test_fr_add_assign() {
// Test associativity // Test associativity
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Generate a, b, c and ensure (a + b) + c == a + (b + c). // Generate a, b, c and ensure (a + b) + c == a + (b + c).
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
let b = Fr::random(&mut rng); let b = Fr::rand(&mut rng);
let c = Fr::random(&mut rng); let c = Fr::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -598,15 +586,12 @@ fn test_fr_sub_assign() {
); );
} }
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that (a - b) + (b - a) = 0. // Ensure that (a - b) + (b - a) = 0.
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
let b = Fr::random(&mut rng); let b = Fr::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.sub_assign(&b); tmp1.sub_assign(&b);
@@ -642,16 +627,13 @@ fn test_fr_mul_assign() {
])) ]))
); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * b) * c = a * (b * c) // Ensure that (a * b) * c = a * (b * c)
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
let b = Fr::random(&mut rng); let b = Fr::rand(&mut rng);
let c = Fr::random(&mut rng); let c = Fr::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.mul_assign(&b); tmp1.mul_assign(&b);
@@ -667,10 +649,10 @@ fn test_fr_mul_assign() {
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that r * (a + b + c) = r*a + r*b + r*c // Ensure that r * (a + b + c) = r*a + r*b + r*c
let r = Fr::random(&mut rng); let r = Fr::rand(&mut rng);
let mut a = Fr::random(&mut rng); let mut a = Fr::rand(&mut rng);
let mut b = Fr::random(&mut rng); let mut b = Fr::rand(&mut rng);
let mut c = Fr::random(&mut rng); let mut c = Fr::rand(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -705,18 +687,14 @@ fn test_fr_squaring() {
0xb79a310579e76ec2, 0xb79a310579e76ec2,
0xac1da8d0a9af4e5f, 0xac1da8d0a9af4e5f,
0x13f629c49bf23e97 0x13f629c49bf23e97
])) ])).unwrap()
.unwrap()
); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * a) = a^2 // Ensure that (a * a) = a^2
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
let mut tmp = a; let mut tmp = a;
tmp.square(); tmp.square();
@@ -732,16 +710,13 @@ fn test_fr_squaring() {
fn test_fr_inverse() { fn test_fr_inverse() {
assert!(Fr::zero().inverse().is_none()); assert!(Fr::zero().inverse().is_none());
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let one = Fr::one(); let one = Fr::one();
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that a * a^-1 = 1 // Ensure that a * a^-1 = 1
let mut a = Fr::random(&mut rng); let mut a = Fr::rand(&mut rng);
let ainv = a.inverse().unwrap(); let ainv = a.inverse().unwrap();
a.mul_assign(&ainv); a.mul_assign(&ainv);
assert_eq!(a, one); assert_eq!(a, one);
@@ -750,14 +725,11 @@ fn test_fr_inverse() {
#[test] #[test]
fn test_fr_double() { fn test_fr_double() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure doubling a is equivalent to adding a to itself. // Ensure doubling a is equivalent to adding a to itself.
let mut a = Fr::random(&mut rng); let mut a = Fr::rand(&mut rng);
let mut b = a; let mut b = a;
b.add_assign(&a); b.add_assign(&a);
a.double(); a.double();
@@ -774,14 +746,11 @@ fn test_fr_negate() {
assert!(a.is_zero()); assert!(a.is_zero());
} }
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure (a - (-a)) = 0. // Ensure (a - (-a)) = 0.
let mut a = Fr::random(&mut rng); let mut a = Fr::rand(&mut rng);
let mut b = a; let mut b = a;
b.negate(); b.negate();
a.add_assign(&b); a.add_assign(&b);
@@ -792,15 +761,12 @@ fn test_fr_negate() {
#[test] #[test]
fn test_fr_pow() { fn test_fr_pow() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for i in 0..1000 { for i in 0..1000 {
// Exponentiate by various small numbers and ensure it consists with repeated // Exponentiate by various small numbers and ensure it consists with repeated
// multiplication. // multiplication.
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
let target = a.pow(&[i]); let target = a.pow(&[i]);
let mut c = Fr::one(); let mut c = Fr::one();
for _ in 0..i { for _ in 0..i {
@@ -811,7 +777,7 @@ fn test_fr_pow() {
for _ in 0..1000 { for _ in 0..1000 {
// Exponentiating by the modulus should have no effect in a prime field. // Exponentiating by the modulus should have no effect in a prime field.
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
assert_eq!(a, a.pow(Fr::char())); assert_eq!(a, a.pow(Fr::char()));
} }
@@ -821,16 +787,13 @@ fn test_fr_pow() {
fn test_fr_sqrt() { fn test_fr_sqrt() {
use ff::SqrtField; use ff::SqrtField;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero()); assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero());
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a^2) = a or -a // Ensure sqrt(a^2) = a or -a
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
let mut nega = a; let mut nega = a;
nega.negate(); nega.negate();
let mut b = a; let mut b = a;
@@ -843,7 +806,7 @@ fn test_fr_sqrt() {
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a)^2 = a for random a // Ensure sqrt(a)^2 = a for random a
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
if let Some(mut tmp) = a.sqrt() { if let Some(mut tmp) = a.sqrt() {
tmp.square(); tmp.square();
@@ -856,13 +819,14 @@ fn test_fr_sqrt() {
#[test] #[test]
fn test_fr_from_into_repr() { fn test_fr_from_into_repr() {
// r + 1 should not be in the field // r + 1 should not be in the field
assert!(Fr::from_repr(FrRepr([ assert!(
Fr::from_repr(FrRepr([
0xffffffff00000002, 0xffffffff00000002,
0x53bda402fffe5bfe, 0x53bda402fffe5bfe,
0x3339d80809a1d805, 0x3339d80809a1d805,
0x73eda753299d7d48 0x73eda753299d7d48
])) ])).is_err()
.is_err()); );
// r should not be in the field // r should not be in the field
assert!(Fr::from_repr(Fr::char()).is_err()); assert!(Fr::from_repr(Fr::char()).is_err());
@@ -894,14 +858,11 @@ fn test_fr_from_into_repr() {
// Zero should be in the field. // Zero should be in the field.
assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero()); assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero());
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Try to turn Fr elements into representations and back again, and compare. // Try to turn Fr elements into representations and back again, and compare.
let a = Fr::random(&mut rng); let a = Fr::rand(&mut rng);
let a_repr = a.into_repr(); let a_repr = a.into_repr();
let b_repr = FrRepr::from(a); let b_repr = FrRepr::from(a);
assert_eq!(a_repr, b_repr); assert_eq!(a_repr, b_repr);
@@ -965,8 +926,7 @@ fn test_fr_display() {
0x185ec8eb3f5b5aee, 0x185ec8eb3f5b5aee,
0x684499ffe4b9dd99, 0x684499ffe4b9dd99,
0x7c9bba7afb68faa 0x7c9bba7afb68faa
])) ])).unwrap()
.unwrap()
), ),
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string() "Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
); );
@@ -978,8 +938,7 @@ fn test_fr_display() {
0xb0ad10817df79b6a, 0xb0ad10817df79b6a,
0xd034a80a2b74132b, 0xd034a80a2b74132b,
0x41cf9a1336f50719 0x41cf9a1336f50719
])) ])).unwrap()
.unwrap()
), ),
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string() "Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
); );
@@ -1023,5 +982,5 @@ fn fr_field_tests() {
#[test] #[test]
fn fr_repr_tests() { fn fr_repr_tests() {
::tests::repr::random_repr_tests::<Fr>(); ::tests::repr::random_repr_tests::<FrRepr>();
} }

View File

@@ -9,8 +9,8 @@ mod fr;
mod tests; mod tests;
pub use self::ec::{ pub use self::ec::{
G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared, G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared,
G2Uncompressed, G1, G2, G2Uncompressed,
}; };
pub use self::fq::{Fq, FqRepr}; pub use self::fq::{Fq, FqRepr};
pub use self::fq12::Fq12; pub use self::fq12::Fq12;

View File

@@ -2,22 +2,19 @@
// common mistakes or strange code patterns. If the `cargo-clippy` feature // common mistakes or strange code patterns. If the `cargo-clippy` feature
// is provided, all compiler warnings are prohibited. // is provided, all compiler warnings are prohibited.
#![cfg_attr(feature = "cargo-clippy", deny(warnings))] #![cfg_attr(feature = "cargo-clippy", deny(warnings))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))] #![cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] #![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] #![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] #![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] #![cfg_attr(feature = "cargo-clippy", allow(write_literal))]
// Force public structures to implement Debug // Force public structures to implement Debug
#![deny(missing_debug_implementations)] #![deny(missing_debug_implementations)]
extern crate byteorder; extern crate byteorder;
extern crate ff; extern crate ff;
extern crate group; extern crate group;
extern crate rand_core; extern crate rand;
#[cfg(test)]
extern crate rand_xorshift;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;
@@ -37,7 +34,8 @@ pub trait Engine: ScalarEngine {
Base = Self::Fq, Base = Self::Fq,
Scalar = Self::Fr, Scalar = Self::Fr,
Affine = Self::G1Affine, Affine = Self::G1Affine,
> + From<Self::G1Affine>; >
+ From<Self::G1Affine>;
/// The affine representation of an element in G1. /// The affine representation of an element in G1.
type G1Affine: PairingCurveAffine< type G1Affine: PairingCurveAffine<
@@ -47,7 +45,8 @@ pub trait Engine: ScalarEngine {
Projective = Self::G1, Projective = Self::G1,
Pair = Self::G2Affine, Pair = Self::G2Affine,
PairingResult = Self::Fqk, PairingResult = Self::Fqk,
> + From<Self::G1>; >
+ From<Self::G1>;
/// The projective representation of an element in G2. /// The projective representation of an element in G2.
type G2: CurveProjective< type G2: CurveProjective<
@@ -55,7 +54,8 @@ pub trait Engine: ScalarEngine {
Base = Self::Fqe, Base = Self::Fqe,
Scalar = Self::Fr, Scalar = Self::Fr,
Affine = Self::G2Affine, Affine = Self::G2Affine,
> + From<Self::G2Affine>; >
+ From<Self::G2Affine>;
/// The affine representation of an element in G2. /// The affine representation of an element in G2.
type G2Affine: PairingCurveAffine< type G2Affine: PairingCurveAffine<
@@ -65,7 +65,8 @@ pub trait Engine: ScalarEngine {
Projective = Self::G2, Projective = Self::G2,
Pair = Self::G1Affine, Pair = Self::G1Affine,
PairingResult = Self::Fqk, PairingResult = Self::Fqk,
> + From<Self::G2>; >
+ From<Self::G2>;
/// The base field that hosts G1. /// The base field that hosts G1.
type Fq: PrimeField + SqrtField; type Fq: PrimeField + SqrtField;
@@ -96,9 +97,8 @@ pub trait Engine: ScalarEngine {
G2: Into<Self::G2Affine>, G2: Into<Self::G2Affine>,
{ {
Self::final_exponentiation(&Self::miller_loop( Self::final_exponentiation(&Self::miller_loop(
[(&(p.into().prepare()), &(q.into().prepare()))].iter(), [(&(p.into().prepare()), &(q.into().prepare()))].into_iter(),
)) )).unwrap()
.unwrap()
} }
} }

View File

@@ -1,18 +1,14 @@
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use {Engine, Field, PairingCurveAffine, PrimeField}; use {Engine, Field, PairingCurveAffine, PrimeField};
pub fn engine_tests<E: Engine>() { pub fn engine_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..10 { for _ in 0..10 {
let a = E::G1::random(&mut rng).into_affine(); let a = E::G1::rand(&mut rng).into_affine();
let b = E::G2::random(&mut rng).into_affine(); let b = E::G2::rand(&mut rng).into_affine();
assert!(a.pairing_with(&b) == b.pairing_with(&a)); assert!(a.pairing_with(&b) == b.pairing_with(&a));
assert!(a.pairing_with(&b) == E::pairing(a, b)); assert!(a.pairing_with(&b) == E::pairing(a, b));
@@ -22,10 +18,10 @@ pub fn engine_tests<E: Engine>() {
let z1 = E::G1Affine::zero().prepare(); let z1 = E::G1Affine::zero().prepare();
let z2 = E::G2Affine::zero().prepare(); let z2 = E::G2Affine::zero().prepare();
let a = E::G1::random(&mut rng).into_affine().prepare(); let a = E::G1::rand(&mut rng).into_affine().prepare();
let b = E::G2::random(&mut rng).into_affine().prepare(); let b = E::G2::rand(&mut rng).into_affine().prepare();
let c = E::G1::random(&mut rng).into_affine().prepare(); let c = E::G1::rand(&mut rng).into_affine().prepare();
let d = E::G2::random(&mut rng).into_affine().prepare(); let d = E::G2::rand(&mut rng).into_affine().prepare();
assert_eq!( assert_eq!(
E::Fqk::one(), E::Fqk::one(),
@@ -53,15 +49,12 @@ pub fn engine_tests<E: Engine>() {
} }
fn random_miller_loop_tests<E: Engine>() { fn random_miller_loop_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// Exercise the miller loop for a reduced pairing // Exercise the miller loop for a reduced pairing
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::random(&mut rng); let a = E::G1::rand(&mut rng);
let b = E::G2::random(&mut rng); let b = E::G2::rand(&mut rng);
let p2 = E::pairing(a, b); let p2 = E::pairing(a, b);
@@ -75,10 +68,10 @@ fn random_miller_loop_tests<E: Engine>() {
// Exercise a double miller loop // Exercise a double miller loop
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::random(&mut rng); let a = E::G1::rand(&mut rng);
let b = E::G2::random(&mut rng); let b = E::G2::rand(&mut rng);
let c = E::G1::random(&mut rng); let c = E::G1::rand(&mut rng);
let d = E::G2::random(&mut rng); let d = E::G2::rand(&mut rng);
let ab = E::pairing(a, b); let ab = E::pairing(a, b);
let cd = E::pairing(c, d); let cd = E::pairing(c, d);
@@ -99,17 +92,14 @@ fn random_miller_loop_tests<E: Engine>() {
} }
fn random_bilinearity_tests<E: Engine>() { fn random_bilinearity_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::random(&mut rng); let a = E::G1::rand(&mut rng);
let b = E::G2::random(&mut rng); let b = E::G2::rand(&mut rng);
let c = E::Fr::random(&mut rng); let c = E::Fr::rand(&mut rng);
let d = E::Fr::random(&mut rng); let d = E::Fr::rand(&mut rng);
let mut ac = a; let mut ac = a;
ac.mul_assign(c); ac.mul_assign(c);

View File

@@ -1,16 +1,12 @@
use ff::{Field, LegendreSymbol, PrimeField, SqrtField}; use ff::{Field, LegendreSymbol, PrimeField, SqrtField};
use rand_core::{RngCore, SeedableRng}; use rand::{Rng, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) { pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
for i in 0..(maxpower + 1) { for i in 0..(maxpower + 1) {
let mut a = F::random(&mut rng); let mut a = F::rand(&mut rng);
let mut b = a; let mut b = a;
for _ in 0..i { for _ in 0..i {
@@ -24,13 +20,10 @@ pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxp
} }
pub fn random_sqrt_tests<F: SqrtField>() { pub fn random_sqrt_tests<F: SqrtField>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(&mut rng); let a = F::rand(&mut rng);
let mut b = a; let mut b = a;
b.square(); b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
@@ -61,10 +54,7 @@ pub fn random_sqrt_tests<F: SqrtField>() {
} }
pub fn random_field_tests<F: Field>() { pub fn random_field_tests<F: Field>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
random_multiplication_tests::<F, _>(&mut rng); random_multiplication_tests::<F, _>(&mut rng);
random_addition_tests::<F, _>(&mut rng); random_addition_tests::<F, _>(&mut rng);
@@ -86,14 +76,14 @@ pub fn random_field_tests<F: Field>() {
// Multiplication by zero // Multiplication by zero
{ {
let mut a = F::random(&mut rng); let mut a = F::rand(&mut rng);
a.mul_assign(&F::zero()); a.mul_assign(&F::zero());
assert!(a.is_zero()); assert!(a.is_zero());
} }
// Addition by zero // Addition by zero
{ {
let mut a = F::random(&mut rng); let mut a = F::rand(&mut rng);
let copy = a; let copy = a;
a.add_assign(&F::zero()); a.add_assign(&F::zero());
assert_eq!(a, copy); assert_eq!(a, copy);
@@ -116,13 +106,10 @@ pub fn from_str_tests<F: PrimeField>() {
} }
{ {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let n = rng.next_u64(); let n: u64 = rng.gen();
let a = F::from_str(&format!("{}", n)).unwrap(); let a = F::from_str(&format!("{}", n)).unwrap();
let b = F::from_repr(n.into()).unwrap(); let b = F::from_repr(n.into()).unwrap();
@@ -137,11 +124,11 @@ pub fn from_str_tests<F: PrimeField>() {
assert!(F::from_str("00000000000").is_none()); assert!(F::from_str("00000000000").is_none());
} }
fn random_multiplication_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_multiplication_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(rng); let a = F::rand(rng);
let b = F::random(rng); let b = F::rand(rng);
let c = F::random(rng); let c = F::rand(rng);
let mut t0 = a; // (a * b) * c let mut t0 = a; // (a * b) * c
t0.mul_assign(&b); t0.mul_assign(&b);
@@ -160,11 +147,11 @@ fn random_multiplication_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_addition_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_addition_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(rng); let a = F::rand(rng);
let b = F::random(rng); let b = F::rand(rng);
let c = F::random(rng); let c = F::rand(rng);
let mut t0 = a; // (a + b) + c let mut t0 = a; // (a + b) + c
t0.add_assign(&b); t0.add_assign(&b);
@@ -183,10 +170,10 @@ fn random_addition_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_subtraction_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_subtraction_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let b = F::random(rng); let a = F::rand(rng);
let a = F::random(rng); let b = F::rand(rng);
let mut t0 = a; // (a - b) let mut t0 = a; // (a - b)
t0.sub_assign(&b); t0.sub_assign(&b);
@@ -201,9 +188,9 @@ fn random_subtraction_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_negation_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_negation_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(rng); let a = F::rand(rng);
let mut b = a; let mut b = a;
b.negate(); b.negate();
b.add_assign(&a); b.add_assign(&a);
@@ -212,9 +199,9 @@ fn random_negation_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_doubling_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_doubling_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::random(rng); let mut a = F::rand(rng);
let mut b = a; let mut b = a;
a.add_assign(&b); a.add_assign(&b);
b.double(); b.double();
@@ -223,9 +210,9 @@ fn random_doubling_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_squaring_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_squaring_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::random(rng); let mut a = F::rand(rng);
let mut b = a; let mut b = a;
a.mul_assign(&b); a.mul_assign(&b);
b.square(); b.square();
@@ -234,11 +221,11 @@ fn random_squaring_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_inversion_tests<F: Field, R: Rng>(rng: &mut R) {
assert!(F::zero().inverse().is_none()); assert!(F::zero().inverse().is_none());
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::random(rng); let mut a = F::rand(rng);
let b = a.inverse().unwrap(); // probablistically nonzero let b = a.inverse().unwrap(); // probablistically nonzero
a.mul_assign(&b); a.mul_assign(&b);
@@ -246,14 +233,14 @@ fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_expansion_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_expansion_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
let a = F::random(rng); let a = F::rand(rng);
let b = F::random(rng); let b = F::rand(rng);
let c = F::random(rng); let c = F::rand(rng);
let d = F::random(rng); let d = F::rand(rng);
let mut t0 = a; let mut t0 = a;
t0.add_assign(&b); t0.add_assign(&b);

View File

@@ -1,25 +1,21 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::PrimeFieldRepr;
use rand_core::SeedableRng; use rand::{SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
pub fn random_repr_tests<P: PrimeField>() { pub fn random_repr_tests<R: PrimeFieldRepr>() {
random_encoding_tests::<P>(); random_encoding_tests::<R>();
random_shl_tests::<P>(); random_shl_tests::<R>();
random_shr_tests::<P>(); random_shr_tests::<R>();
} }
fn random_encoding_tests<P: PrimeField>() { fn random_encoding_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let r = P::random(&mut rng).into_repr(); let r = R::rand(&mut rng);
// Big endian // Big endian
{ {
let mut rdecoded = <P as PrimeField>::Repr::default(); let mut rdecoded = R::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_be(&mut v).unwrap(); r.write_be(&mut v).unwrap();
@@ -30,7 +26,7 @@ fn random_encoding_tests<P: PrimeField>() {
// Little endian // Little endian
{ {
let mut rdecoded = <P as PrimeField>::Repr::default(); let mut rdecoded = R::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap(); r.write_le(&mut v).unwrap();
@@ -40,8 +36,8 @@ fn random_encoding_tests<P: PrimeField>() {
} }
{ {
let mut rdecoded_le = <P as PrimeField>::Repr::default(); let mut rdecoded_le = R::default();
let mut rdecoded_be_flip = <P as PrimeField>::Repr::default(); let mut rdecoded_be_flip = R::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap(); r.write_le(&mut v).unwrap();
@@ -59,14 +55,11 @@ fn random_encoding_tests<P: PrimeField>() {
} }
} }
fn random_shl_tests<P: PrimeField>() { fn random_shl_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let r = P::random(&mut rng).into_repr(); let r = R::rand(&mut rng);
for shift in 0..(r.num_bits() + 1) { for shift in 0..(r.num_bits() + 1) {
let mut r1 = r; let mut r1 = r;
@@ -83,14 +76,11 @@ fn random_shl_tests<P: PrimeField>() {
} }
} }
fn random_shr_tests<P: PrimeField>() { fn random_shr_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let r = P::random(&mut rng).into_repr(); let r = R::rand(&mut rng);
for shift in 0..(r.num_bits() + 1) { for shift in 0..(r.num_bits() + 1) {
let mut r1 = r; let mut r1 = r;

3
sapling-crypto/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/target/
**/*.rs.bk
Cargo.lock

14
sapling-crypto/COPYRIGHT Normal file
View File

@@ -0,0 +1,14 @@
Copyrights in the "sapling-crypto" library are retained by their contributors. No
copyright assignment is required to contribute to the "sapling-crypto" library.
The "sapling-crypto" library is licensed under either of
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

28
sapling-crypto/Cargo.toml Normal file
View File

@@ -0,0 +1,28 @@
[package]
authors = ["The Hush Developers", "Sean Bowe <sean@z.cash>"]
description = "Cryptographic library for Zcash Sapling"
documentation = "https://github.com/zcash-hackworks/sapling"
homepage = "https://github.com/zcash-hackworks/sapling"
license = "GPLv3 or later"
name = "sapling-crypto"
repository = "https://github.com/zcash-hackworks/sapling"
version = "0.0.1"
[dependencies.pairing]
path = "../pairing"
features = ["expose-arith"]
[dependencies]
bellman = { path = "../bellman" }
ff = { path = "../ff" }
rand = "0.4"
digest = "0.7"
byteorder = "1"
[dependencies.blake2-rfc]
git = "https://git.hush.is/hush/blake2-rfc"
rev = "a3adc1d6859b887457c6ae4821ddad1802dad784"
[dev-dependencies]
hex-literal = "0.1"
rust-crypto = "0.2"

View File

@@ -199,4 +199,3 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.

View File

@@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

23
sapling-crypto/README.md Normal file
View File

@@ -0,0 +1,23 @@
# sapling-crypto
This repository contains a (work-in-progress) implementation of Zcash's "Sapling" cryptography.
## Security Warnings
This library is currently under development and has not been reviewed.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

View File

@@ -0,0 +1,23 @@
#![feature(test)]
extern crate rand;
extern crate test;
extern crate pairing;
extern crate sapling_crypto;
use rand::{Rand, thread_rng};
use pairing::bls12_381::Bls12;
use sapling_crypto::jubjub::JubjubBls12;
use sapling_crypto::pedersen_hash::{pedersen_hash, Personalization};
#[bench]
fn bench_pedersen_hash(b: &mut test::Bencher) {
let params = JubjubBls12::new();
let rng = &mut thread_rng();
let bits = (0..510).map(|_| bool::rand(rng)).collect::<Vec<_>>();
let personalization = Personalization::MerkleTree(31);
b.iter(|| {
pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params)
});
}

View File

@@ -0,0 +1,102 @@
extern crate sapling_crypto;
extern crate bellman;
extern crate rand;
extern crate pairing;
use std::time::{Duration, Instant};
use sapling_crypto::jubjub::{
JubjubBls12,
edwards,
fs,
};
use sapling_crypto::circuit::sapling::{
Spend
};
use sapling_crypto::primitives::{
Diversifier,
ProofGenerationKey,
ValueCommitment
};
use bellman::groth16::*;
use rand::{XorShiftRng, SeedableRng, Rng};
use pairing::bls12_381::{Bls12, Fr};
const TREE_DEPTH: usize = 32;
fn main() {
let jubjub_params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
println!("Creating sample parameters...");
let groth_params = generate_random_parameters::<Bls12, _, _>(
Spend {
params: jubjub_params,
value_commitment: None,
proof_generation_key: None,
payment_address: None,
commitment_randomness: None,
ar: None,
auth_path: vec![None; TREE_DEPTH],
anchor: None
},
rng
).unwrap();
const SAMPLES: u32 = 50;
let mut total_time = Duration::new(0, 0);
for _ in 0..SAMPLES {
let value_commitment = ValueCommitment {
value: 1,
randomness: rng.gen()
};
let nsk: fs::Fs = rng.gen();
let ak = edwards::Point::rand(rng, jubjub_params).mul_by_cofactor(jubjub_params);
let proof_generation_key = ProofGenerationKey {
ak: ak.clone(),
nsk: nsk.clone()
};
let viewing_key = proof_generation_key.into_viewing_key(jubjub_params);
let payment_address;
loop {
let diversifier = Diversifier(rng.gen());
if let Some(p) = viewing_key.into_payment_address(
diversifier,
jubjub_params
)
{
payment_address = p;
break;
}
}
let commitment_randomness: fs::Fs = rng.gen();
let auth_path = vec![Some((rng.gen(), rng.gen())); TREE_DEPTH];
let ar: fs::Fs = rng.gen();
let anchor: Fr = rng.gen();
let start = Instant::now();
let _ = create_random_proof(Spend {
params: jubjub_params,
value_commitment: Some(value_commitment),
proof_generation_key: Some(proof_generation_key),
payment_address: Some(payment_address),
commitment_randomness: Some(commitment_randomness),
ar: Some(ar),
auth_path: auth_path,
anchor: Some(anchor)
}, &groth_params, rng).unwrap();
total_time += start.elapsed();
}
let avg = total_time / SAMPLES;
let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64
+ (avg.as_secs() as f64);
println!("Average proving time (in seconds): {}", avg);
}

View File

@@ -1,10 +1,19 @@
use pairing::Engine; use pairing::{
Engine,
};
use crate::{ConstraintSystem, SynthesisError}; use bellman::{
SynthesisError,
ConstraintSystem
};
use super::boolean::Boolean; use super::boolean::{
Boolean
};
use super::uint32::UInt32; use super::uint32::{
UInt32
};
use super::multieq::MultiEq; use super::multieq::MultiEq;
@@ -56,7 +65,7 @@ const SIGMA: [[usize; 16]; 10] = [
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]
]; ];
/* /*
@@ -89,30 +98,17 @@ fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
c: usize, c: usize,
d: usize, d: usize,
x: &UInt32, x: &UInt32,
y: &UInt32, y: &UInt32
) -> Result<(), SynthesisError> ) -> Result<(), SynthesisError>
where where M: ConstraintSystem<E, Root=MultiEq<E, CS>>
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
{ {
v[a] = UInt32::addmany( v[a] = UInt32::addmany(cs.namespace(|| "mixing step 1"), &[v[a].clone(), v[b].clone(), x.clone()])?;
cs.namespace(|| "mixing step 1"),
&[v[a].clone(), v[b].clone(), x.clone()],
)?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1); v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1);
v[c] = UInt32::addmany( v[c] = UInt32::addmany(cs.namespace(|| "mixing step 3"), &[v[c].clone(), v[d].clone()])?;
cs.namespace(|| "mixing step 3"),
&[v[c].clone(), v[d].clone()],
)?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2); v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2);
v[a] = UInt32::addmany( v[a] = UInt32::addmany(cs.namespace(|| "mixing step 5"), &[v[a].clone(), v[b].clone(), y.clone()])?;
cs.namespace(|| "mixing step 5"),
&[v[a].clone(), v[b].clone(), y.clone()],
)?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3); v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3);
v[c] = UInt32::addmany( v[c] = UInt32::addmany(cs.namespace(|| "mixing step 7"), &[v[c].clone(), v[d].clone()])?;
cs.namespace(|| "mixing step 7"),
&[v[c].clone(), v[d].clone()],
)?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4); v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
Ok(()) Ok(())
@@ -166,13 +162,15 @@ where
END FUNCTION. END FUNCTION.
*/ */
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>( fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS, mut cs: CS,
h: &mut [UInt32], h: &mut [UInt32],
m: &[UInt32], m: &[UInt32],
t: u64, t: u64,
f: bool, f: bool
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError>
{
assert_eq!(h.len(), 8); assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16); assert_eq!(m.len(), 16);
@@ -198,16 +196,10 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
assert_eq!(v.len(), 16); assert_eq!(v.len(), 16);
v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?; v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?;
v[13] = v[13].xor( v[13] = v[13].xor(cs.namespace(|| "second xor"), &UInt32::constant((t >> 32) as u32))?;
cs.namespace(|| "second xor"),
&UInt32::constant((t >> 32) as u32),
)?;
if f { if f {
v[14] = v[14].xor( v[14] = v[14].xor(cs.namespace(|| "third xor"), &UInt32::constant(u32::max_value()))?;
cs.namespace(|| "third xor"),
&UInt32::constant(u32::max_value()),
)?;
} }
{ {
@@ -218,87 +210,15 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
let s = SIGMA[i % 10]; let s = SIGMA[i % 10];
mixing_g( mixing_g(cs.namespace(|| "mixing invocation 1"), &mut v, 0, 4, 8, 12, &m[s[ 0]], &m[s[ 1]])?;
cs.namespace(|| "mixing invocation 1"), mixing_g(cs.namespace(|| "mixing invocation 2"), &mut v, 1, 5, 9, 13, &m[s[ 2]], &m[s[ 3]])?;
&mut v, mixing_g(cs.namespace(|| "mixing invocation 3"), &mut v, 2, 6, 10, 14, &m[s[ 4]], &m[s[ 5]])?;
0, mixing_g(cs.namespace(|| "mixing invocation 4"), &mut v, 3, 7, 11, 15, &m[s[ 6]], &m[s[ 7]])?;
4,
8,
12,
&m[s[0]],
&m[s[1]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 2"),
&mut v,
1,
5,
9,
13,
&m[s[2]],
&m[s[3]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 3"),
&mut v,
2,
6,
10,
14,
&m[s[4]],
&m[s[5]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 4"),
&mut v,
3,
7,
11,
15,
&m[s[6]],
&m[s[7]],
)?;
mixing_g( mixing_g(cs.namespace(|| "mixing invocation 5"), &mut v, 0, 5, 10, 15, &m[s[ 8]], &m[s[ 9]])?;
cs.namespace(|| "mixing invocation 5"), mixing_g(cs.namespace(|| "mixing invocation 6"), &mut v, 1, 6, 11, 12, &m[s[10]], &m[s[11]])?;
&mut v, mixing_g(cs.namespace(|| "mixing invocation 7"), &mut v, 2, 7, 8, 13, &m[s[12]], &m[s[13]])?;
0, mixing_g(cs.namespace(|| "mixing invocation 8"), &mut v, 3, 4, 9, 14, &m[s[14]], &m[s[15]])?;
5,
10,
15,
&m[s[8]],
&m[s[9]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 6"),
&mut v,
1,
6,
11,
12,
&m[s[10]],
&m[s[11]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 7"),
&mut v,
2,
7,
8,
13,
&m[s[12]],
&m[s[13]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 8"),
&mut v,
3,
4,
9,
14,
&m[s[14]],
&m[s[15]],
)?;
} }
} }
@@ -342,8 +262,9 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>( pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS, mut cs: CS,
input: &[Boolean], input: &[Boolean],
personalization: &[u8], personalization: &[u8]
) -> Result<Vec<Boolean>, SynthesisError> { ) -> Result<Vec<Boolean>, SynthesisError>
{
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
assert_eq!(personalization.len(), 8); assert_eq!(personalization.len(), 8);
@@ -358,12 +279,8 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
h.push(UInt32::constant(0x9B05688C)); h.push(UInt32::constant(0x9B05688C));
// Personalization is stored here // Personalization is stored here
h.push(UInt32::constant( h.push(UInt32::constant(0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4])));
0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]), h.push(UInt32::constant(0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8])));
));
h.push(UInt32::constant(
0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]),
));
let mut blocks: Vec<Vec<UInt32>> = vec![]; let mut blocks: Vec<Vec<UInt32>> = vec![];
@@ -395,13 +312,7 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
{ {
let cs = cs.namespace(|| "final block"); let cs = cs.namespace(|| "final block");
blake2s_compression( blake2s_compression(cs, &mut h, &blocks[blocks.len() - 1], (input.len() / 8) as u64, true)?;
cs,
&mut h,
&blocks[blocks.len() - 1],
(input.len() / 8) as u64,
true,
)?;
} }
Ok(h.iter().flat_map(|b| b.into_bits()).collect()) Ok(h.iter().flat_map(|b| b.into_bits()).collect())
@@ -409,15 +320,13 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use blake2s_simd::Params as Blake2sParams; use rand::{XorShiftRng, SeedableRng, Rng};
use pairing::bls12_381::Bls12; use pairing::bls12_381::{Bls12};
use rand_core::{RngCore, SeedableRng}; use ::circuit::boolean::{Boolean, AllocatedBit};
use rand_xorshift::XorShiftRng; use ::circuit::test::TestConstraintSystem;
use super::blake2s; use super::blake2s;
use crate::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::{ConstraintSystem};
use crate::gadgets::test::TestConstraintSystem; use blake2_rfc::blake2s::Blake2s;
use crate::ConstraintSystem;
#[test] #[test]
fn test_blank_hash() { fn test_blank_hash() {
@@ -445,13 +354,7 @@ mod test {
#[test] #[test]
fn test_blake2s_constraints() { fn test_blake2s_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512) let input_bits: Vec<_> = (0..512).map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()).collect();
.map(|i| {
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
.unwrap()
.into()
})
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap(); blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518); assert_eq!(cs.num_constraints(), 21518);
@@ -463,17 +366,11 @@ mod test {
// doesn't result in more constraints. // doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let input_bits: Vec<_> = (0..512) let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) .map(|_| Boolean::constant(rng.gen()))
.chain((0..512).map(|i| { .chain((0..512)
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)) .map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()))
.unwrap()
.into()
}))
.collect(); .collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap(); blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -483,31 +380,21 @@ mod test {
#[test] #[test]
fn test_blake2s_constant_constraints() { fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect();
0xbc, 0xe5,
]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap(); blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert_eq!(cs.num_constraints(), 0); assert_eq!(cs.num_constraints(), 0);
} }
#[test] #[test]
fn test_blake2s() { fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0))
let mut h = Blake2sParams::new() {
.hash_length(32) let mut h = Blake2s::with_params(32, &[], &[], b"12345678");
.personal(b"12345678")
.to_state();
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect(); let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
h.update(&data); h.update(&data);
@@ -521,11 +408,7 @@ mod test {
for bit_i in 0..8 { for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push( input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
} }
} }
@@ -533,19 +416,17 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut s = hash_result let mut s = hash_result.as_ref().iter()
.as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r { for b in r {
match b { match b {
Boolean::Is(b) => { Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap()); assert!(s.next().unwrap() == b.get_value().unwrap());
} },
Boolean::Not(b) => { Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap()); assert!(s.next().unwrap() != b.get_value().unwrap());
} },
Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(input_len == 0); assert!(input_len == 0);
assert!(s.next().unwrap() == b); assert!(s.next().unwrap() == b);

View File

@@ -1,15 +1,23 @@
use ff::Field; use ff::Field;
use pairing::Engine; use pairing::Engine;
use super::boolean::Boolean;
use super::num::{AllocatedNum, Num};
use super::*; use super::*;
use crate::ConstraintSystem; use super::num::{
AllocatedNum,
Num
};
use super::boolean::Boolean;
use bellman::{
ConstraintSystem
};
// Synthesize the constants for each base pattern. // Synthesize the constants for each base pattern.
fn synth<'a, E: Engine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr]) fn synth<'a, E: Engine, I>(
where window_size: usize,
I: IntoIterator<Item = &'a E::Fr>, constants: I,
assignment: &mut [E::Fr]
)
where I: IntoIterator<Item=&'a E::Fr>
{ {
assert_eq!(assignment.len(), 1 << window_size); assert_eq!(assignment.len(), 1 << window_size);
@@ -31,20 +39,16 @@ where
pub fn lookup3_xy<E: Engine, CS>( pub fn lookup3_xy<E: Engine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)], coords: &[(E::Fr, E::Fr)]
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError> ) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
where where CS: ConstraintSystem<E>
CS: ConstraintSystem<E>,
{ {
assert_eq!(bits.len(), 3); assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 8); assert_eq!(coords.len(), 8);
// Calculate the index into `coords` // Calculate the index into `coords`
let i = match ( let i =
bits[0].get_value(), match (bits[0].get_value(), bits[1].get_value(), bits[2].get_value()) {
bits[1].get_value(),
bits[2].get_value(),
) {
(Some(a_value), Some(b_value), Some(c_value)) => { (Some(a_value), Some(b_value), Some(c_value)) => {
let mut tmp = 0; let mut tmp = 0;
if a_value { if a_value {
@@ -57,15 +61,25 @@ where
tmp += 4; tmp += 4;
} }
Some(tmp) Some(tmp)
} },
_ => None, _ => None
}; };
// Allocate the x-coordinate resulting from the lookup // Allocate the x-coordinate resulting from the lookup
let res_x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?; let res_x = AllocatedNum::alloc(
cs.namespace(|| "x"),
|| {
Ok(coords[*i.get()?].0)
}
)?;
// Allocate the y-coordinate resulting from the lookup // Allocate the y-coordinate resulting from the lookup
let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?; let res_y = AllocatedNum::alloc(
cs.namespace(|| "y"),
|| {
Ok(coords[*i.get()?].1)
}
)?;
// Compute the coefficients for the lookup constraints // Compute the coefficients for the lookup constraints
let mut x_coeffs = [E::Fr::zero(); 8]; let mut x_coeffs = [E::Fr::zero(); 8];
@@ -79,38 +93,30 @@ where
cs.enforce( cs.enforce(
|| "x-coordinate lookup", || "x-coordinate lookup",
|lc| { |lc| lc + (x_coeffs[0b001], one)
lc + (x_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, x_coeffs[0b011]) + &bits[1].lc::<E>(one, x_coeffs[0b011])
+ &bits[2].lc::<E>(one, x_coeffs[0b101]) + &bits[2].lc::<E>(one, x_coeffs[0b101])
+ &precomp.lc::<E>(one, x_coeffs[0b111]) + &precomp.lc::<E>(one, x_coeffs[0b111]),
},
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| { |lc| lc + res_x.get_variable()
lc + res_x.get_variable()
- (x_coeffs[0b000], one) - (x_coeffs[0b000], one)
- &bits[1].lc::<E>(one, x_coeffs[0b010]) - &bits[1].lc::<E>(one, x_coeffs[0b010])
- &bits[2].lc::<E>(one, x_coeffs[0b100]) - &bits[2].lc::<E>(one, x_coeffs[0b100])
- &precomp.lc::<E>(one, x_coeffs[0b110]) - &precomp.lc::<E>(one, x_coeffs[0b110]),
},
); );
cs.enforce( cs.enforce(
|| "y-coordinate lookup", || "y-coordinate lookup",
|lc| { |lc| lc + (y_coeffs[0b001], one)
lc + (y_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, y_coeffs[0b011]) + &bits[1].lc::<E>(one, y_coeffs[0b011])
+ &bits[2].lc::<E>(one, y_coeffs[0b101]) + &bits[2].lc::<E>(one, y_coeffs[0b101])
+ &precomp.lc::<E>(one, y_coeffs[0b111]) + &precomp.lc::<E>(one, y_coeffs[0b111]),
},
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| { |lc| lc + res_y.get_variable()
lc + res_y.get_variable()
- (y_coeffs[0b000], one) - (y_coeffs[0b000], one)
- &bits[1].lc::<E>(one, y_coeffs[0b010]) - &bits[1].lc::<E>(one, y_coeffs[0b010])
- &bits[2].lc::<E>(one, y_coeffs[0b100]) - &bits[2].lc::<E>(one, y_coeffs[0b100])
- &precomp.lc::<E>(one, y_coeffs[0b110]) - &precomp.lc::<E>(one, y_coeffs[0b110]),
},
); );
Ok((res_x, res_y)) Ok((res_x, res_y))
@@ -121,16 +127,16 @@ where
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>( pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)], coords: &[(E::Fr, E::Fr)]
) -> Result<(Num<E>, Num<E>), SynthesisError> ) -> Result<(Num<E>, Num<E>), SynthesisError>
where where CS: ConstraintSystem<E>
CS: ConstraintSystem<E>,
{ {
assert_eq!(bits.len(), 3); assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 4); assert_eq!(coords.len(), 4);
// Calculate the index into `coords` // Calculate the index into `coords`
let i = match (bits[0].get_value(), bits[1].get_value()) { let i =
match (bits[0].get_value(), bits[1].get_value()) {
(Some(a_value), Some(b_value)) => { (Some(a_value), Some(b_value)) => {
let mut tmp = 0; let mut tmp = 0;
if a_value { if a_value {
@@ -140,19 +146,22 @@ where
tmp += 2; tmp += 2;
} }
Some(tmp) Some(tmp)
} },
_ => None, _ => None
}; };
// Allocate the y-coordinate resulting from the lookup // Allocate the y-coordinate resulting from the lookup
// and conditional negation // and conditional negation
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { let y = AllocatedNum::alloc(
cs.namespace(|| "y"),
|| {
let mut tmp = coords[*i.get()?].1; let mut tmp = coords[*i.get()?].1;
if *bits[2].get_value().get()? { if *bits[2].get_value().get()? {
tmp.negate(); tmp.negate();
} }
Ok(tmp) Ok(tmp)
})?; }
)?;
let one = CS::one(); let one = CS::one();
@@ -170,16 +179,16 @@ where
.add_bool_with_coeff(one, &bits[1], x_coeffs[0b10]) .add_bool_with_coeff(one, &bits[1], x_coeffs[0b10])
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]); .add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11]) let y_lc = precomp.lc::<E>(one, y_coeffs[0b11]) +
+ &bits[1].lc::<E>(one, y_coeffs[0b10]) &bits[1].lc::<E>(one, y_coeffs[0b10]) +
+ &bits[0].lc::<E>(one, y_coeffs[0b01]) &bits[0].lc::<E>(one, y_coeffs[0b01]) +
+ (y_coeffs[0b00], one); (y_coeffs[0b00], one);
cs.enforce( cs.enforce(
|| "y-coordinate lookup", || "y-coordinate lookup",
|lc| lc + &y_lc + &y_lc, |lc| lc + &y_lc + &y_lc,
|lc| lc + &bits[2].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[2].lc::<E>(one, E::Fr::one()),
|lc| lc + &y_lc - y.get_variable(), |lc| lc + &y_lc - y.get_variable()
); );
Ok((x, y.into())) Ok((x, y.into()))
@@ -187,52 +196,46 @@ where
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use super::*; use super::*;
use crate::gadgets::boolean::{AllocatedBit, Boolean}; use ::circuit::test::*;
use crate::gadgets::test::*; use ::circuit::boolean::{Boolean, AllocatedBit};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_lookup3_xy() { fn test_lookup3_xy() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0656]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.next_u32() % 2 != 0; let a_val = rng.gen();
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0; let b_val = rng.gen();
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0; let c_val = rng.gen();
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()); let c = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c]; let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..8) let points: Vec<(Fr, Fr)> = (0..8).map(|_| (rng.gen(), rng.gen())).collect();
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy(&mut cs, &bits, &points).unwrap(); let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut index = 0; let mut index = 0;
if a_val { if a_val { index += 1 }
index += 1 if b_val { index += 2 }
} if c_val { index += 4 }
if b_val {
index += 2
}
if c_val {
index += 4
}
assert_eq!(res.0.get_value().unwrap(), points[index].0); assert_eq!(res.0.get_value().unwrap(), points[index].0);
assert_eq!(res.1.get_value().unwrap(), points[index].1); assert_eq!(res.1.get_value().unwrap(), points[index].1);
@@ -241,63 +244,53 @@ mod test {
#[test] #[test]
fn test_lookup3_xy_with_conditional_negation() { fn test_lookup3_xy_with_conditional_negation() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.next_u32() % 2 != 0; let a_val = rng.gen();
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0; let b_val = rng.gen();
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0; let c_val = rng.gen();
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()); let c = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c]; let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..4) let points: Vec<(Fr, Fr)> = (0..4).map(|_| (rng.gen(), rng.gen())).collect();
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap(); let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut index = 0; let mut index = 0;
if a_val { if a_val { index += 1 }
index += 1 if b_val { index += 2 }
}
if b_val {
index += 2
}
assert_eq!(res.0.get_value().unwrap(), points[index].0); assert_eq!(res.0.get_value().unwrap(), points[index].0);
let mut tmp = points[index].1; let mut tmp = points[index].1;
if c_val { if c_val { tmp.negate() }
tmp.negate()
}
assert_eq!(res.1.get_value().unwrap(), tmp); assert_eq!(res.1.get_value().unwrap(), tmp);
} }
} }
#[test] #[test]
fn test_synth() { fn test_synth() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let window_size = 4; let window_size = 4;
let mut assignment = vec![Fr::zero(); 1 << window_size]; let mut assignment = vec![Fr::zero(); 1 << window_size];
let constants: Vec<_> = (0..(1 << window_size)) let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::rand(&mut rng)).collect();
.map(|_| Fr::random(&mut rng))
.collect();
synth::<Bls12, _>(window_size, &constants, &mut assignment); synth::<Bls12, _>(window_size, &constants, &mut assignment);

View File

@@ -1,15 +1,23 @@
#[cfg(test)]
pub mod test; pub mod test;
pub mod blake2s;
pub mod boolean; pub mod boolean;
pub mod lookup;
pub mod multieq; pub mod multieq;
pub mod multipack;
pub mod num;
pub mod sha256;
pub mod uint32; pub mod uint32;
pub mod blake2s;
pub mod num;
pub mod lookup;
pub mod ecc;
pub mod pedersen_hash;
pub mod multipack;
pub mod sha256;
use crate::SynthesisError; pub mod sapling;
pub mod sprout;
use bellman::{
SynthesisError
};
// TODO: This should probably be removed and we // TODO: This should probably be removed and we
// should use existing helper methods on `Option` // should use existing helper methods on `Option`
@@ -17,7 +25,7 @@ use crate::SynthesisError;
/// This basically is just an extension to `Option` /// This basically is just an extension to `Option`
/// which allows for a convenient mapping to an /// which allows for a convenient mapping to an
/// error on `None`. /// error on `None`.
pub trait Assignment<T> { trait Assignment<T> {
fn get(&self) -> Result<&T, SynthesisError>; fn get(&self) -> Result<&T, SynthesisError>;
} }
@@ -25,7 +33,7 @@ impl<T> Assignment<T> for Option<T> {
fn get(&self) -> Result<&T, SynthesisError> { fn get(&self) -> Result<&T, SynthesisError> {
match *self { match *self {
Some(ref v) => Ok(v), Some(ref v) => Ok(v),
None => Err(SynthesisError::AssignmentMissing), None => Err(SynthesisError::AssignmentMissing)
} }
} }
} }

View File

@@ -1,7 +1,12 @@
use ff::{Field, PrimeField}; use ff::{Field, PrimeField};
use pairing::Engine; use pairing::Engine;
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{ pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{
cs: CS, cs: CS,
@@ -18,11 +23,12 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
ops: 0, ops: 0,
bits_used: 0, bits_used: 0,
lhs: LinearCombination::zero(), lhs: LinearCombination::zero(),
rhs: LinearCombination::zero(), rhs: LinearCombination::zero()
} }
} }
fn accumulate(&mut self) { fn accumulate(&mut self)
{
let ops = self.ops; let ops = self.ops;
let lhs = self.lhs.clone(); let lhs = self.lhs.clone();
let rhs = self.rhs.clone(); let rhs = self.rhs.clone();
@@ -30,7 +36,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|| format!("multieq {}", ops), || format!("multieq {}", ops),
|_| lhs, |_| lhs,
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| rhs, |_| rhs
); );
self.lhs = LinearCombination::zero(); self.lhs = LinearCombination::zero();
self.rhs = LinearCombination::zero(); self.rhs = LinearCombination::zero();
@@ -42,8 +48,9 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
&mut self, &mut self,
num_bits: usize, num_bits: usize,
lhs: &LinearCombination<E>, lhs: &LinearCombination<E>,
rhs: &LinearCombination<E>, rhs: &LinearCombination<E>
) { )
{
// Check if we will exceed the capacity // Check if we will exceed the capacity
if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) { if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) {
self.accumulate(); self.accumulate();
@@ -66,55 +73,62 @@ impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
} }
} }
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> { impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS>
{
type Root = Self; type Root = Self;
fn one() -> Variable { fn one() -> Variable {
CS::one() CS::one()
} }
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.cs.alloc(annotation, f) self.cs.alloc(annotation, f)
} }
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.cs.alloc_input(annotation, f) self.cs.alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
self.cs.enforce(annotation, a, b, c) self.cs.enforce(annotation, a, b, c)
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where where NR: Into<String>, N: FnOnce() -> NR
NR: Into<String>,
N: FnOnce() -> NR,
{ {
self.cs.get_root().push_namespace(name_fn) self.cs.get_root().push_namespace(name_fn)
} }
fn pop_namespace(&mut self) { fn pop_namespace(&mut self)
{
self.cs.get_root().pop_namespace() self.cs.get_root().pop_namespace()
} }
fn get_root(&mut self) -> &mut Self::Root { fn get_root(&mut self) -> &mut Self::Root
{
self self
} }
} }

View File

@@ -0,0 +1,114 @@
use ff::{Field, PrimeField};
use pairing::Engine;
use bellman::{ConstraintSystem, SynthesisError};
use super::boolean::{Boolean};
use super::num::Num;
use super::Assignment;
/// Takes a sequence of booleans and exposes them as compact
/// public inputs
pub fn pack_into_inputs<E, CS>(
mut cs: CS,
bits: &[Boolean]
) -> Result<(), SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
{
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate()
{
let mut num = Num::<E>::zero();
let mut coeff = E::Fr::one();
for bit in bits {
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
coeff.double();
}
let input = cs.alloc_input(|| format!("input {}", i), || {
Ok(*num.get_value().get()?)
})?;
// num * 1 = input
cs.enforce(
|| format!("packing constraint {}", i),
|_| num.lc(E::Fr::one()),
|lc| lc + CS::one(),
|lc| lc + input
);
}
Ok(())
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool>
{
bytes.iter()
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool>
{
bytes.iter()
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn compute_multipacking<E: Engine>(
bits: &[bool]
) -> Vec<E::Fr>
{
let mut result = vec![];
for bits in bits.chunks(E::Fr::CAPACITY as usize)
{
let mut cur = E::Fr::zero();
let mut coeff = E::Fr::one();
for bit in bits {
if *bit {
cur.add_assign(&coeff);
}
coeff.double();
}
result.push(cur);
}
result
}
#[test]
fn test_multipacking() {
use rand::{SeedableRng, Rng, XorShiftRng};
use bellman::{ConstraintSystem};
use pairing::bls12_381::{Bls12};
use ::circuit::test::*;
use super::boolean::{AllocatedBit, Boolean};
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for num_bits in 0..1500 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let bits: Vec<bool> = (0..num_bits).map(|_| rng.gen()).collect();
let circuit_bits = bits.iter().enumerate()
.map(|(i, &b)| {
Boolean::from(
AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)),
Some(b)
).unwrap()
)
})
.collect::<Vec<_>>();
let expected_inputs = compute_multipacking::<Bls12>(&bits);
pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap();
assert!(cs.is_satisfied());
assert!(cs.verify(&expected_inputs));
}
}

View File

@@ -1,61 +1,78 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
use pairing::Engine; use pairing::Engine;
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
use super::Assignment; use super::{
Assignment
};
use super::boolean::{self, AllocatedBit, Boolean}; use super::boolean::{
self,
Boolean,
AllocatedBit
};
pub struct AllocatedNum<E: Engine> { pub struct AllocatedNum<E: Engine> {
value: Option<E::Fr>, value: Option<E::Fr>,
variable: Variable, variable: Variable
} }
impl<E: Engine> Clone for AllocatedNum<E> { impl<E: Engine> Clone for AllocatedNum<E> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
AllocatedNum { AllocatedNum {
value: self.value, value: self.value,
variable: self.variable, variable: self.variable
} }
} }
} }
impl<E: Engine> AllocatedNum<E> { impl<E: Engine> AllocatedNum<E> {
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError> pub fn alloc<CS, F>(
where mut cs: CS,
CS: ConstraintSystem<E>, value: F,
F: FnOnce() -> Result<E::Fr, SynthesisError>, ) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>,
F: FnOnce() -> Result<E::Fr, SynthesisError>
{ {
let mut new_value = None; let mut new_value = None;
let var = cs.alloc( let var = cs.alloc(|| "num", || {
|| "num",
|| {
let tmp = value()?; let tmp = value()?;
new_value = Some(tmp); new_value = Some(tmp);
Ok(tmp) Ok(tmp)
}, })?;
)?;
Ok(AllocatedNum { Ok(AllocatedNum {
value: new_value, value: new_value,
variable: var, variable: var
}) })
} }
pub fn inputize<CS>(&self, mut cs: CS) -> Result<(), SynthesisError> pub fn inputize<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?; let input = cs.alloc_input(
|| "input variable",
|| {
Ok(*self.value.get()?)
}
)?;
cs.enforce( cs.enforce(
|| "enforce input is correct", || "enforce input is correct",
|lc| lc + input, |lc| lc + input,
|lc| lc + CS::one(), |lc| lc + CS::one(),
|lc| lc + self.variable, |lc| lc + self.variable
); );
Ok(()) Ok(())
@@ -66,17 +83,18 @@ impl<E: Engine> AllocatedNum<E> {
/// order, requiring that the representation /// order, requiring that the representation
/// strictly exists "in the field" (i.e., a /// strictly exists "in the field" (i.e., a
/// congruency is not allowed.) /// congruency is not allowed.)
pub fn into_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> pub fn into_bits_le_strict<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{ {
pub fn kary_and<E, CS>( pub fn kary_and<E, CS>(
mut cs: CS, mut cs: CS,
v: &[AllocatedBit], v: &[AllocatedBit]
) -> Result<AllocatedBit, SynthesisError> ) -> Result<AllocatedBit, SynthesisError>
where where E: Engine,
E: Engine, CS: ConstraintSystem<E>
CS: ConstraintSystem<E>,
{ {
assert!(v.len() > 0); assert!(v.len() > 0);
@@ -91,7 +109,7 @@ impl<E: Engine> AllocatedNum<E> {
cur = Some(AllocatedBit::and( cur = Some(AllocatedBit::and(
cs.namespace(|| format!("and {}", i)), cs.namespace(|| format!("and {}", i)),
cur.as_ref().unwrap(), cur.as_ref().unwrap(),
v, v
)?); )?);
} }
} }
@@ -127,7 +145,10 @@ impl<E: Engine> AllocatedNum<E> {
if b { if b {
// This is part of a run of ones. Let's just // This is part of a run of ones. Let's just
// allocate the boolean with the expected value. // allocate the boolean with the expected value.
let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?; let a_bit = AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)),
a_bit
)?;
// ... and add it to the current run of ones. // ... and add it to the current run of ones.
current_run.push(a_bit.clone()); current_run.push(a_bit.clone());
result.push(a_bit); result.push(a_bit);
@@ -141,7 +162,7 @@ impl<E: Engine> AllocatedNum<E> {
} }
last_run = Some(kary_and( last_run = Some(kary_and(
cs.namespace(|| format!("run ending at {}", i)), cs.namespace(|| format!("run ending at {}", i)),
&current_run, &current_run
)?); )?);
current_run.truncate(0); current_run.truncate(0);
} }
@@ -154,7 +175,7 @@ impl<E: Engine> AllocatedNum<E> {
let a_bit = AllocatedBit::alloc_conditionally( let a_bit = AllocatedBit::alloc_conditionally(
cs.namespace(|| format!("bit {}", i)), cs.namespace(|| format!("bit {}", i)),
a_bit, a_bit,
&last_run.as_ref().expect("char always starts with a one"), &last_run.as_ref().expect("char always starts with a one")
)?; )?;
result.push(a_bit); result.push(a_bit);
} }
@@ -180,7 +201,12 @@ impl<E: Engine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); cs.enforce(
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
// Convert into booleans, and reverse for little-endian bit order // Convert into booleans, and reverse for little-endian bit order
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect()) Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
@@ -189,11 +215,16 @@ impl<E: Engine> AllocatedNum<E> {
/// Convert the allocated number into its little-endian representation. /// Convert the allocated number into its little-endian representation.
/// Note that this does not strongly enforce that the commitment is /// Note that this does not strongly enforce that the commitment is
/// "in the field." /// "in the field."
pub fn into_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> pub fn into_bits_le<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?; let bits = boolean::field_into_allocated_bits_le(
&mut cs,
self.value
)?;
let mut lc = LinearCombination::zero(); let mut lc = LinearCombination::zero();
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
@@ -206,82 +237,86 @@ impl<E: Engine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); cs.enforce(
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect()) Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
} }
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError> pub fn mul<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS,
other: &Self
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc( let var = cs.alloc(|| "product num", || {
|| "product num",
|| {
let mut tmp = *self.value.get()?; let mut tmp = *self.value.get()?;
tmp.mul_assign(other.value.get()?); tmp.mul_assign(other.value.get()?);
value = Some(tmp); value = Some(tmp);
Ok(tmp) Ok(tmp)
}, })?;
)?;
// Constrain: a * b = ab // Constrain: a * b = ab
cs.enforce( cs.enforce(
|| "multiplication constraint", || "multiplication constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + other.variable, |lc| lc + other.variable,
|lc| lc + var, |lc| lc + var
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value: value,
variable: var, variable: var
}) })
} }
pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError> pub fn square<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc( let var = cs.alloc(|| "squared num", || {
|| "squared num",
|| {
let mut tmp = *self.value.get()?; let mut tmp = *self.value.get()?;
tmp.square(); tmp.square();
value = Some(tmp); value = Some(tmp);
Ok(tmp) Ok(tmp)
}, })?;
)?;
// Constrain: a * a = aa // Constrain: a * a = aa
cs.enforce( cs.enforce(
|| "squaring constraint", || "squaring constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + var, |lc| lc + var
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value: value,
variable: var, variable: var
}) })
} }
pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError> pub fn assert_nonzero<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let inv = cs.alloc( let inv = cs.alloc(|| "ephemeral inverse", || {
|| "ephemeral inverse",
|| {
let tmp = *self.value.get()?; let tmp = *self.value.get()?;
if tmp.is_zero() { if tmp.is_zero() {
@@ -289,8 +324,7 @@ impl<E: Engine> AllocatedNum<E> {
} else { } else {
Ok(tmp.inverse().unwrap()) Ok(tmp.inverse().unwrap())
} }
}, })?;
)?;
// Constrain a * inv = 1, which is only valid // Constrain a * inv = 1, which is only valid
// iff a has a multiplicative inverse, untrue // iff a has a multiplicative inverse, untrue
@@ -299,7 +333,7 @@ impl<E: Engine> AllocatedNum<E> {
|| "nonzero assertion constraint", || "nonzero assertion constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + inv, |lc| lc + inv,
|lc| lc + CS::one(), |lc| lc + CS::one()
); );
Ok(()) Ok(())
@@ -312,39 +346,44 @@ impl<E: Engine> AllocatedNum<E> {
mut cs: CS, mut cs: CS,
a: &Self, a: &Self,
b: &Self, b: &Self,
condition: &Boolean, condition: &Boolean
) -> Result<(Self, Self), SynthesisError> ) -> Result<(Self, Self), SynthesisError>
where where CS: ConstraintSystem<E>
CS: ConstraintSystem<E>,
{ {
let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || { let c = Self::alloc(
cs.namespace(|| "conditional reversal result 1"),
|| {
if *condition.get_value().get()? { if *condition.get_value().get()? {
Ok(*b.value.get()?) Ok(*b.value.get()?)
} else { } else {
Ok(*a.value.get()?) Ok(*a.value.get()?)
} }
})?; }
)?;
cs.enforce( cs.enforce(
|| "first conditional reversal", || "first conditional reversal",
|lc| lc + a.variable - b.variable, |lc| lc + a.variable - b.variable,
|_| condition.lc(CS::one(), E::Fr::one()), |_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + a.variable - c.variable, |lc| lc + a.variable - c.variable
); );
let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || { let d = Self::alloc(
cs.namespace(|| "conditional reversal result 2"),
|| {
if *condition.get_value().get()? { if *condition.get_value().get()? {
Ok(*a.value.get()?) Ok(*a.value.get()?)
} else { } else {
Ok(*b.value.get()?) Ok(*b.value.get()?)
} }
})?; }
)?;
cs.enforce( cs.enforce(
|| "second conditional reversal", || "second conditional reversal",
|lc| lc + b.variable - a.variable, |lc| lc + b.variable - a.variable,
|_| condition.lc(CS::one(), E::Fr::one()), |_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + b.variable - d.variable, |lc| lc + b.variable - d.variable
); );
Ok((c, d)) Ok((c, d))
@@ -361,14 +400,14 @@ impl<E: Engine> AllocatedNum<E> {
pub struct Num<E: Engine> { pub struct Num<E: Engine> {
value: Option<E::Fr>, value: Option<E::Fr>,
lc: LinearCombination<E>, lc: LinearCombination<E>
} }
impl<E: Engine> From<AllocatedNum<E>> for Num<E> { impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
fn from(num: AllocatedNum<E>) -> Num<E> { fn from(num: AllocatedNum<E>) -> Num<E> {
Num { Num {
value: num.value, value: num.value,
lc: LinearCombination::<E>::zero() + num.variable, lc: LinearCombination::<E>::zero() + num.variable
} }
} }
} }
@@ -377,7 +416,7 @@ impl<E: Engine> Num<E> {
pub fn zero() -> Self { pub fn zero() -> Self {
Num { Num {
value: Some(E::Fr::zero()), value: Some(E::Fr::zero()),
lc: LinearCombination::zero(), lc: LinearCombination::zero()
} }
} }
@@ -389,7 +428,13 @@ impl<E: Engine> Num<E> {
LinearCombination::zero() + (coeff, &self.lc) LinearCombination::zero() + (coeff, &self.lc)
} }
pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: E::Fr) -> Self { pub fn add_bool_with_coeff(
self,
one: Variable,
bit: &Boolean,
coeff: E::Fr
) -> Self
{
let newval = match (self.value, bit.get_value()) { let newval = match (self.value, bit.get_value()) {
(Some(mut curval), Some(bval)) => { (Some(mut curval), Some(bval)) => {
if bval { if bval {
@@ -397,27 +442,25 @@ impl<E: Engine> Num<E> {
} }
Some(curval) Some(curval)
} },
_ => None, _ => None
}; };
Num { Num {
value: newval, value: newval,
lc: self.lc + &bit.lc(one, coeff), lc: self.lc + &bit.lc(one, coeff)
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::ConstraintSystem; use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use bellman::{ConstraintSystem};
use ff::{BitIterator, Field, PrimeField}; use ff::{BitIterator, Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::SeedableRng; use ::circuit::test::*;
use rand_xorshift::XorShiftRng;
use super::{AllocatedNum, Boolean}; use super::{AllocatedNum, Boolean};
use crate::gadgets::test::*;
#[test] #[test]
fn test_allocated_num() { fn test_allocated_num() {
@@ -446,10 +489,8 @@ mod test {
fn test_num_multiplication() { fn test_num_multiplication() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap();
AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap(); let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
let n2 =
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
let n3 = n.mul(&mut cs, &n2).unwrap(); let n3 = n.mul(&mut cs, &n2).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -461,15 +502,12 @@ mod test {
#[test] #[test]
fn test_num_conditional_reversal() { fn test_num_conditional_reversal() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let condition = Boolean::constant(false); let condition = Boolean::constant(false);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -482,8 +520,8 @@ mod test {
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let condition = Boolean::constant(true); let condition = Boolean::constant(true);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -530,21 +568,15 @@ mod test {
cs.set("bit 254/boolean", Fr::one()); cs.set("bit 254/boolean", Fr::one());
// this makes the conditional boolean constraint fail // this makes the conditional boolean constraint fail
assert_eq!( assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint");
cs.which_is_unsatisfied().unwrap(),
"bit 254/boolean constraint"
);
} }
#[test] #[test]
fn test_into_bits() { fn test_into_bits() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for i in 0..200 { for i in 0..200 {
let r = Fr::random(&mut rng); let r = Fr::rand(&mut rng);
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
@@ -557,10 +589,7 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
for (b, a) in BitIterator::new(r.into_repr()) for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) {
.skip(1)
.zip(bits.iter().rev())
{
if let &Boolean::Is(ref a) = a { if let &Boolean::Is(ref a) = a {
assert_eq!(b, a.get_value().unwrap()); assert_eq!(b, a.get_value().unwrap());
} else { } else {
@@ -568,7 +597,7 @@ mod test {
} }
} }
cs.set("num", Fr::random(&mut rng)); cs.set("num", Fr::rand(&mut rng));
assert!(!cs.is_satisfied()); assert!(!cs.is_satisfied());
cs.set("num", r); cs.set("num", r);
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());

View File

@@ -0,0 +1,194 @@
use super::*;
use super::ecc::{
MontgomeryPoint,
EdwardsPoint
};
use super::boolean::Boolean;
use ::jubjub::*;
use bellman::{
ConstraintSystem
};
use super::lookup::*;
pub use pedersen_hash::Personalization;
impl Personalization {
fn get_constant_bools(&self) -> Vec<Boolean> {
self.get_bits()
.into_iter()
.map(|e| Boolean::constant(e))
.collect()
}
}
pub fn pedersen_hash<E: JubjubEngine, CS>(
mut cs: CS,
personalization: Personalization,
bits: &[Boolean],
params: &E::Params
) -> Result<EdwardsPoint<E>, SynthesisError>
where CS: ConstraintSystem<E>
{
let personalization = personalization.get_constant_bools();
assert_eq!(personalization.len(), 6);
let mut edwards_result = None;
let mut bits = personalization.iter().chain(bits.iter());
let mut segment_generators = params.pedersen_circuit_generators().iter();
let boolean_false = Boolean::constant(false);
let mut segment_i = 0;
loop {
let mut segment_result = None;
let mut segment_windows = &segment_generators.next()
.expect("enough segments")[..];
let mut window_i = 0;
while let Some(a) = bits.next() {
let b = bits.next().unwrap_or(&boolean_false);
let c = bits.next().unwrap_or(&boolean_false);
let tmp = lookup3_xy_with_conditional_negation(
cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)),
&[a.clone(), b.clone(), c.clone()],
&segment_windows[0]
)?;
let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1);
match segment_result {
None => {
segment_result = Some(tmp);
},
Some(ref mut segment_result) => {
*segment_result = tmp.add(
cs.namespace(|| format!("addition of segment {}, window {}", segment_i, window_i)),
segment_result,
params
)?;
}
}
segment_windows = &segment_windows[1..];
if segment_windows.len() == 0 {
break;
}
window_i += 1;
}
match segment_result {
Some(segment_result) => {
// Convert this segment into twisted Edwards form.
let segment_result = segment_result.into_edwards(
cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)),
params
)?;
match edwards_result {
Some(ref mut edwards_result) => {
*edwards_result = segment_result.add(
cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)),
edwards_result,
params
)?;
},
None => {
edwards_result = Some(segment_result);
}
}
},
None => {
// We didn't process any new bits.
break;
}
}
segment_i += 1;
}
Ok(edwards_result.unwrap())
}
#[cfg(test)]
mod test {
use rand::{SeedableRng, Rng, XorShiftRng};
use super::*;
use ::circuit::test::*;
use ::circuit::boolean::{Boolean, AllocatedBit};
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
#[test]
fn test_pedersen_hash_constraints() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let params = &JubjubBls12::new();
let mut cs = TestConstraintSystem::<Bls12>::new();
let input: Vec<bool> = (0..(Fr::NUM_BITS * 2)).map(|_| rng.gen()).collect();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap()
)
}).collect();
pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::NoteCommitment,
&input_bools,
params
).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1377);
}
#[test]
fn test_pedersen_hash() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let params = &JubjubBls12::new();
for length in 0..751 {
for _ in 0..5 {
let mut input: Vec<bool> = (0..length).map(|_| rng.gen()).collect();
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap()
)
}).collect();
let res = pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::MerkleTree(1),
&input_bools,
params
).unwrap();
assert!(cs.is_satisfied());
let expected = ::pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(1),
input.clone().into_iter(),
params
).into_xy();
assert_eq!(res.get_x().get_value().unwrap(), expected.0);
assert_eq!(res.get_y().get_value().unwrap(), expected.1);
// Test against the output of a different personalization
let unexpected = ::pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(0),
input.into_iter(),
params
).into_xy();
assert!(res.get_x().get_value().unwrap() != unexpected.0);
assert!(res.get_y().get_value().unwrap() != unexpected.1);
}
}
}
}

View File

@@ -1,22 +1,33 @@
use ff::{Field, PrimeField, PrimeFieldRepr}; use ff::{Field, PrimeField, PrimeFieldRepr};
use bellman::{Circuit, ConstraintSystem, SynthesisError}; use bellman::{
SynthesisError,
ConstraintSystem,
Circuit
};
use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; use jubjub::{
JubjubEngine,
FixedGenerators
};
use zcash_primitives::constants; use constants;
use zcash_primitives::primitives::{PaymentAddress, ProofGenerationKey, ValueCommitment}; use primitives::{
ValueCommitment,
ProofGenerationKey,
PaymentAddress
};
use super::Assignment;
use super::boolean;
use super::ecc; use super::ecc;
use super::pedersen_hash; use super::pedersen_hash;
use bellman::gadgets::blake2s; use super::blake2s;
use bellman::gadgets::boolean; use super::num;
use bellman::gadgets::multipack; use super::multipack;
use bellman::gadgets::num;
use bellman::gadgets::Assignment;
pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH; pub const TREE_DEPTH: usize = 32;
/// This is an instance of the `Spend` circuit. /// This is an instance of the `Spend` circuit.
pub struct Spend<'a, E: JubjubEngine> { pub struct Spend<'a, E: JubjubEngine> {
@@ -43,7 +54,7 @@ pub struct Spend<'a, E: JubjubEngine> {
/// The anchor; the root of the tree. If the note being /// The anchor; the root of the tree. If the note being
/// spent is zero-value, this can be anything. /// spent is zero-value, this can be anything.
pub anchor: Option<E::Fr>, pub anchor: Option<E::Fr>
} }
/// This is an output circuit instance. /// This is an output circuit instance.
@@ -60,7 +71,7 @@ pub struct Output<'a, E: JubjubEngine> {
pub commitment_randomness: Option<E::Fs>, pub commitment_randomness: Option<E::Fs>,
/// The ephemeral secret key for DH with recipient /// The ephemeral secret key for DH with recipient
pub esk: Option<E::Fs>, pub esk: Option<E::Fs>
} }
/// Exposes a Pedersen commitment to the value as an /// Exposes a Pedersen commitment to the value as an
@@ -68,16 +79,15 @@ pub struct Output<'a, E: JubjubEngine> {
fn expose_value_commitment<E, CS>( fn expose_value_commitment<E, CS>(
mut cs: CS, mut cs: CS,
value_commitment: Option<ValueCommitment<E>>, value_commitment: Option<ValueCommitment<E>>,
params: &E::Params, params: &E::Params
) -> Result<Vec<boolean::Boolean>, SynthesisError> ) -> Result<Vec<boolean::Boolean>, SynthesisError>
where where E: JubjubEngine,
E: JubjubEngine, CS: ConstraintSystem<E>
CS: ConstraintSystem<E>,
{ {
// Booleanize the value into little-endian bit order // Booleanize the value into little-endian bit order
let value_bits = boolean::u64_into_boolean_vec_le( let value_bits = boolean::u64_into_boolean_vec_le(
cs.namespace(|| "value"), cs.namespace(|| "value"),
value_commitment.as_ref().map(|c| c.value), value_commitment.as_ref().map(|c| c.value)
)?; )?;
// Compute the note value in the exponent // Compute the note value in the exponent
@@ -85,7 +95,7 @@ where
cs.namespace(|| "compute the value in the exponent"), cs.namespace(|| "compute the value in the exponent"),
FixedGenerators::ValueCommitmentValue, FixedGenerators::ValueCommitmentValue,
&value_bits, &value_bits,
params, params
)?; )?;
// Booleanize the randomness. This does not ensure // Booleanize the randomness. This does not ensure
@@ -93,7 +103,7 @@ where
// it doesn't matter for security. // it doesn't matter for security.
let rcv = boolean::field_into_boolean_vec_le( let rcv = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcv"), cs.namespace(|| "rcv"),
value_commitment.as_ref().map(|c| c.randomness), value_commitment.as_ref().map(|c| c.randomness)
)?; )?;
// Compute the randomness in the exponent // Compute the randomness in the exponent
@@ -101,11 +111,15 @@ where
cs.namespace(|| "computation of rcv"), cs.namespace(|| "computation of rcv"),
FixedGenerators::ValueCommitmentRandomness, FixedGenerators::ValueCommitmentRandomness,
&rcv, &rcv,
params, params
)?; )?;
// Compute the Pedersen commitment to the value // Compute the Pedersen commitment to the value
let cv = value.add(cs.namespace(|| "computation of cv"), &rcv, params)?; let cv = value.add(
cs.namespace(|| "computation of cv"),
&rcv,
params
)?;
// Expose the commitment as an input to the circuit // Expose the commitment as an input to the circuit
cv.inputize(cs.namespace(|| "commitment point"))?; cv.inputize(cs.namespace(|| "commitment point"))?;
@@ -114,32 +128,43 @@ where
} }
impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> { impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>
{
// Prover witnesses ak (ensures that it's on the curve) // Prover witnesses ak (ensures that it's on the curve)
let ak = ecc::EdwardsPoint::witness( let ak = ecc::EdwardsPoint::witness(
cs.namespace(|| "ak"), cs.namespace(|| "ak"),
self.proof_generation_key.as_ref().map(|k| k.ak.clone()), self.proof_generation_key.as_ref().map(|k| k.ak.clone()),
self.params, self.params
)?; )?;
// There are no sensible attacks on small order points // There are no sensible attacks on small order points
// of ak (that we're aware of!) but it's a cheap check, // of ak (that we're aware of!) but it's a cheap check,
// so we do it. // so we do it.
ak.assert_not_small_order(cs.namespace(|| "ak not small order"), self.params)?; ak.assert_not_small_order(
cs.namespace(|| "ak not small order"),
self.params
)?;
// Rerandomize ak and expose it as an input to the circuit // Rerandomize ak and expose it as an input to the circuit
{ {
let ar = boolean::field_into_boolean_vec_le(cs.namespace(|| "ar"), self.ar)?; let ar = boolean::field_into_boolean_vec_le(
cs.namespace(|| "ar"),
self.ar
)?;
// Compute the randomness in the exponent // Compute the randomness in the exponent
let ar = ecc::fixed_base_multiplication( let ar = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of randomization for the signing key"), cs.namespace(|| "computation of randomization for the signing key"),
FixedGenerators::SpendingKeyGenerator, FixedGenerators::SpendingKeyGenerator,
&ar, &ar,
self.params, self.params
)?; )?;
let rk = ak.add(cs.namespace(|| "computation of rk"), &ar, self.params)?; let rk = ak.add(
cs.namespace(|| "computation of rk"),
&ar,
self.params
)?;
rk.inputize(cs.namespace(|| "rk"))?; rk.inputize(cs.namespace(|| "rk"))?;
} }
@@ -150,7 +175,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Witness nsk as bits // Witness nsk as bits
let nsk = boolean::field_into_boolean_vec_le( let nsk = boolean::field_into_boolean_vec_le(
cs.namespace(|| "nsk"), cs.namespace(|| "nsk"),
self.proof_generation_key.as_ref().map(|k| k.nsk.clone()), self.proof_generation_key.as_ref().map(|k| k.nsk.clone())
)?; )?;
// NB: We don't ensure that the bit representation of nsk // NB: We don't ensure that the bit representation of nsk
@@ -163,7 +188,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of nk"), cs.namespace(|| "computation of nk"),
FixedGenerators::ProofGenerationKey, FixedGenerators::ProofGenerationKey,
&nsk, &nsk,
self.params, self.params
)?; )?;
} }
@@ -171,7 +196,9 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let mut ivk_preimage = vec![]; let mut ivk_preimage = vec![];
// Place ak in the preimage for CRH^ivk // Place ak in the preimage for CRH^ivk
ivk_preimage.extend(ak.repr(cs.namespace(|| "representation of ak"))?); ivk_preimage.extend(
ak.repr(cs.namespace(|| "representation of ak"))?
);
// This is the nullifier preimage for PRF^nf // This is the nullifier preimage for PRF^nf
let mut nf_preimage = vec![]; let mut nf_preimage = vec![];
@@ -179,7 +206,9 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Extend ivk and nf preimages with the representation of // Extend ivk and nf preimages with the representation of
// nk. // nk.
{ {
let repr_nk = nk.repr(cs.namespace(|| "representation of nk"))?; let repr_nk = nk.repr(
cs.namespace(|| "representation of nk")
)?;
ivk_preimage.extend(repr_nk.iter().cloned()); ivk_preimage.extend(repr_nk.iter().cloned());
nf_preimage.extend(repr_nk); nf_preimage.extend(repr_nk);
@@ -192,7 +221,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let mut ivk = blake2s::blake2s( let mut ivk = blake2s::blake2s(
cs.namespace(|| "computation of ivk"), cs.namespace(|| "computation of ivk"),
&ivk_preimage, &ivk_preimage,
constants::CRH_IVK_PERSONALIZATION, constants::CRH_IVK_PERSONALIZATION
)?; )?;
// drop_5 to ensure it's in the field // drop_5 to ensure it's in the field
@@ -210,7 +239,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
ecc::EdwardsPoint::witness( ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"), cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)), self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params, self.params
)? )?
}; };
@@ -218,10 +247,17 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// is already done in the Output circuit, and this proof ensures // is already done in the Output circuit, and this proof ensures
// g_d is bound to a product of that check, but for defense in // g_d is bound to a product of that check, but for defense in
// depth let's check it anyway. It's cheap. // depth let's check it anyway. It's cheap.
g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?; g_d.assert_not_small_order(
cs.namespace(|| "g_d not small order"),
self.params
)?;
// Compute pk_d = g_d^ivk // Compute pk_d = g_d^ivk
let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk, self.params)?; let pk_d = g_d.mul(
cs.namespace(|| "compute pk_d"),
&ivk,
self.params
)?;
// Compute note contents: // Compute note contents:
// value (in big endian) followed by g_d and pk_d // value (in big endian) followed by g_d and pk_d
@@ -235,14 +271,18 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let value_bits = expose_value_commitment( let value_bits = expose_value_commitment(
cs.namespace(|| "value commitment"), cs.namespace(|| "value commitment"),
self.value_commitment, self.value_commitment,
self.params, self.params
)?; )?;
// Compute the note's value as a linear combination // Compute the note's value as a linear combination
// of the bits. // of the bits.
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
for bit in &value_bits { for bit in &value_bits {
value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff); value_num = value_num.add_bool_with_coeff(
CS::one(),
bit,
coeff
);
coeff.double(); coeff.double();
} }
@@ -251,10 +291,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
} }
// Place g_d in the note // Place g_d in the note
note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?); note_contents.extend(
g_d.repr(cs.namespace(|| "representation of g_d"))?
);
// Place pk_d in the note // Place pk_d in the note
note_contents.extend(pk_d.repr(cs.namespace(|| "representation of pk_d"))?); note_contents.extend(
pk_d.repr(cs.namespace(|| "representation of pk_d"))?
);
assert_eq!( assert_eq!(
note_contents.len(), note_contents.len(),
@@ -268,14 +312,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "note content hash"), cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment, pedersen_hash::Personalization::NoteCommitment,
&note_contents, &note_contents,
self.params, self.params
)?; )?;
{ {
// Booleanize the randomness for the note commitment // Booleanize the randomness for the note commitment
let rcm = boolean::field_into_boolean_vec_le( let rcm = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcm"), cs.namespace(|| "rcm"),
self.commitment_randomness, self.commitment_randomness
)?; )?;
// Compute the note commitment randomness in the exponent // Compute the note commitment randomness in the exponent
@@ -283,7 +327,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of commitment randomness"), cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness, FixedGenerators::NoteCommitmentRandomness,
&rcm, &rcm,
self.params, self.params
)?; )?;
// Randomize the note commitment. Pedersen hashes are not // Randomize the note commitment. Pedersen hashes are not
@@ -291,7 +335,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cm = cm.add( cm = cm.add(
cs.namespace(|| "randomization of note commitment"), cs.namespace(|| "randomization of note commitment"),
&rcm, &rcm,
self.params, self.params
)?; )?;
} }
@@ -312,7 +356,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// depth of the tree. // depth of the tree.
let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc( let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc(
cs.namespace(|| "position bit"), cs.namespace(|| "position bit"),
e.map(|e| e.1), e.map(|e| e.1)
)?); )?);
// Push this boolean for nullifier computation later // Push this boolean for nullifier computation later
@@ -320,15 +364,19 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Witness the authentication path element adjacent // Witness the authentication path element adjacent
// at this depth. // at this depth.
let path_element = let path_element = num::AllocatedNum::alloc(
num::AllocatedNum::alloc(cs.namespace(|| "path element"), || Ok(e.get()?.0))?; cs.namespace(|| "path element"),
|| {
Ok(e.get()?.0)
}
)?;
// Swap the two if the current subtree is on the right // Swap the two if the current subtree is on the right
let (xl, xr) = num::AllocatedNum::conditionally_reverse( let (xl, xr) = num::AllocatedNum::conditionally_reverse(
cs.namespace(|| "conditional reversal of preimage"), cs.namespace(|| "conditional reversal of preimage"),
&cur, &cur,
&path_element, &path_element,
&cur_is_right, &cur_is_right
)?; )?;
// We don't need to be strict, because the function is // We don't need to be strict, because the function is
@@ -344,19 +392,20 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of pedersen hash"), cs.namespace(|| "computation of pedersen hash"),
pedersen_hash::Personalization::MerkleTree(i), pedersen_hash::Personalization::MerkleTree(i),
&preimage, &preimage,
self.params, self.params
)? )?.get_x().clone(); // Injective encoding
.get_x()
.clone(); // Injective encoding
} }
{ {
let real_anchor_value = self.anchor; let real_anchor_value = self.anchor;
// Allocate the "real" anchor that will be exposed. // Allocate the "real" anchor that will be exposed.
let rt = num::AllocatedNum::alloc(cs.namespace(|| "conditional anchor"), || { let rt = num::AllocatedNum::alloc(
cs.namespace(|| "conditional anchor"),
|| {
Ok(*real_anchor_value.get()?) Ok(*real_anchor_value.get()?)
})?; }
)?;
// (cur - rt) * value = 0 // (cur - rt) * value = 0
// if value is zero, cur and rt can be different // if value is zero, cur and rt can be different
@@ -365,7 +414,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
|| "conditionally enforce correct root", || "conditionally enforce correct root",
|lc| lc + cur.get_variable() - rt.get_variable(), |lc| lc + cur.get_variable() - rt.get_variable(),
|lc| lc + &value_num.lc(E::Fr::one()), |lc| lc + &value_num.lc(E::Fr::one()),
|lc| lc, |lc| lc
); );
// Expose the anchor // Expose the anchor
@@ -381,19 +430,21 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "g^position"), cs.namespace(|| "g^position"),
FixedGenerators::NullifierPosition, FixedGenerators::NullifierPosition,
&position_bits, &position_bits,
self.params, self.params
)?; )?;
// Add the position to the commitment // Add the position to the commitment
rho = rho.add( rho = rho.add(
cs.namespace(|| "faerie gold prevention"), cs.namespace(|| "faerie gold prevention"),
&position, &position,
self.params, self.params
)?; )?;
} }
// Let's compute nf = BLAKE2s(nk || rho) // Let's compute nf = BLAKE2s(nk || rho)
nf_preimage.extend(rho.repr(cs.namespace(|| "representation of rho"))?); nf_preimage.extend(
rho.repr(cs.namespace(|| "representation of rho"))?
);
assert_eq!(nf_preimage.len(), 512); assert_eq!(nf_preimage.len(), 512);
@@ -401,7 +452,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let nf = blake2s::blake2s( let nf = blake2s::blake2s(
cs.namespace(|| "nf computation"), cs.namespace(|| "nf computation"),
&nf_preimage, &nf_preimage,
constants::PRF_NF_PERSONALIZATION, constants::PRF_NF_PERSONALIZATION
)?; )?;
multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf) multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf)
@@ -409,7 +460,8 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
} }
impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> { impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>
{
// Let's start to construct our note, which contains // Let's start to construct our note, which contains
// value (big endian) // value (big endian)
let mut note_contents = vec![]; let mut note_contents = vec![];
@@ -419,7 +471,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
note_contents.extend(expose_value_commitment( note_contents.extend(expose_value_commitment(
cs.namespace(|| "value commitment"), cs.namespace(|| "value commitment"),
self.value_commitment, self.value_commitment,
self.params, self.params
)?); )?);
// Let's deal with g_d // Let's deal with g_d
@@ -431,7 +483,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
let g_d = ecc::EdwardsPoint::witness( let g_d = ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"), cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)), self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params, self.params
)?; )?;
// g_d is ensured to be large order. The relationship // g_d is ensured to be large order. The relationship
@@ -443,17 +495,29 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
// //
// Further, if it were small order, epk would be // Further, if it were small order, epk would be
// small order too! // small order too!
g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?; g_d.assert_not_small_order(
cs.namespace(|| "g_d not small order"),
self.params
)?;
// Extend our note contents with the representation of // Extend our note contents with the representation of
// g_d. // g_d.
note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?); note_contents.extend(
g_d.repr(cs.namespace(|| "representation of g_d"))?
);
// Booleanize our ephemeral secret key // Booleanize our ephemeral secret key
let esk = boolean::field_into_boolean_vec_le(cs.namespace(|| "esk"), self.esk)?; let esk = boolean::field_into_boolean_vec_le(
cs.namespace(|| "esk"),
self.esk
)?;
// Create the ephemeral public key from g_d. // Create the ephemeral public key from g_d.
let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk, self.params)?; let epk = g_d.mul(
cs.namespace(|| "epk computation"),
&esk,
self.params
)?;
// Expose epk publicly. // Expose epk publicly.
epk.inputize(cs.namespace(|| "epk"))?; epk.inputize(cs.namespace(|| "epk"))?;
@@ -470,13 +534,13 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
// endian bits (to match the representation) // endian bits (to match the representation)
let y_contents = boolean::field_into_boolean_vec_le( let y_contents = boolean::field_into_boolean_vec_le(
cs.namespace(|| "pk_d bits of y"), cs.namespace(|| "pk_d bits of y"),
pk_d.map(|e| e.1), pk_d.map(|e| e.1)
)?; )?;
// Witness the sign bit // Witness the sign bit
let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc(
cs.namespace(|| "pk_d bit of x"), cs.namespace(|| "pk_d bit of x"),
pk_d.map(|e| e.0.into_repr().is_odd()), pk_d.map(|e| e.0.into_repr().is_odd())
)?); )?);
// Extend the note with pk_d representation // Extend the note with pk_d representation
@@ -496,14 +560,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
cs.namespace(|| "note content hash"), cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment, pedersen_hash::Personalization::NoteCommitment,
&note_contents, &note_contents,
self.params, self.params
)?; )?;
{ {
// Booleanize the randomness // Booleanize the randomness
let rcm = boolean::field_into_boolean_vec_le( let rcm = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcm"), cs.namespace(|| "rcm"),
self.commitment_randomness, self.commitment_randomness
)?; )?;
// Compute the note commitment randomness in the exponent // Compute the note commitment randomness in the exponent
@@ -511,14 +575,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
cs.namespace(|| "computation of commitment randomness"), cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness, FixedGenerators::NoteCommitmentRandomness,
&rcm, &rcm,
self.params, self.params
)?; )?;
// Randomize our note commitment // Randomize our note commitment
cm = cm.add( cm = cm.add(
cs.namespace(|| "randomization of note commitment"), cs.namespace(|| "randomization of note commitment"),
&rcm, &rcm,
self.params, self.params
)?; )?;
} }
@@ -534,37 +598,29 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
#[test] #[test]
fn test_input_circuit_with_bls12_381() { fn test_input_circuit_with_bls12_381() {
use bellman::gadgets::test::*;
use ff::{BitIterator, Field}; use ff::{BitIterator, Field};
use pairing::bls12_381::*; use pairing::bls12_381::*;
use rand_core::{RngCore, SeedableRng}; use rand::{SeedableRng, Rng, XorShiftRng};
use rand_xorshift::XorShiftRng; use ::circuit::test::*;
use zcash_primitives::{ use jubjub::{JubjubBls12, fs, edwards};
jubjub::{edwards, fs, JubjubBls12},
pedersen_hash,
primitives::{Diversifier, Note, ProofGenerationKey},
};
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let tree_depth = 32; let tree_depth = 32;
for _ in 0..10 { for _ in 0..10 {
let value_commitment = ValueCommitment { let value_commitment = ValueCommitment {
value: rng.next_u64(), value: rng.gen(),
randomness: fs::Fs::random(rng), randomness: rng.gen()
}; };
let nsk = fs::Fs::random(rng); let nsk: fs::Fs = rng.gen();
let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params);
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ::primitives::ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone(), nsk: nsk.clone()
}; };
let viewing_key = proof_generation_key.into_viewing_key(params); let viewing_key = proof_generation_key.into_viewing_key(params);
@@ -572,38 +628,39 @@ fn test_input_circuit_with_bls12_381() {
let payment_address; let payment_address;
loop { loop {
let diversifier = { let diversifier = ::primitives::Diversifier(rng.gen());
let mut d = [0; 11];
rng.fill_bytes(&mut d);
Diversifier(d)
};
if let Some(p) = viewing_key.into_payment_address(diversifier, params) { if let Some(p) = viewing_key.into_payment_address(
diversifier,
params
)
{
payment_address = p; payment_address = p;
break; break;
} }
} }
let g_d = payment_address.diversifier.g_d(params).unwrap(); let g_d = payment_address.diversifier.g_d(params).unwrap();
let commitment_randomness = fs::Fs::random(rng); let commitment_randomness: fs::Fs = rng.gen();
let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; let auth_path = vec![Some((rng.gen(), rng.gen())); tree_depth];
let ar = fs::Fs::random(rng); let ar: fs::Fs = rng.gen();
{ {
let rk = viewing_key.rk(ar, params).into_xy(); let rk = viewing_key.rk(ar, params).into_xy();
let expected_value_cm = value_commitment.cm(params).into_xy(); let expected_value_cm = value_commitment.cm(params).into_xy();
let note = Note { let note = ::primitives::Note {
value: value_commitment.value, value: value_commitment.value,
g_d: g_d.clone(), g_d: g_d.clone(),
pk_d: payment_address.pk_d.clone(), pk_d: payment_address.pk_d.clone(),
r: commitment_randomness.clone(), r: commitment_randomness.clone()
}; };
let mut position = 0u64; let mut position = 0u64;
let cm: Fr = note.cm(params); let cm: Fr = note.cm(params);
let mut cur = cm.clone(); let mut cur = cm.clone();
for (i, val) in auth_path.clone().into_iter().enumerate() { for (i, val) in auth_path.clone().into_iter().enumerate()
{
let (uncle, b) = val.unwrap(); let (uncle, b) = val.unwrap();
let mut lhs = cur; let mut lhs = cur;
@@ -619,15 +676,13 @@ fn test_input_circuit_with_bls12_381() {
lhs.reverse(); lhs.reverse();
rhs.reverse(); rhs.reverse();
cur = pedersen_hash::pedersen_hash::<Bls12, _>( cur = ::pedersen_hash::pedersen_hash::<Bls12, _>(
pedersen_hash::Personalization::MerkleTree(i), ::pedersen_hash::Personalization::MerkleTree(i),
lhs.into_iter() lhs.into_iter()
.take(Fr::NUM_BITS as usize) .take(Fr::NUM_BITS as usize)
.chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)),
params, params
) ).into_xy().0;
.into_xy()
.0;
if b { if b {
position |= 1 << i; position |= 1 << i;
@@ -649,17 +704,14 @@ fn test_input_circuit_with_bls12_381() {
commitment_randomness: Some(commitment_randomness), commitment_randomness: Some(commitment_randomness),
ar: Some(ar), ar: Some(ar),
auth_path: auth_path.clone(), auth_path: auth_path.clone(),
anchor: Some(cur), anchor: Some(cur)
}; };
instance.synthesize(&mut cs).unwrap(); instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 98777); assert_eq!(cs.num_constraints(), 98777);
assert_eq!( assert_eq!(cs.hash(), "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89");
cs.hash(),
"d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
);
assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);
@@ -667,14 +719,8 @@ fn test_input_circuit_with_bls12_381() {
assert_eq!(cs.get_input(0, "ONE"), Fr::one()); assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0);
assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1);
assert_eq!( assert_eq!(cs.get_input(3, "value commitment/commitment point/x/input variable"), expected_value_cm.0);
cs.get_input(3, "value commitment/commitment point/x/input variable"), assert_eq!(cs.get_input(4, "value commitment/commitment point/y/input variable"), expected_value_cm.1);
expected_value_cm.0
);
assert_eq!(
cs.get_input(4, "value commitment/commitment point/y/input variable"),
expected_value_cm.1
);
assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(5, "anchor/input variable"), cur);
assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]); assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
@@ -684,34 +730,27 @@ fn test_input_circuit_with_bls12_381() {
#[test] #[test]
fn test_output_circuit_with_bls12_381() { fn test_output_circuit_with_bls12_381() {
use bellman::gadgets::test::*;
use ff::Field; use ff::Field;
use pairing::bls12_381::*; use pairing::bls12_381::*;
use rand_core::{RngCore, SeedableRng}; use rand::{SeedableRng, Rng, XorShiftRng};
use rand_xorshift::XorShiftRng; use ::circuit::test::*;
use zcash_primitives::{ use jubjub::{JubjubBls12, fs, edwards};
jubjub::{edwards, fs, JubjubBls12},
primitives::{Diversifier, ProofGenerationKey},
};
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let value_commitment = ValueCommitment { let value_commitment = ValueCommitment {
value: rng.next_u64(), value: rng.gen(),
randomness: fs::Fs::random(rng), randomness: rng.gen()
}; };
let nsk = fs::Fs::random(rng); let nsk: fs::Fs = rng.gen();
let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params);
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ::primitives::ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone(), nsk: nsk.clone()
}; };
let viewing_key = proof_generation_key.into_viewing_key(params); let viewing_key = proof_generation_key.into_viewing_key(params);
@@ -719,20 +758,20 @@ fn test_output_circuit_with_bls12_381() {
let payment_address; let payment_address;
loop { loop {
let diversifier = { let diversifier = ::primitives::Diversifier(rng.gen());
let mut d = [0; 11];
rng.fill_bytes(&mut d);
Diversifier(d)
};
if let Some(p) = viewing_key.into_payment_address(diversifier, params) { if let Some(p) = viewing_key.into_payment_address(
diversifier,
params
)
{
payment_address = p; payment_address = p;
break; break;
} }
} }
let commitment_randomness = fs::Fs::random(rng); let commitment_randomness: fs::Fs = rng.gen();
let esk = fs::Fs::random(rng); let esk: fs::Fs = rng.gen();
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
@@ -742,41 +781,30 @@ fn test_output_circuit_with_bls12_381() {
value_commitment: Some(value_commitment.clone()), value_commitment: Some(value_commitment.clone()),
payment_address: Some(payment_address.clone()), payment_address: Some(payment_address.clone()),
commitment_randomness: Some(commitment_randomness), commitment_randomness: Some(commitment_randomness),
esk: Some(esk.clone()), esk: Some(esk.clone())
}; };
instance.synthesize(&mut cs).unwrap(); instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 7827); assert_eq!(cs.num_constraints(), 7827);
assert_eq!( assert_eq!(cs.hash(), "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee");
cs.hash(),
"c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"
);
let expected_cm = payment_address let expected_cm = payment_address.create_note(
.create_note(value_commitment.value, commitment_randomness, params) value_commitment.value,
.expect("should be valid") commitment_randomness,
.cm(params); params
).expect("should be valid").cm(params);
let expected_value_cm = value_commitment.cm(params).into_xy(); let expected_value_cm = value_commitment.cm(params).into_xy();
let expected_epk = payment_address let expected_epk = payment_address.g_d(params).expect("should be valid").mul(esk, params);
.g_d(params)
.expect("should be valid")
.mul(esk, params);
let expected_epk_xy = expected_epk.into_xy(); let expected_epk_xy = expected_epk.into_xy();
assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.num_inputs(), 6);
assert_eq!(cs.get_input(0, "ONE"), Fr::one()); assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!( assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm.0);
cs.get_input(1, "value commitment/commitment point/x/input variable"), assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1);
expected_value_cm.0
);
assert_eq!(
cs.get_input(2, "value commitment/commitment point/y/input variable"),
expected_value_cm.1
);
assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0);
assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1);
assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm);

View File

@@ -1,7 +1,7 @@
use super::boolean::Boolean;
use super::multieq::MultiEq;
use super::uint32::UInt32; use super::uint32::UInt32;
use crate::{ConstraintSystem, SynthesisError}; use super::multieq::MultiEq;
use super::boolean::Boolean;
use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use pairing::Engine;
const ROUND_CONSTANTS: [u32; 64] = [ const ROUND_CONSTANTS: [u32; 64] = [
@@ -12,35 +12,37 @@ const ROUND_CONSTANTS: [u32; 64] = [
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]; ];
const IV: [u32; 8] = [ const IV: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]; ];
pub fn sha256_block_no_padding<E, CS>( pub fn sha256_block_no_padding<E, CS>(
mut cs: CS, mut cs: CS,
input: &[Boolean], input: &[Boolean]
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
Ok( Ok(sha256_compression_function(
sha256_compression_function(&mut cs, &input, &get_sha256_iv())? &mut cs,
&input,
&get_sha256_iv()
)?
.into_iter() .into_iter()
.flat_map(|e| e.into_bits_be()) .flat_map(|e| e.into_bits_be())
.collect(), .collect())
)
} }
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError> pub fn sha256<E, CS>(
where mut cs: CS,
E: Engine, input: &[Boolean]
CS: ConstraintSystem<E>, ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
{ {
assert!(input.len() % 8 == 0); assert!(input.len() % 8 == 0);
@@ -60,10 +62,16 @@ where
let mut cur = get_sha256_iv(); let mut cur = get_sha256_iv();
for (i, block) in padded.chunks(512).enumerate() { for (i, block) in padded.chunks(512).enumerate() {
cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?; cur = sha256_compression_function(
cs.namespace(|| format!("block {}", i)),
block,
&cur
)?;
} }
Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect()) Ok(cur.into_iter()
.flat_map(|e| e.into_bits_be())
.collect())
} }
fn get_sha256_iv() -> Vec<UInt32> { fn get_sha256_iv() -> Vec<UInt32> {
@@ -73,17 +81,14 @@ fn get_sha256_iv() -> Vec<UInt32> {
fn sha256_compression_function<E, CS>( fn sha256_compression_function<E, CS>(
cs: CS, cs: CS,
input: &[Boolean], input: &[Boolean],
current_hash_value: &[UInt32], current_hash_value: &[UInt32]
) -> Result<Vec<UInt32>, SynthesisError> ) -> Result<Vec<UInt32>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
assert_eq!(current_hash_value.len(), 8); assert_eq!(current_hash_value.len(), 8);
let mut w = input let mut w = input.chunks(32)
.chunks(32)
.map(|e| UInt32::from_bits_be(e)) .map(|e| UInt32::from_bits_be(e))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -96,17 +101,29 @@ where
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3) // s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
let mut s0 = w[i-15].rotr(7); let mut s0 = w[i-15].rotr(7);
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?; s0 = s0.xor(
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?; cs.namespace(|| "first xor for s0"),
&w[i-15].rotr(18)
)?;
s0 = s0.xor(
cs.namespace(|| "second xor for s0"),
&w[i-15].shr(3)
)?;
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10) // s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
let mut s1 = w[i-2].rotr(17); let mut s1 = w[i-2].rotr(17);
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?; s1 = s1.xor(
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?; cs.namespace(|| "first xor for s1"),
&w[i-2].rotr(19)
)?;
s1 = s1.xor(
cs.namespace(|| "second xor for s1"),
&w[i-2].shr(10)
)?;
let tmp = UInt32::addmany( let tmp = UInt32::addmany(
cs.namespace(|| "computation of w[i]"), cs.namespace(|| "computation of w[i]"),
&[w[i - 16].clone(), s0, w[i - 7].clone(), s1], &[w[i-16].clone(), s0, w[i-7].clone(), s1]
)?; )?;
// w[i] := w[i-16] + s0 + w[i-7] + s1 // w[i] := w[i-16] + s0 + w[i-7] + s1
@@ -117,21 +134,29 @@ where
enum Maybe { enum Maybe {
Deferred(Vec<UInt32>), Deferred(Vec<UInt32>),
Concrete(UInt32), Concrete(UInt32)
} }
impl Maybe { impl Maybe {
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError> fn compute<E, CS, M>(
where self,
E: Engine, cs: M,
others: &[UInt32]
) -> Result<UInt32, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root = MultiEq<E, CS>>, M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{ {
Ok(match self { Ok(match self {
Maybe::Concrete(ref v) => return Ok(v.clone()), Maybe::Concrete(ref v) => {
return Ok(v.clone())
},
Maybe::Deferred(mut v) => { Maybe::Deferred(mut v) => {
v.extend(others.into_iter().cloned()); v.extend(others.into_iter().cloned());
UInt32::addmany(cs, &v)? UInt32::addmany(
cs,
&v
)?
} }
}) })
} }
@@ -152,11 +177,22 @@ where
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25) // S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?; let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
let mut s1 = new_e.rotr(6); let mut s1 = new_e.rotr(6);
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?; s1 = s1.xor(
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?; cs.namespace(|| "first xor for s1"),
&new_e.rotr(11)
)?;
s1 = s1.xor(
cs.namespace(|| "second xor for s1"),
&new_e.rotr(25)
)?;
// ch := (e and f) xor ((not e) and g) // ch := (e and f) xor ((not e) and g)
let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?; let ch = UInt32::sha256_ch(
cs.namespace(|| "ch"),
&new_e,
&f,
&g
)?;
// temp1 := h + S1 + ch + k[i] + w[i] // temp1 := h + S1 + ch + k[i] + w[i]
let temp1 = vec![ let temp1 = vec![
@@ -164,17 +200,28 @@ where
s1, s1,
ch, ch,
UInt32::constant(ROUND_CONSTANTS[i]), UInt32::constant(ROUND_CONSTANTS[i]),
w[i].clone(), w[i].clone()
]; ];
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22) // S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?; let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
let mut s0 = new_a.rotr(2); let mut s0 = new_a.rotr(2);
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?; s0 = s0.xor(
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?; cs.namespace(|| "first xor for s0"),
&new_a.rotr(13)
)?;
s0 = s0.xor(
cs.namespace(|| "second xor for s0"),
&new_a.rotr(22)
)?;
// maj := (a and b) xor (a and c) xor (b and c) // maj := (a and b) xor (a and c) xor (b and c)
let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?; let maj = UInt32::sha256_maj(
cs.namespace(|| "maj"),
&new_a,
&b,
&c
)?;
// temp2 := S0 + maj // temp2 := S0 + maj
let temp2 = vec![s0, maj]; let temp2 = vec![s0, maj];
@@ -197,13 +244,7 @@ where
d = c; d = c;
c = b; c = b;
b = new_a; b = new_a;
a = Maybe::Deferred( a = Maybe::Deferred(temp1.iter().cloned().chain(temp2.iter().cloned()).collect::<Vec<_>>());
temp1
.iter()
.cloned()
.chain(temp2.iter().cloned())
.collect::<Vec<_>>(),
);
} }
/* /*
@@ -220,42 +261,42 @@ where
let h0 = a.compute( let h0 = a.compute(
cs.namespace(|| "deferred h0 computation"), cs.namespace(|| "deferred h0 computation"),
&[current_hash_value[0].clone()], &[current_hash_value[0].clone()]
)?; )?;
let h1 = UInt32::addmany( let h1 = UInt32::addmany(
cs.namespace(|| "new h1"), cs.namespace(|| "new h1"),
&[current_hash_value[1].clone(), b], &[current_hash_value[1].clone(), b]
)?; )?;
let h2 = UInt32::addmany( let h2 = UInt32::addmany(
cs.namespace(|| "new h2"), cs.namespace(|| "new h2"),
&[current_hash_value[2].clone(), c], &[current_hash_value[2].clone(), c]
)?; )?;
let h3 = UInt32::addmany( let h3 = UInt32::addmany(
cs.namespace(|| "new h3"), cs.namespace(|| "new h3"),
&[current_hash_value[3].clone(), d], &[current_hash_value[3].clone(), d]
)?; )?;
let h4 = e.compute( let h4 = e.compute(
cs.namespace(|| "deferred h4 computation"), cs.namespace(|| "deferred h4 computation"),
&[current_hash_value[4].clone()], &[current_hash_value[4].clone()]
)?; )?;
let h5 = UInt32::addmany( let h5 = UInt32::addmany(
cs.namespace(|| "new h5"), cs.namespace(|| "new h5"),
&[current_hash_value[5].clone(), f], &[current_hash_value[5].clone(), f]
)?; )?;
let h6 = UInt32::addmany( let h6 = UInt32::addmany(
cs.namespace(|| "new h6"), cs.namespace(|| "new h6"),
&[current_hash_value[6].clone(), g], &[current_hash_value[6].clone(), g]
)?; )?;
let h7 = UInt32::addmany( let h7 = UInt32::addmany(
cs.namespace(|| "new h7"), cs.namespace(|| "new h7"),
&[current_hash_value[7].clone(), h], &[current_hash_value[7].clone(), h]
)?; )?;
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7]) Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
@@ -264,11 +305,10 @@ where
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::gadgets::boolean::AllocatedBit; use circuit::boolean::AllocatedBit;
use crate::gadgets::test::TestConstraintSystem;
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use circuit::test::TestConstraintSystem;
use rand_xorshift::XorShiftRng; use rand::{XorShiftRng, SeedableRng, Rng};
#[test] #[test]
fn test_blank_hash() { fn test_blank_hash() {
@@ -277,7 +317,11 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
input_bits[0] = Boolean::Constant(true); input_bits[0] = Boolean::Constant(true);
let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap(); let out = sha256_compression_function(
&mut cs,
&input_bits,
&iv
).unwrap();
let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect(); let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -297,27 +341,25 @@ mod test {
#[test] #[test]
fn test_full_block() { fn test_full_block() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let iv = get_sha256_iv(); let iv = get_sha256_iv();
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512) let input_bits: Vec<_> = (0..512).map(|i| {
.map(|i| {
Boolean::from( Boolean::from(
AllocatedBit::alloc( AllocatedBit::alloc(
cs.namespace(|| format!("input bit {}", i)), cs.namespace(|| format!("input bit {}", i)),
Some(rng.next_u32() % 2 != 0), Some(rng.gen())
).unwrap()
) )
.unwrap(), }).collect();
)
})
.collect();
sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap(); sha256_compression_function(
cs.namespace(|| "sha256"),
&input_bits,
&iv
).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints() - 512, 25840); assert_eq!(cs.num_constraints() - 512, 25840);
@@ -325,18 +367,18 @@ mod test {
#[test] #[test]
fn test_against_vectors() { fn test_against_vectors() {
use sha2::{Digest, Sha256}; use crypto::sha2::Sha256;
use crypto::digest::Digest;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0))
{
let mut h = Sha256::new(); let mut h = Sha256::new();
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect(); let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
h.input(&data); h.input(&data);
let hash_result = h.result(); let mut hash_result = [0u8; 32];
h.result(&mut hash_result[..]);
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![]; let mut input_bits = vec![];
@@ -345,11 +387,7 @@ mod test {
for bit_i in (0..8).rev() { for bit_i in (0..8).rev() {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push( input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
} }
} }
@@ -357,19 +395,17 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut s = hash_result let mut s = hash_result.as_ref().iter()
.as_ref()
.iter()
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r { for b in r {
match b { match b {
Boolean::Is(b) => { Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap()); assert!(s.next().unwrap() == b.get_value().unwrap());
} },
Boolean::Not(b) => { Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap()); assert!(s.next().unwrap() != b.get_value().unwrap());
} },
Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(input_len == 0); assert!(input_len == 0);
assert!(s.next().unwrap() == b); assert!(s.next().unwrap() == b);

View File

@@ -1,18 +1,20 @@
use bellman::gadgets::boolean::Boolean; use pairing::{Engine};
use bellman::gadgets::sha256::sha256;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use circuit::sha256::{
sha256
};
use circuit::boolean::{
Boolean
};
pub fn note_comm<E, CS>( pub fn note_comm<E, CS>(
cs: CS, cs: CS,
a_pk: &[Boolean], a_pk: &[Boolean],
value: &[Boolean], value: &[Boolean],
rho: &[Boolean], rho: &[Boolean],
r: &[Boolean], r: &[Boolean]
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(a_pk.len(), 256); assert_eq!(a_pk.len(), 256);
assert_eq!(value.len(), 64); assert_eq!(value.len(), 64);
@@ -33,5 +35,8 @@ where
image.extend(rho.iter().cloned()); image.extend(rho.iter().cloned());
image.extend(r.iter().cloned()); image.extend(r.iter().cloned());
sha256(cs, &image) sha256(
cs,
&image
)
} }

View File

@@ -1,11 +1,16 @@
use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use pairing::{Engine};
use bellman::gadgets::sha256::sha256_block_no_padding;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use circuit::sha256::{
sha256_block_no_padding
};
use circuit::boolean::{
AllocatedBit,
Boolean
};
use super::commitment::note_comm;
use super::prfs::*;
use super::*; use super::*;
use super::prfs::*;
use super::commitment::note_comm;
pub struct InputNote { pub struct InputNote {
pub nf: Vec<Boolean>, pub nf: Vec<Boolean>,
@@ -22,33 +27,49 @@ impl InputNote {
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool, nonce: bool,
auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH], auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH],
rt: &[Boolean], rt: &[Boolean]
) -> Result<InputNote, SynthesisError> ) -> Result<InputNote, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
let a_sk = witness_u252( let a_sk = witness_u252(
cs.namespace(|| "a_sk"), cs.namespace(|| "a_sk"),
a_sk.as_ref().map(|a_sk| &a_sk.0[..]), a_sk.as_ref().map(|a_sk| &a_sk.0[..])
)?; )?;
let rho = witness_u256(cs.namespace(|| "rho"), rho.as_ref().map(|rho| &rho.0[..]))?; let rho = witness_u256(
cs.namespace(|| "rho"),
rho.as_ref().map(|rho| &rho.0[..])
)?;
let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?; let r = witness_u256(
cs.namespace(|| "r"),
r.as_ref().map(|r| &r.0[..])
)?;
let a_pk = prf_a_pk(cs.namespace(|| "a_pk computation"), &a_sk)?; let a_pk = prf_a_pk(
cs.namespace(|| "a_pk computation"),
&a_sk
)?;
let nf = prf_nf(cs.namespace(|| "nf computation"), &a_sk, &rho)?; let nf = prf_nf(
cs.namespace(|| "nf computation"),
&a_sk,
&rho
)?;
let mac = prf_pk(cs.namespace(|| "mac computation"), &a_sk, h_sig, nonce)?; let mac = prf_pk(
cs.namespace(|| "mac computation"),
&a_sk,
h_sig,
nonce
)?;
let cm = note_comm( let cm = note_comm(
cs.namespace(|| "cm computation"), cs.namespace(|| "cm computation"),
&a_pk, &a_pk,
&value.bits_le(), &value.bits_le(),
&rho, &rho,
&r, &r
)?; )?;
// Witness into the merkle tree // Witness into the merkle tree
@@ -59,13 +80,13 @@ impl InputNote {
let cur_is_right = AllocatedBit::alloc( let cur_is_right = AllocatedBit::alloc(
cs.namespace(|| "cur is right"), cs.namespace(|| "cur is right"),
layer.as_ref().map(|&(_, p)| p), layer.as_ref().map(|&(_, p)| p)
)?; )?;
let lhs = cur; let lhs = cur;
let rhs = witness_u256( let rhs = witness_u256(
cs.namespace(|| "sibling"), cs.namespace(|| "sibling"),
layer.as_ref().map(|&(ref sibling, _)| &sibling[..]), layer.as_ref().map(|&(ref sibling, _)| &sibling[..])
)?; )?;
// Conditionally swap if cur is right // Conditionally swap if cur is right
@@ -73,16 +94,19 @@ impl InputNote {
cs.namespace(|| "conditional swap"), cs.namespace(|| "conditional swap"),
&lhs[..], &lhs[..],
&rhs[..], &rhs[..],
&cur_is_right, &cur_is_right
)?; )?;
cur = sha256_block_no_padding(cs.namespace(|| "hash of this layer"), &preimage)?; cur = sha256_block_no_padding(
cs.namespace(|| "hash of this layer"),
&preimage
)?;
} }
// enforce must be true if the value is nonzero // enforce must be true if the value is nonzero
let enforce = AllocatedBit::alloc( let enforce = AllocatedBit::alloc(
cs.namespace(|| "enforce"), cs.namespace(|| "enforce"),
value.get_value().map(|n| n != 0), value.get_value().map(|n| n != 0)
)?; )?;
// value * (1 - enforce) = 0 // value * (1 - enforce) = 0
@@ -92,7 +116,7 @@ impl InputNote {
|| "enforce validity", || "enforce validity",
|_| value.lc(), |_| value.lc(),
|lc| lc + CS::one() - enforce.get_variable(), |lc| lc + CS::one() - enforce.get_variable(),
|lc| lc, |lc| lc
); );
assert_eq!(cur.len(), rt.len()); assert_eq!(cur.len(), rt.len());
@@ -108,11 +132,14 @@ impl InputNote {
|| format!("conditionally enforce correct root for bit {}", i), || format!("conditionally enforce correct root for bit {}", i),
|_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()), |_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()),
|lc| lc + enforce.get_variable(), |lc| lc + enforce.get_variable(),
|lc| lc, |lc| lc
); );
} }
Ok(InputNote { mac: mac, nf: nf }) Ok(InputNote {
mac: mac,
nf: nf
})
} }
} }
@@ -122,11 +149,9 @@ pub fn conditionally_swap_u256<E, CS>(
mut cs: CS, mut cs: CS,
lhs: &[Boolean], lhs: &[Boolean],
rhs: &[Boolean], rhs: &[Boolean],
condition: &AllocatedBit, condition: &AllocatedBit
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>,
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(lhs.len(), 256); assert_eq!(lhs.len(), 256);
assert_eq!(rhs.len(), 256); assert_eq!(rhs.len(), 256);
@@ -139,9 +164,13 @@ where
let x = Boolean::from(AllocatedBit::alloc( let x = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "x"), cs.namespace(|| "x"),
condition condition.get_value().and_then(|v| {
.get_value() if v {
.and_then(|v| if v { rhs.get_value() } else { lhs.get_value() }), rhs.get_value()
} else {
lhs.get_value()
}
})
)?); )?);
// x = (1-condition)lhs + (condition)rhs // x = (1-condition)lhs + (condition)rhs
@@ -155,25 +184,33 @@ where
// x = rhs // x = rhs
cs.enforce( cs.enforce(
|| "conditional swap for x", || "conditional swap for x",
|lc| lc + &rhs.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()), |lc| lc + &rhs.lc(CS::one(), E::Fr::one())
- &lhs.lc(CS::one(), E::Fr::one()),
|lc| lc + condition.get_variable(), |lc| lc + condition.get_variable(),
|lc| lc + &x.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()), |lc| lc + &x.lc(CS::one(), E::Fr::one())
- &lhs.lc(CS::one(), E::Fr::one())
); );
let y = Boolean::from(AllocatedBit::alloc( let y = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "y"), cs.namespace(|| "y"),
condition condition.get_value().and_then(|v| {
.get_value() if v {
.and_then(|v| if v { lhs.get_value() } else { rhs.get_value() }), lhs.get_value()
} else {
rhs.get_value()
}
})
)?); )?);
// y = (1-condition)rhs + (condition)lhs // y = (1-condition)rhs + (condition)lhs
// y - rhs = condition (lhs - rhs) // y - rhs = condition (lhs - rhs)
cs.enforce( cs.enforce(
|| "conditional swap for y", || "conditional swap for y",
|lc| lc + &lhs.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()), |lc| lc + &lhs.lc(CS::one(), E::Fr::one())
- &rhs.lc(CS::one(), E::Fr::one()),
|lc| lc + condition.get_variable(), |lc| lc + condition.get_variable(),
|lc| lc + &y.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()), |lc| lc + &y.lc(CS::one(), E::Fr::one())
- &rhs.lc(CS::one(), E::Fr::one())
); );
new_lhs.push(x); new_lhs.push(x);

View File

@@ -1,13 +1,16 @@
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::multipack::pack_into_inputs;
use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError};
use ff::Field; use ff::Field;
use pairing::Engine; use pairing::Engine;
use bellman::{ConstraintSystem, SynthesisError, Circuit, LinearCombination};
use circuit::boolean::{
AllocatedBit,
Boolean
};
use circuit::multipack::pack_into_inputs;
mod prfs;
mod commitment; mod commitment;
mod input; mod input;
mod output; mod output;
mod prfs;
use self::input::*; use self::input::*;
use self::output::*; use self::output::*;
@@ -34,29 +37,39 @@ pub struct JSInput {
pub a_sk: Option<SpendingKey>, pub a_sk: Option<SpendingKey>,
pub rho: Option<UniqueRandomness>, pub rho: Option<UniqueRandomness>,
pub r: Option<CommitmentRandomness>, pub r: Option<CommitmentRandomness>,
pub auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH], pub auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH]
} }
pub struct JSOutput { pub struct JSOutput {
pub value: Option<u64>, pub value: Option<u64>,
pub a_pk: Option<PayingKey>, pub a_pk: Option<PayingKey>,
pub r: Option<CommitmentRandomness>, pub r: Option<CommitmentRandomness>
} }
impl<E: Engine> Circuit<E> for JoinSplit { impl<E: Engine> Circuit<E> for JoinSplit {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.inputs.len(), 2); assert_eq!(self.inputs.len(), 2);
assert_eq!(self.outputs.len(), 2); assert_eq!(self.outputs.len(), 2);
// vpub_old is the value entering the // vpub_old is the value entering the
// JoinSplit from the "outside" value // JoinSplit from the "outside" value
// pool // pool
let vpub_old = NoteValue::new(cs.namespace(|| "vpub_old"), self.vpub_old)?; let vpub_old = NoteValue::new(
cs.namespace(|| "vpub_old"),
self.vpub_old
)?;
// vpub_new is the value leaving the // vpub_new is the value leaving the
// JoinSplit into the "outside" value // JoinSplit into the "outside" value
// pool // pool
let vpub_new = NoteValue::new(cs.namespace(|| "vpub_new"), self.vpub_new)?; let vpub_new = NoteValue::new(
cs.namespace(|| "vpub_new"),
self.vpub_new
)?;
// The left hand side of the balance equation // The left hand side of the balance equation
// vpub_old + inputs[0].value + inputs[1].value // vpub_old + inputs[0].value + inputs[1].value
@@ -67,17 +80,22 @@ impl<E: Engine> Circuit<E> for JoinSplit {
let mut rhs = vpub_new.lc(); let mut rhs = vpub_new.lc();
// Witness rt (merkle tree root) // Witness rt (merkle tree root)
let rt = witness_u256(cs.namespace(|| "rt"), self.rt.as_ref().map(|v| &v[..])).unwrap(); let rt = witness_u256(
cs.namespace(|| "rt"),
self.rt.as_ref().map(|v| &v[..])
).unwrap();
// Witness h_sig // Witness h_sig
let h_sig = witness_u256( let h_sig = witness_u256(
cs.namespace(|| "h_sig"), cs.namespace(|| "h_sig"),
self.h_sig.as_ref().map(|v| &v[..]), self.h_sig.as_ref().map(|v| &v[..])
) ).unwrap();
.unwrap();
// Witness phi // Witness phi
let phi = witness_u252(cs.namespace(|| "phi"), self.phi.as_ref().map(|v| &v[..])).unwrap(); let phi = witness_u252(
cs.namespace(|| "phi"),
self.phi.as_ref().map(|v| &v[..])
).unwrap();
let mut input_notes = vec![]; let mut input_notes = vec![];
let mut lhs_total = self.vpub_old; let mut lhs_total = self.vpub_old;
@@ -92,14 +110,17 @@ impl<E: Engine> Circuit<E> for JoinSplit {
} }
// Allocate the value of the note // Allocate the value of the note
let value = NoteValue::new(cs.namespace(|| "value"), input.value)?; let value = NoteValue::new(
cs.namespace(|| "value"),
input.value
)?;
// Compute the nonce (for PRF inputs) which is false // Compute the nonce (for PRF inputs) which is false
// for the first input, and true for the second input. // for the first input, and true for the second input.
let nonce = match i { let nonce = match i {
0 => false, 0 => false,
1 => true, 1 => true,
_ => unreachable!(), _ => unreachable!()
}; };
// Perform input note computations // Perform input note computations
@@ -112,7 +133,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
&h_sig, &h_sig,
nonce, nonce,
input.auth_path, input.auth_path,
&rt, &rt
)?); )?);
// Add the note value to the left hand side of // Add the note value to the left hand side of
@@ -127,8 +148,10 @@ impl<E: Engine> Circuit<E> for JoinSplit {
{ {
// Expected sum of the left hand side of the balance // Expected sum of the left hand side of the balance
// equation, expressed as a 64-bit unsigned integer // equation, expressed as a 64-bit unsigned integer
let lhs_total = let lhs_total = NoteValue::new(
NoteValue::new(cs.namespace(|| "total value of left hand side"), lhs_total)?; cs.namespace(|| "total value of left hand side"),
lhs_total
)?;
// Enforce that the left hand side can be expressed as a 64-bit // Enforce that the left hand side can be expressed as a 64-bit
// integer // integer
@@ -136,7 +159,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
|| "left hand side can be expressed as a 64-bit unsigned integer", || "left hand side can be expressed as a 64-bit unsigned integer",
|_| lhs.clone(), |_| lhs.clone(),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| lhs_total.lc(), |_| lhs_total.lc()
); );
} }
@@ -146,14 +169,17 @@ impl<E: Engine> Circuit<E> for JoinSplit {
for (i, output) in self.outputs.into_iter().enumerate() { for (i, output) in self.outputs.into_iter().enumerate() {
let cs = &mut cs.namespace(|| format!("output {}", i)); let cs = &mut cs.namespace(|| format!("output {}", i));
let value = NoteValue::new(cs.namespace(|| "value"), output.value)?; let value = NoteValue::new(
cs.namespace(|| "value"),
output.value
)?;
// Compute the nonce (for PRF inputs) which is false // Compute the nonce (for PRF inputs) which is false
// for the first output, and true for the second output. // for the first output, and true for the second output.
let nonce = match i { let nonce = match i {
0 => false, 0 => false,
1 => true, 1 => true,
_ => unreachable!(), _ => unreachable!()
}; };
// Perform output note computations // Perform output note computations
@@ -164,7 +190,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
output.r, output.r,
&phi, &phi,
&h_sig, &h_sig,
nonce, nonce
)?); )?);
// Add the note value to the right hand side of // Add the note value to the right hand side of
@@ -177,7 +203,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
|| "balance equation", || "balance equation",
|_| lhs.clone(), |_| lhs.clone(),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| rhs, |_| rhs
); );
let mut public_inputs = vec![]; let mut public_inputs = vec![];
@@ -203,14 +229,15 @@ impl<E: Engine> Circuit<E> for JoinSplit {
pub struct NoteValue { pub struct NoteValue {
value: Option<u64>, value: Option<u64>,
// Least significant digit first // Least significant digit first
bits: Vec<AllocatedBit>, bits: Vec<AllocatedBit>
} }
impl NoteValue { impl NoteValue {
fn new<E, CS>(mut cs: CS, value: Option<u64>) -> Result<NoteValue, SynthesisError> fn new<E, CS>(
where mut cs: CS,
E: Engine, value: Option<u64>
CS: ConstraintSystem<E>, ) -> Result<NoteValue, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>,
{ {
let mut values; let mut values;
match value { match value {
@@ -220,7 +247,7 @@ impl NoteValue {
values.push(Some(val & 1 == 1)); values.push(Some(val & 1 == 1));
val >>= 1; val >>= 1;
} }
} },
None => { None => {
values = vec![None; 64]; values = vec![None; 64];
} }
@@ -228,23 +255,24 @@ impl NoteValue {
let mut bits = vec![]; let mut bits = vec![];
for (i, value) in values.into_iter().enumerate() { for (i, value) in values.into_iter().enumerate() {
bits.push(AllocatedBit::alloc( bits.push(
AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)), cs.namespace(|| format!("bit {}", i)),
value, value
)?); )?
);
} }
Ok(NoteValue { Ok(NoteValue {
value: value, value: value,
bits: bits, bits: bits
}) })
} }
/// Encodes the bits of the value into little-endian /// Encodes the bits of the value into little-endian
/// byte order. /// byte order.
fn bits_le(&self) -> Vec<Boolean> { fn bits_le(&self) -> Vec<Boolean> {
self.bits self.bits.chunks(8)
.chunks(8)
.flat_map(|v| v.iter().rev()) .flat_map(|v| v.iter().rev())
.cloned() .cloned()
.map(|e| Boolean::from(e)) .map(|e| Boolean::from(e))
@@ -276,16 +304,13 @@ fn witness_bits<E, CS>(
mut cs: CS, mut cs: CS,
value: Option<&[u8]>, value: Option<&[u8]>,
num_bits: usize, num_bits: usize,
skip_bits: usize, skip_bits: usize
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>,
E: Engine,
CS: ConstraintSystem<E>,
{ {
let bit_values = if let Some(value) = value { let bit_values = if let Some(value) = value {
let mut tmp = vec![]; let mut tmp = vec![];
for b in value for b in value.iter()
.iter()
.flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1)) .flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1))
.skip(skip_bits) .skip(skip_bits)
{ {
@@ -302,35 +327,37 @@ where
for (i, value) in bit_values.into_iter().enumerate() { for (i, value) in bit_values.into_iter().enumerate() {
bits.push(Boolean::from(AllocatedBit::alloc( bits.push(Boolean::from(AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)), cs.namespace(|| format!("bit {}", i)),
value, value
)?)); )?));
} }
Ok(bits) Ok(bits)
} }
fn witness_u256<E, CS>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError> fn witness_u256<E, CS>(
where cs: CS,
E: Engine, value: Option<&[u8]>,
CS: ConstraintSystem<E>, ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 256, 0) witness_bits(cs, value, 256, 0)
} }
fn witness_u252<E, CS>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError> fn witness_u252<E, CS>(
where cs: CS,
E: Engine, value: Option<&[u8]>,
CS: ConstraintSystem<E>, ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 252, 4) witness_bits(cs, value, 252, 4)
} }
#[test] #[test]
fn test_sprout_constraints() { fn test_sprout_constraints() {
use bellman::gadgets::test::*; use pairing::bls12_381::{Bls12};
use pairing::bls12_381::Bls12; use ::circuit::test::*;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian};
let test_vector = include_bytes!("test_vectors.dat"); let test_vector = include_bytes!("test_vectors.dat");
let mut test_vector = &test_vector[..]; let mut test_vector = &test_vector[..];
@@ -366,7 +393,9 @@ fn test_sprout_constraints() {
} }
let mut position = test_vector.read_u64::<LittleEndian>().unwrap(); let mut position = test_vector.read_u64::<LittleEndian>().unwrap();
for i in 0..TREE_DEPTH { for i in 0..TREE_DEPTH {
auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1); auth_path[i].as_mut().map(|p| {
p.1 = (position & 1) == 1
});
position >>= 1; position >>= 1;
} }
@@ -378,13 +407,15 @@ fn test_sprout_constraints() {
let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); let r = Some(CommitmentRandomness(get_u256(&mut test_vector)));
let a_sk = Some(SpendingKey(get_u256(&mut test_vector))); let a_sk = Some(SpendingKey(get_u256(&mut test_vector)));
inputs.push(JSInput { inputs.push(
JSInput {
value: value, value: value,
a_sk: a_sk, a_sk: a_sk,
rho: rho, rho: rho,
r: r, r: r,
auth_path: auth_path, auth_path: auth_path
}); }
);
} }
let mut outputs = vec![]; let mut outputs = vec![];
@@ -395,11 +426,13 @@ fn test_sprout_constraints() {
get_u256(&mut test_vector); get_u256(&mut test_vector);
let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); let r = Some(CommitmentRandomness(get_u256(&mut test_vector)));
outputs.push(JSOutput { outputs.push(
JSOutput {
value: value, value: value,
a_pk: a_pk, a_pk: a_pk,
r: r, r: r
}); }
);
} }
let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap()); let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap());
@@ -421,7 +454,7 @@ fn test_sprout_constraints() {
phi: phi, phi: phi,
inputs: inputs, inputs: inputs,
outputs: outputs, outputs: outputs,
rt: rt, rt: rt
}; };
js.synthesize(&mut cs).unwrap(); js.synthesize(&mut cs).unwrap();
@@ -432,10 +465,7 @@ fn test_sprout_constraints() {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1989085); assert_eq!(cs.num_constraints(), 1989085);
assert_eq!(cs.num_inputs(), 10); assert_eq!(cs.num_inputs(), 10);
assert_eq!( assert_eq!(cs.hash(), "1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c");
cs.hash(),
"1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c"
);
let mut expected_inputs = vec![]; let mut expected_inputs = vec![];
expected_inputs.extend(rt.unwrap().to_vec()); expected_inputs.extend(rt.unwrap().to_vec());
@@ -446,14 +476,10 @@ fn test_sprout_constraints() {
expected_inputs.extend(mac2.to_vec()); expected_inputs.extend(mac2.to_vec());
expected_inputs.extend(cm1.to_vec()); expected_inputs.extend(cm1.to_vec());
expected_inputs.extend(cm2.to_vec()); expected_inputs.extend(cm2.to_vec());
expected_inputs expected_inputs.write_u64::<LittleEndian>(vpub_old.unwrap()).unwrap();
.write_u64::<LittleEndian>(vpub_old.unwrap()) expected_inputs.write_u64::<LittleEndian>(vpub_new.unwrap()).unwrap();
.unwrap();
expected_inputs
.write_u64::<LittleEndian>(vpub_new.unwrap())
.unwrap();
use bellman::gadgets::multipack; use circuit::multipack;
let expected_inputs = multipack::bytes_to_bits(&expected_inputs); let expected_inputs = multipack::bytes_to_bits(&expected_inputs);
let expected_inputs = multipack::compute_multipacking::<Bls12>(&expected_inputs); let expected_inputs = multipack::compute_multipacking::<Bls12>(&expected_inputs);

View File

@@ -1,13 +1,13 @@
use bellman::gadgets::boolean::Boolean; use pairing::{Engine};
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use circuit::boolean::{Boolean};
use super::commitment::note_comm;
use super::prfs::*;
use super::*; use super::*;
use super::prfs::*;
use super::commitment::note_comm;
pub struct OutputNote { pub struct OutputNote {
pub cm: Vec<Boolean>, pub cm: Vec<Boolean>
} }
impl OutputNote { impl OutputNote {
@@ -18,29 +18,37 @@ impl OutputNote {
r: Option<CommitmentRandomness>, r: Option<CommitmentRandomness>,
phi: &[Boolean], phi: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool, nonce: bool
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>,
E: Engine,
CS: ConstraintSystem<E>,
{ {
let rho = prf_rho(cs.namespace(|| "rho"), phi, h_sig, nonce)?; let rho = prf_rho(
cs.namespace(|| "rho"),
phi,
h_sig,
nonce
)?;
let a_pk = witness_u256( let a_pk = witness_u256(
cs.namespace(|| "a_pk"), cs.namespace(|| "a_pk"),
a_pk.as_ref().map(|a_pk| &a_pk.0[..]), a_pk.as_ref().map(|a_pk| &a_pk.0[..])
)?; )?;
let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?; let r = witness_u256(
cs.namespace(|| "r"),
r.as_ref().map(|r| &r.0[..])
)?;
let cm = note_comm( let cm = note_comm(
cs.namespace(|| "cm computation"), cs.namespace(|| "cm computation"),
&a_pk, &a_pk,
&value.bits_le(), &value.bits_le(),
&rho, &rho,
&r, &r
)?; )?;
Ok(OutputNote { cm: cm }) Ok(OutputNote {
cm: cm
})
} }
} }

View File

@@ -1,7 +1,11 @@
use bellman::gadgets::boolean::Boolean; use pairing::{Engine};
use bellman::gadgets::sha256::sha256_block_no_padding;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use circuit::sha256::{
sha256_block_no_padding
};
use circuit::boolean::{
Boolean
};
fn prf<E, CS>( fn prf<E, CS>(
cs: CS, cs: CS,
@@ -10,11 +14,9 @@ fn prf<E, CS>(
c: bool, c: bool,
d: bool, d: bool,
x: &[Boolean], x: &[Boolean],
y: &[Boolean], y: &[Boolean]
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(x.len(), 252); assert_eq!(x.len(), 252);
assert_eq!(y.len(), 256); assert_eq!(y.len(), 256);
@@ -29,35 +31,27 @@ where
assert_eq!(image.len(), 512); assert_eq!(image.len(), 512);
sha256_block_no_padding(cs, &image) sha256_block_no_padding(
cs,
&image
)
} }
pub fn prf_a_pk<E, CS>(cs: CS, a_sk: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError> pub fn prf_a_pk<E, CS>(
where cs: CS,
E: Engine, a_sk: &[Boolean]
CS: ConstraintSystem<E>, ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
{ {
prf( prf(cs, true, true, false, false, a_sk, &(0..256).map(|_| Boolean::constant(false)).collect::<Vec<_>>())
cs,
true,
true,
false,
false,
a_sk,
&(0..256)
.map(|_| Boolean::constant(false))
.collect::<Vec<_>>(),
)
} }
pub fn prf_nf<E, CS>( pub fn prf_nf<E, CS>(
cs: CS, cs: CS,
a_sk: &[Boolean], a_sk: &[Boolean],
rho: &[Boolean], rho: &[Boolean]
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, true, true, true, false, a_sk, rho) prf(cs, true, true, true, false, a_sk, rho)
} }
@@ -66,11 +60,9 @@ pub fn prf_pk<E, CS>(
cs: CS, cs: CS,
a_sk: &[Boolean], a_sk: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool, nonce: bool
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, false, nonce, false, false, a_sk, h_sig) prf(cs, false, nonce, false, false, a_sk, h_sig)
} }
@@ -79,11 +71,9 @@ pub fn prf_rho<E, CS>(
cs: CS, cs: CS,
phi: &[Boolean], phi: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool, nonce: bool
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where E: Engine, CS: ConstraintSystem<E>
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, false, nonce, true, false, phi, h_sig) prf(cs, false, nonce, true, false, phi, h_sig)
} }

View File

@@ -1,7 +1,13 @@
use ff::{Field, PrimeField, PrimeFieldRepr}; use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::Engine; use pairing::Engine;
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use bellman::{
LinearCombination,
SynthesisError,
ConstraintSystem,
Variable,
Index
};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
@@ -10,13 +16,13 @@ use byteorder::{BigEndian, ByteOrder};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use blake2s_simd::{Params as Blake2sParams, State as Blake2sState}; use blake2_rfc::blake2s::Blake2s;
#[derive(Debug)] #[derive(Debug)]
enum NamedObject { enum NamedObject {
Constraint(usize), Constraint(usize),
Var(Variable), Var(Variable),
Namespace, Namespace
} }
/// Constraint system for testing purposes. /// Constraint system for testing purposes.
@@ -27,10 +33,10 @@ pub struct TestConstraintSystem<E: Engine> {
LinearCombination<E>, LinearCombination<E>,
LinearCombination<E>, LinearCombination<E>,
LinearCombination<E>, LinearCombination<E>,
String, String
)>, )>,
inputs: Vec<(E::Fr, String)>, inputs: Vec<(E::Fr, String)>,
aux: Vec<(E::Fr, String)>, aux: Vec<(E::Fr, String)>
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -42,7 +48,7 @@ impl PartialEq for OrderedVariable {
match (self.0.get_unchecked(), other.0.get_unchecked()) { match (self.0.get_unchecked(), other.0.get_unchecked()) {
(Index::Input(ref a), Index::Input(ref b)) => a == b, (Index::Input(ref a), Index::Input(ref b)) => a == b,
(Index::Aux(ref a), Index::Aux(ref b)) => a == b, (Index::Aux(ref a), Index::Aux(ref b)) => a == b,
_ => false, _ => false
} }
} }
} }
@@ -57,12 +63,15 @@ impl Ord for OrderedVariable {
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
(Index::Input(_), Index::Aux(_)) => Ordering::Less, (Index::Input(_), Index::Aux(_)) => Ordering::Less,
(Index::Aux(_), Index::Input(_)) => Ordering::Greater, (Index::Aux(_), Index::Input(_)) => Ordering::Greater
} }
} }
} }
fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> { fn proc_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
) -> BTreeMap<OrderedVariable, E::Fr>
{
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
for &(var, coeff) in terms { for &(var, coeff) in terms {
map.entry(OrderedVariable(var)) map.entry(OrderedVariable(var))
@@ -85,7 +94,11 @@ fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable,
map map
} }
fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { fn hash_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
h: &mut Blake2s
)
{
let map = proc_lc::<E>(terms); let map = proc_lc::<E>(terms);
let mut buf = [0u8; 9 + 32]; let mut buf = [0u8; 9 + 32];
@@ -97,7 +110,7 @@ fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
Index::Input(i) => { Index::Input(i) => {
buf[0] = b'I'; buf[0] = b'I';
BigEndian::write_u64(&mut buf[1..9], i as u64); BigEndian::write_u64(&mut buf[1..9], i as u64);
} },
Index::Aux(i) => { Index::Aux(i) => {
buf[0] = b'A'; buf[0] = b'A';
BigEndian::write_u64(&mut buf[1..9], i as u64); BigEndian::write_u64(&mut buf[1..9], i as u64);
@@ -113,14 +126,15 @@ fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
fn eval_lc<E: Engine>( fn eval_lc<E: Engine>(
terms: &[(Variable, E::Fr)], terms: &[(Variable, E::Fr)],
inputs: &[(E::Fr, String)], inputs: &[(E::Fr, String)],
aux: &[(E::Fr, String)], aux: &[(E::Fr, String)]
) -> E::Fr { ) -> E::Fr
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(var, ref coeff) in terms { for &(var, ref coeff) in terms {
let mut tmp = match var.get_unchecked() { let mut tmp = match var.get_unchecked() {
Index::Input(index) => inputs[index].0, Index::Input(index) => inputs[index].0,
Index::Aux(index) => aux[index].0, Index::Aux(index) => aux[index].0
}; };
tmp.mul_assign(&coeff); tmp.mul_assign(&coeff);
@@ -133,17 +147,14 @@ fn eval_lc<E: Engine>(
impl<E: Engine> TestConstraintSystem<E> { impl<E: Engine> TestConstraintSystem<E> {
pub fn new() -> TestConstraintSystem<E> { pub fn new() -> TestConstraintSystem<E> {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert( map.insert("ONE".into(), NamedObject::Var(TestConstraintSystem::<E>::one()));
"ONE".into(),
NamedObject::Var(TestConstraintSystem::<E>::one()),
);
TestConstraintSystem { TestConstraintSystem {
named_objects: map, named_objects: map,
current_namespace: vec![], current_namespace: vec![],
constraints: vec![], constraints: vec![],
inputs: vec![(E::Fr::one(), "ONE".into())], inputs: vec![(E::Fr::one(), "ONE".into())],
aux: vec![], aux: vec![]
} }
} }
@@ -156,9 +167,9 @@ impl<E: Engine> TestConstraintSystem<E> {
tmp tmp
}; };
let powers_of_two = (0..E::Fr::NUM_BITS) let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| {
.map(|i| E::Fr::from_str("2").unwrap().pow(&[i as u64])) E::Fr::from_str("2").unwrap().pow(&[i as u64])
.collect::<Vec<_>>(); }).collect::<Vec<_>>();
let pp = |s: &mut String, lc: &LinearCombination<E>| { let pp = |s: &mut String, lc: &LinearCombination<E>| {
write!(s, "(").unwrap(); write!(s, "(").unwrap();
@@ -185,7 +196,7 @@ impl<E: Engine> TestConstraintSystem<E> {
match var.0.get_unchecked() { match var.0.get_unchecked() {
Index::Input(i) => { Index::Input(i) => {
write!(s, "`{}`", &self.inputs[i].1).unwrap(); write!(s, "`{}`", &self.inputs[i].1).unwrap();
} },
Index::Aux(i) => { Index::Aux(i) => {
write!(s, "`{}`", &self.aux[i].1).unwrap(); write!(s, "`{}`", &self.aux[i].1).unwrap();
} }
@@ -215,7 +226,7 @@ impl<E: Engine> TestConstraintSystem<E> {
} }
pub fn hash(&self) -> String { pub fn hash(&self) -> String {
let mut h = Blake2sParams::new().hash_length(32).to_state(); let mut h = Blake2s::new(32);
{ {
let mut buf = [0u8; 24]; let mut buf = [0u8; 24];
@@ -248,41 +259,45 @@ impl<E: Engine> TestConstraintSystem<E> {
a.mul_assign(&b); a.mul_assign(&b);
if a != c { if a != c {
return Some(&*path); return Some(&*path)
} }
} }
None None
} }
pub fn is_satisfied(&self) -> bool { pub fn is_satisfied(&self) -> bool
{
self.which_is_unsatisfied().is_none() self.which_is_unsatisfied().is_none()
} }
pub fn num_constraints(&self) -> usize { pub fn num_constraints(&self) -> usize
{
self.constraints.len() self.constraints.len()
} }
pub fn set(&mut self, path: &str, to: E::Fr) { pub fn set(&mut self, path: &str, to: E::Fr)
{
match self.named_objects.get(path) { match self.named_objects.get(path) {
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { Some(&NamedObject::Var(ref v)) => {
match v.get_unchecked() {
Index::Input(index) => self.inputs[index].0 = to, Index::Input(index) => self.inputs[index].0 = to,
Index::Aux(index) => self.aux[index].0 = to, Index::Aux(index) => self.aux[index].0 = to
}, }
Some(e) => panic!( }
"tried to set path `{}` to value, but `{:?}` already exists there.", Some(e) => panic!("tried to set path `{}` to value, but `{:?}` already exists there.", path, e),
path, e _ => panic!("no variable exists at path: {}", path)
),
_ => panic!("no variable exists at path: {}", path),
} }
} }
pub fn verify(&self, expected: &[E::Fr]) -> bool { pub fn verify(&self, expected: &[E::Fr]) -> bool
{
assert_eq!(expected.len() + 1, self.inputs.len()); assert_eq!(expected.len() + 1, self.inputs.len());
for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) { for (a, b) in self.inputs.iter().skip(1).zip(expected.iter())
{
if &a.0 != b { if &a.0 != b {
return false; return false
} }
} }
@@ -293,7 +308,8 @@ impl<E: Engine> TestConstraintSystem<E> {
self.inputs.len() self.inputs.len()
} }
pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr { pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr
{
let (assignment, name) = self.inputs[index].clone(); let (assignment, name) = self.inputs[index].clone();
assert_eq!(path, name); assert_eq!(path, name);
@@ -301,17 +317,17 @@ impl<E: Engine> TestConstraintSystem<E> {
assignment assignment
} }
pub fn get(&mut self, path: &str) -> E::Fr { pub fn get(&mut self, path: &str) -> E::Fr
{
match self.named_objects.get(path) { match self.named_objects.get(path) {
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { Some(&NamedObject::Var(ref v)) => {
match v.get_unchecked() {
Index::Input(index) => self.inputs[index].0, Index::Input(index) => self.inputs[index].0,
Index::Aux(index) => self.aux[index].0, Index::Aux(index) => self.aux[index].0
}, }
Some(e) => panic!( }
"tried to get value of path `{}`, but `{:?}` exists there (not a variable)", Some(e) => panic!("tried to get value of path `{}`, but `{:?}` exists there (not a variable)", path, e),
path, e _ => panic!("no variable exists at path: {}", path)
),
_ => panic!("no variable exists at path: {}", path),
} }
} }
@@ -332,7 +348,8 @@ fn compute_path(ns: &[String], this: String) -> String {
let mut name = String::new(); let mut name = String::new();
let mut needs_separation = false; let mut needs_separation = false;
for ns in ns.iter().chain(Some(&this).into_iter()) { for ns in ns.iter().chain(Some(&this).into_iter())
{
if needs_separation { if needs_separation {
name += "/"; name += "/";
} }
@@ -347,11 +364,12 @@ fn compute_path(ns: &[String], this: String) -> String {
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> { impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
let index = self.aux.len(); let index = self.aux.len();
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
@@ -362,11 +380,12 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
let index = self.inputs.len(); let index = self.inputs.len();
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
@@ -377,13 +396,17 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>, LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
let index = self.constraints.len(); let index = self.constraints.len();
@@ -397,9 +420,7 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where where NR: Into<String>, N: FnOnce() -> NR
NR: Into<String>,
N: FnOnce() -> NR,
{ {
let name = name_fn().into(); let name = name_fn().into();
let path = compute_path(&self.current_namespace, name.clone()); let path = compute_path(&self.current_namespace, name.clone());
@@ -407,11 +428,13 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
self.current_namespace.push(name); self.current_namespace.push(name);
} }
fn pop_namespace(&mut self) { fn pop_namespace(&mut self)
{
assert!(self.current_namespace.pop().is_some()); assert!(self.current_namespace.pop().is_some());
} }
fn get_root(&mut self) -> &mut Self::Root { fn get_root(&mut self) -> &mut Self::Root
{
self self
} }
} }
@@ -424,26 +447,28 @@ fn test_cs() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0); assert_eq!(cs.num_constraints(), 0);
let a = cs let a = cs.namespace(|| "a").alloc(|| "var", || Ok(Fr::from_str("10").unwrap())).unwrap();
.namespace(|| "a") let b = cs.namespace(|| "b").alloc(|| "var", || Ok(Fr::from_str("4").unwrap())).unwrap();
.alloc(|| "var", || Ok(Fr::from_str("10").unwrap())) let c = cs.alloc(|| "product", || Ok(Fr::from_str("40").unwrap())).unwrap();
.unwrap();
let b = cs
.namespace(|| "b")
.alloc(|| "var", || Ok(Fr::from_str("4").unwrap()))
.unwrap();
let c = cs
.alloc(|| "product", || Ok(Fr::from_str("40").unwrap()))
.unwrap();
cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c); cs.enforce(
|| "mult",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1); assert_eq!(cs.num_constraints(), 1);
cs.set("a/var", Fr::from_str("4").unwrap()); cs.set("a/var", Fr::from_str("4").unwrap());
let one = TestConstraintSystem::<Bls12>::one(); let one = TestConstraintSystem::<Bls12>::one();
cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b); cs.enforce(
|| "eq",
|lc| lc + a,
|lc| lc + one,
|lc| lc + b
);
assert!(!cs.is_satisfied()); assert!(!cs.is_satisfied());
assert!(cs.which_is_unsatisfied() == Some("mult")); assert!(cs.which_is_unsatisfied() == Some("mult"));

View File

@@ -1,9 +1,16 @@
use ff::{Field, PrimeField}; use ff::{Field, PrimeField};
use pairing::Engine; use pairing::Engine;
use crate::{ConstraintSystem, LinearCombination, SynthesisError}; use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination
};
use super::boolean::{AllocatedBit, Boolean}; use super::boolean::{
Boolean,
AllocatedBit
};
use super::multieq::MultiEq; use super::multieq::MultiEq;
@@ -13,12 +20,13 @@ use super::multieq::MultiEq;
pub struct UInt32 { pub struct UInt32 {
// Least significant bit first // Least significant bit first
bits: Vec<Boolean>, bits: Vec<Boolean>,
value: Option<u32>, value: Option<u32>
} }
impl UInt32 { impl UInt32 {
/// Construct a constant `UInt32` from a `u32` /// Construct a constant `UInt32` from a `u32`
pub fn constant(value: u32) -> Self { pub fn constant(value: u32) -> Self
{
let mut bits = Vec::with_capacity(32); let mut bits = Vec::with_capacity(32);
let mut tmp = value; let mut tmp = value;
@@ -34,15 +42,17 @@ impl UInt32 {
UInt32 { UInt32 {
bits: bits, bits: bits,
value: Some(value), value: Some(value)
} }
} }
/// Allocate a `UInt32` in the constraint system /// Allocate a `UInt32` in the constraint system
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError> pub fn alloc<E, CS>(
where mut cs: CS,
E: Engine, value: Option<u32>
CS: ConstraintSystem<E>, ) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
let values = match value { let values = match value {
Some(mut val) => { Some(mut val) => {
@@ -54,24 +64,23 @@ impl UInt32 {
} }
v v
} },
None => vec![None; 32], None => vec![None; 32]
}; };
let bits = values let bits = values.into_iter()
.into_iter()
.enumerate() .enumerate()
.map(|(i, v)| { .map(|(i, v)| {
Ok(Boolean::from(AllocatedBit::alloc( Ok(Boolean::from(AllocatedBit::alloc(
cs.namespace(|| format!("allocated bit {}", i)), cs.namespace(|| format!("allocated bit {}", i)),
v, v
)?)) )?))
}) })
.collect::<Result<Vec<_>, SynthesisError>>()?; .collect::<Result<Vec<_>, SynthesisError>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits: bits,
value: value, value: value
}) })
} }
@@ -87,22 +96,19 @@ impl UInt32 {
value.as_mut().map(|v| *v <<= 1); value.as_mut().map(|v| *v <<= 1);
match b.get_value() { match b.get_value() {
Some(true) => { Some(true) => { value.as_mut().map(|v| *v |= 1); },
value.as_mut().map(|v| *v |= 1); Some(false) => {},
} None => { value = None; }
Some(false) => {}
None => {
value = None;
}
} }
} }
UInt32 { UInt32 {
value: value, value: value,
bits: bits.iter().rev().cloned().collect(), bits: bits.iter().rev().cloned().collect()
} }
} }
/// Turns this `UInt32` into its little-endian byte order representation. /// Turns this `UInt32` into its little-endian byte order representation.
pub fn into_bits(&self) -> Vec<Boolean> { pub fn into_bits(&self) -> Vec<Boolean> {
self.bits.clone() self.bits.clone()
@@ -110,7 +116,8 @@ impl UInt32 {
/// Converts a little-endian byte order representation of bits into a /// Converts a little-endian byte order representation of bits into a
/// `UInt32`. /// `UInt32`.
pub fn from_bits(bits: &[Boolean]) -> Self { pub fn from_bits(bits: &[Boolean]) -> Self
{
assert_eq!(bits.len(), 32); assert_eq!(bits.len(), 32);
let new_bits = bits.to_vec(); let new_bits = bits.to_vec();
@@ -124,36 +131,34 @@ impl UInt32 {
if b { if b {
value.as_mut().map(|v| *v |= 1); value.as_mut().map(|v| *v |= 1);
} }
}
&Boolean::Is(ref b) => match b.get_value() {
Some(true) => {
value.as_mut().map(|v| *v |= 1);
}
Some(false) => {}
None => value = None,
}, },
&Boolean::Not(ref b) => match b.get_value() { &Boolean::Is(ref b) => {
Some(false) => { match b.get_value() {
value.as_mut().map(|v| *v |= 1); Some(true) => { value.as_mut().map(|v| *v |= 1); },
Some(false) => {},
None => { value = None }
} }
Some(true) => {}
None => value = None,
}, },
&Boolean::Not(ref b) => {
match b.get_value() {
Some(false) => { value.as_mut().map(|v| *v |= 1); },
Some(true) => {},
None => { value = None }
}
}
} }
} }
UInt32 { UInt32 {
value: value, value: value,
bits: new_bits, bits: new_bits
} }
} }
pub fn rotr(&self, by: usize) -> Self { pub fn rotr(&self, by: usize) -> Self {
let by = by % 32; let by = by % 32;
let new_bits = self let new_bits = self.bits.iter()
.bits
.iter()
.skip(by) .skip(by)
.chain(self.bits.iter()) .chain(self.bits.iter())
.take(32) .take(32)
@@ -162,7 +167,7 @@ impl UInt32 {
UInt32 { UInt32 {
bits: new_bits, bits: new_bits,
value: self.value.map(|v| v.rotate_right(by as u32)), value: self.value.map(|v| v.rotate_right(by as u32))
} }
} }
@@ -171,8 +176,7 @@ impl UInt32 {
let fill = Boolean::constant(false); let fill = Boolean::constant(false);
let new_bits = self let new_bits = self.bits
.bits
.iter() // The bits are least significant first .iter() // The bits are least significant first
.skip(by) // Skip the bits that will be lost during the shift .skip(by) // Skip the bits that will be lost during the shift
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
@@ -182,7 +186,7 @@ impl UInt32 {
UInt32 { UInt32 {
bits: new_bits, bits: new_bits,
value: self.value.map(|v| v >> by as u32), value: self.value.map(|v| v >> by as u32)
} }
} }
@@ -192,22 +196,21 @@ impl UInt32 {
b: &Self, b: &Self,
c: &Self, c: &Self,
tri_fn: F, tri_fn: F,
circuit_fn: U, circuit_fn: U
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where where E: Engine,
E: Engine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
F: Fn(u32, u32, u32) -> u32, F: Fn(u32, u32, u32) -> u32,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>, U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>
{ {
let new_value = match (a.value, b.value, c.value) { let new_value = match (a.value, b.value, c.value) {
(Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)), (Some(a), Some(b), Some(c)) => {
_ => None, Some(tri_fn(a, b, c))
},
_ => None
}; };
let bits = a let bits = a.bits.iter()
.bits
.iter()
.zip(b.bits.iter()) .zip(b.bits.iter())
.zip(c.bits.iter()) .zip(c.bits.iter())
.enumerate() .enumerate()
@@ -216,75 +219,98 @@ impl UInt32 {
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits: bits,
value: new_value, value: new_value
}) })
} }
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c) /// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
/// during SHA256. /// during SHA256.
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError> pub fn sha256_maj<E, CS>(
where cs: CS,
E: Engine, a: &Self,
CS: ConstraintSystem<E>, b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop( Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c),
cs, |cs, i, a, b, c| {
Boolean::sha256_maj(
cs.namespace(|| format!("maj {}", i)),
a, a,
b, b,
c, c
|a, b, c| (a & b) ^ (a & c) ^ (b & c), )
|cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c), }
) )
} }
/// Compute the `ch` value `(a and b) xor ((not a) and c)` /// Compute the `ch` value `(a and b) xor ((not a) and c)`
/// during SHA256. /// during SHA256.
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError> pub fn sha256_ch<E, CS>(
where cs: CS,
E: Engine, a: &Self,
CS: ConstraintSystem<E>, b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop( Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c),
cs, |cs, i, a, b, c| {
Boolean::sha256_ch(
cs.namespace(|| format!("ch {}", i)),
a, a,
b, b,
c, c
|a, b, c| (a & b) ^ ((!a) & c), )
|cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c), }
) )
} }
/// XOR this `UInt32` with another `UInt32` /// XOR this `UInt32` with another `UInt32`
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError> pub fn xor<E, CS>(
where &self,
E: Engine, mut cs: CS,
CS: ConstraintSystem<E>, other: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
let new_value = match (self.value, other.value) { let new_value = match (self.value, other.value) {
(Some(a), Some(b)) => Some(a ^ b), (Some(a), Some(b)) => {
_ => None, Some(a ^ b)
},
_ => None
}; };
let bits = self let bits = self.bits.iter()
.bits
.iter()
.zip(other.bits.iter()) .zip(other.bits.iter())
.enumerate() .enumerate()
.map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) .map(|(i, (a, b))| {
Boolean::xor(
cs.namespace(|| format!("xor of bit {}", i)),
a,
b
)
})
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits: bits,
value: new_value, value: new_value
}) })
} }
/// Perform modular addition of several `UInt32` objects. /// Perform modular addition of several `UInt32` objects.
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError> pub fn addmany<E, CS, M>(
where mut cs: M,
E: Engine, operands: &[Self]
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root = MultiEq<E, CS>>, M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{ {
// Make some arbitrary bounds for ourselves to avoid overflows // Make some arbitrary bounds for ourselves to avoid overflows
// in the scalar field // in the scalar field
@@ -311,7 +337,7 @@ impl UInt32 {
match op.value { match op.value {
Some(val) => { Some(val) => {
result_value.as_mut().map(|v| *v += val as u64); result_value.as_mut().map(|v| *v += val as u64);
} },
None => { None => {
// If any of our operands have unknown value, we won't // If any of our operands have unknown value, we won't
// know the value of the result // know the value of the result
@@ -355,7 +381,7 @@ impl UInt32 {
// Allocate the bit // Allocate the bit
let b = AllocatedBit::alloc( let b = AllocatedBit::alloc(
cs.namespace(|| format!("result bit {}", i)), cs.namespace(|| format!("result bit {}", i)),
result_value.map(|v| (v >> i) & 1 == 1), result_value.map(|v| (v >> i) & 1 == 1)
)?; )?;
// Add this bit to the result combination // Add this bit to the result combination
@@ -376,34 +402,28 @@ impl UInt32 {
Ok(UInt32 { Ok(UInt32 {
bits: result_bits, bits: result_bits,
value: modular_value, value: modular_value
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::UInt32; use rand::{XorShiftRng, SeedableRng, Rng};
use crate::gadgets::boolean::Boolean; use ::circuit::boolean::{Boolean};
use crate::gadgets::multieq::MultiEq; use super::{UInt32};
use crate::gadgets::test::*;
use crate::ConstraintSystem;
use ff::Field; use ff::Field;
use pairing::bls12_381::Bls12; use pairing::bls12_381::{Bls12};
use rand_core::{RngCore, SeedableRng}; use ::circuit::test::*;
use rand_xorshift::XorShiftRng; use bellman::{ConstraintSystem};
use circuit::multieq::MultiEq;
#[test] #[test]
fn test_uint32_from_bits_be() { fn test_uint32_from_bits_be() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32) let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>();
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits_be(&v); let b = UInt32::from_bits_be(&v);
@@ -411,18 +431,19 @@ mod test {
match bit { match bit {
&Boolean::Constant(bit) => { &Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
} },
_ => unreachable!(), _ => unreachable!()
} }
} }
let expected_to_be_same = b.into_bits_be(); let expected_to_be_same = b.into_bits_be();
for x in v.iter().zip(expected_to_be_same.iter()) { for x in v.iter().zip(expected_to_be_same.iter())
{
match x { match x {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {} (&Boolean::Constant(true), &Boolean::Constant(true)) => {},
(&Boolean::Constant(false), &Boolean::Constant(false)) => {} (&Boolean::Constant(false), &Boolean::Constant(false)) => {},
_ => unreachable!(), _ => unreachable!()
} }
} }
} }
@@ -430,15 +451,10 @@ mod test {
#[test] #[test]
fn test_uint32_from_bits() { fn test_uint32_from_bits() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32) let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>();
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits(&v); let b = UInt32::from_bits(&v);
@@ -446,18 +462,19 @@ mod test {
match bit { match bit {
&Boolean::Constant(bit) => { &Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
} },
_ => unreachable!(), _ => unreachable!()
} }
} }
let expected_to_be_same = b.into_bits(); let expected_to_be_same = b.into_bits();
for x in v.iter().zip(expected_to_be_same.iter()) { for x in v.iter().zip(expected_to_be_same.iter())
{
match x { match x {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {} (&Boolean::Constant(true), &Boolean::Constant(true)) => {},
(&Boolean::Constant(false), &Boolean::Constant(false)) => {} (&Boolean::Constant(false), &Boolean::Constant(false)) => {},
_ => unreachable!(), _ => unreachable!()
} }
} }
} }
@@ -465,17 +482,14 @@ mod test {
#[test] #[test]
fn test_uint32_xor() { fn test_uint32_xor() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let mut expected = a ^ b ^ c; let mut expected = a ^ b ^ c;
@@ -494,10 +508,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
} },
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
} },
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
@@ -510,17 +524,14 @@ mod test {
#[test] #[test]
fn test_uint32_addmany_constants() { fn test_uint32_addmany_constants() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let a_bit = UInt32::constant(a); let a_bit = UInt32::constant(a);
let b_bit = UInt32::constant(b); let b_bit = UInt32::constant(b);
@@ -530,8 +541,7 @@ mod test {
let r = { let r = {
let mut cs = MultiEq::new(&mut cs); let mut cs = MultiEq::new(&mut cs);
let r = let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
r r
}; };
@@ -553,18 +563,15 @@ mod test {
#[test] #[test]
fn test_uint32_addmany() { fn test_uint32_addmany() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let d = rng.next_u32(); let d: u32 = rng.gen();
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
@@ -588,11 +595,13 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
} },
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(_) => {
unreachable!()
} }
&Boolean::Constant(_) => unreachable!(),
} }
expected >>= 1; expected >>= 1;
@@ -611,12 +620,9 @@ mod test {
#[test] #[test]
fn test_uint32_rotr() { fn test_uint32_rotr() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let mut num = rng.next_u32(); let mut num = rng.gen();
let a = UInt32::constant(num); let a = UInt32::constant(num);
@@ -631,8 +637,8 @@ mod test {
match b { match b {
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert_eq!(b, tmp & 1 == 1); assert_eq!(b, tmp & 1 == 1);
} },
_ => unreachable!(), _ => unreachable!()
} }
tmp >>= 1; tmp >>= 1;
@@ -644,14 +650,11 @@ mod test {
#[test] #[test]
fn test_uint32_shr() { fn test_uint32_shr() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..50 { for _ in 0..50 {
for i in 0..60 { for i in 0..60 {
let num = rng.next_u32(); let num = rng.gen();
let a = UInt32::constant(num).shr(i); let a = UInt32::constant(num).shr(i);
let b = UInt32::constant(num.wrapping_shr(i as u32)); let b = UInt32::constant(num.wrapping_shr(i as u32));
@@ -667,17 +670,14 @@ mod test {
#[test] #[test]
fn test_uint32_sha256_maj() { fn test_uint32_sha256_maj() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let mut expected = (a & b) ^ (a & c) ^ (b & c); let mut expected = (a & b) ^ (a & c) ^ (b & c);
@@ -695,10 +695,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
} },
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
} },
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
@@ -711,17 +711,14 @@ mod test {
#[test] #[test]
fn test_uint32_sha256_ch() { fn test_uint32_sha256_ch() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let mut expected = (a & b) ^ ((!a) & c); let mut expected = (a & b) ^ ((!a) & c);
@@ -739,10 +736,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
} },
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
} },
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }

View File

@@ -2,31 +2,39 @@
/// This is chosen to be some random string that we couldn't have anticipated when we designed /// This is chosen to be some random string that we couldn't have anticipated when we designed
/// the algorithm, for rigidity purposes. /// the algorithm, for rigidity purposes.
/// We deliberately use an ASCII hex string of 32 bytes here. /// We deliberately use an ASCII hex string of 32 bytes here.
pub const GH_FIRST_BLOCK: &'static [u8; 64] = pub const GH_FIRST_BLOCK: &'static [u8; 64]
b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0"; = b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
// BLAKE2s invocation personalizations // BLAKE2s invocation personalizations
/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk) /// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk)
pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk"; pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8]
= b"Zcashivk";
/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho) /// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho)
pub const PRF_NF_PERSONALIZATION: &'static [u8; 8] = b"Zcash_nf"; pub const PRF_NF_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_nf";
// Group hash personalizations // Group hash personalizations
/// BLAKE2s Personalization for Pedersen hash generators. /// BLAKE2s Personalization for Pedersen hash generators.
pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"Zcash_PH"; pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_PH";
/// BLAKE2s Personalization for the group hash for key diversification /// BLAKE2s Personalization for the group hash for key diversification
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] = b"Zcash_gd"; pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_gd";
/// BLAKE2s Personalization for the spending key base point /// BLAKE2s Personalization for the spending key base point
pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_G_"; pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_G_";
/// BLAKE2s Personalization for the proof generation key base point /// BLAKE2s Personalization for the proof generation key base point
pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_H_"; pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_H_";
/// BLAKE2s Personalization for the value commitment generator for the value /// BLAKE2s Personalization for the value commitment generator for the value
pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_cv"; pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_cv";
/// BLAKE2s Personalization for the nullifier position generator (for computing rho) /// BLAKE2s Personalization for the nullifier position generator (for computing rho)
pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_J_"; pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_J_";

View File

@@ -1,8 +1,17 @@
use jubjub::{edwards, JubjubEngine, PrimeOrder}; // Copyright The Hush Developers
// Released under the GPLv3
use ff::PrimeField; use jubjub::{
JubjubEngine,
PrimeOrder,
edwards
};
use blake2s_simd::Params; use ff::{
PrimeField
};
use blake2_rfc::blake2s::Blake2s;
use constants; use constants;
/// Produces a random point in the Jubjub curve. /// Produces a random point in the Jubjub curve.
@@ -11,22 +20,21 @@ use constants;
pub fn group_hash<E: JubjubEngine>( pub fn group_hash<E: JubjubEngine>(
tag: &[u8], tag: &[u8],
personalization: &[u8], personalization: &[u8],
params: &E::Params, params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>> { ) -> Option<edwards::Point<E, PrimeOrder>>
{
assert_eq!(personalization.len(), 8); assert_eq!(personalization.len(), 8);
// Check to see that scalar field is 255 bits // Check to see that scalar field is 255 bits
assert!(E::Fr::NUM_BITS == 255); assert!(E::Fr::NUM_BITS == 255);
let h = Params::new() let mut h = Blake2s::with_params(32, &[], &[], personalization);
.hash_length(32) h.update(constants::GH_FIRST_BLOCK);
.personal(personalization) h.update(tag);
.to_state() let h = h.finalize().as_ref().to_vec();
.update(constants::GH_FIRST_BLOCK) assert!(h.len() == 32);
.update(tag)
.finalize();
match edwards::Point::<E, _>::read(h.as_ref(), params) { match edwards::Point::<E, _>::read(&h[..], params) {
Ok(p) => { Ok(p) => {
let p = p.mul_by_cofactor(params); let p = p.mul_by_cofactor(params);
@@ -35,7 +43,7 @@ pub fn group_hash<E: JubjubEngine>(
} else { } else {
None None
} }
} },
Err(_) => None, Err(_) => None
} }
} }

View File

@@ -1,12 +1,24 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; use super::{
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
montgomery
};
use rand_core::RngCore; use rand::{
Rng
};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::io::{self, Read, Write}; use std::io::{
self,
Write,
Read
};
// Represents the affine point (X/Z, Y/Z) via the extended // Represents the affine point (X/Z, Y/Z) via the extended
// twisted Edwards coordinates. // twisted Edwards coordinates.
@@ -19,38 +31,46 @@ pub struct Point<E: JubjubEngine, Subgroup> {
y: E::Fr, y: E::Fr,
t: E::Fr, t: E::Fr,
z: E::Fr, z: E::Fr,
_marker: PhantomData<Subgroup>, _marker: PhantomData<Subgroup>
} }
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2> { fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
{
Point { Point {
x: from.x, x: from.x,
y: from.y, y: from.y,
t: from.t, t: from.t,
z: from.z, z: from.z,
_marker: PhantomData, _marker: PhantomData
} }
} }
impl<E: JubjubEngine> From<&Point<E, Unknown>> for Point<E, Unknown> { impl<E: JubjubEngine> From<&Point<E, Unknown>> for Point<E, Unknown>
fn from(p: &Point<E, Unknown>) -> Point<E, Unknown> { {
fn from(p: &Point<E, Unknown>) -> Point<E, Unknown>
{
p.clone() p.clone()
} }
} }
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown> { impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown> { {
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(&p) convert_subgroup(&p)
} }
} }
impl<E: JubjubEngine> From<&Point<E, PrimeOrder>> for Point<E, Unknown> { impl<E: JubjubEngine> From<&Point<E, PrimeOrder>> for Point<E, Unknown>
fn from(p: &Point<E, PrimeOrder>) -> Point<E, Unknown> { {
fn from(p: &Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(p) convert_subgroup(p)
} }
} }
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> { impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
convert_subgroup(self) convert_subgroup(self)
} }
@@ -81,7 +101,11 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
} }
impl<E: JubjubEngine> Point<E, Unknown> { impl<E: JubjubEngine> Point<E, Unknown> {
pub fn read<R: Read>(reader: R, params: &E::Params) -> io::Result<Self> { pub fn read<R: Read>(
reader: R,
params: &E::Params
) -> io::Result<Self>
{
let mut y_repr = <E::Fr as PrimeField>::Repr::default(); let mut y_repr = <E::Fr as PrimeField>::Repr::default();
y_repr.read_le(reader)?; y_repr.read_le(reader)?;
@@ -89,18 +113,22 @@ impl<E: JubjubEngine> Point<E, Unknown> {
y_repr.as_mut()[3] &= 0x7fffffffffffffff; y_repr.as_mut()[3] &= 0x7fffffffffffffff;
match E::Fr::from_repr(y_repr) { match E::Fr::from_repr(y_repr) {
Ok(y) => match Self::get_for_y(y, x_sign, params) { Ok(y) => {
match Self::get_for_y(y, x_sign, params) {
Some(p) => Ok(p), Some(p) => Ok(p),
None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")), None => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve"))
}
}
}, },
Err(_) => Err(io::Error::new( Err(_) => {
io::ErrorKind::InvalidInput, Err(io::Error::new(io::ErrorKind::InvalidInput, "y is not in field"))
"y is not in field", }
)),
} }
} }
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self> { pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self>
{
// Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1)
// This is defined for all valid y-coordinates, // This is defined for all valid y-coordinates,
// as dy^2 + 1 = 0 has no solution in Fr. // as dy^2 + 1 = 0 has no solution in Fr.
@@ -136,30 +164,33 @@ impl<E: JubjubEngine> Point<E, Unknown> {
y: y, y: y,
t: t, t: t,
z: E::Fr::one(), z: E::Fr::one(),
_marker: PhantomData, _marker: PhantomData
}) })
},
None => None
} }
None => None, },
} None => None
}
None => None,
} }
} }
/// This guarantees the point is in the prime order subgroup /// This guarantees the point is in the prime order subgroup
#[must_use] #[must_use]
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder> { pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder>
let tmp = self.double(params).double(params).double(params); {
let tmp = self.double(params)
.double(params)
.double(params);
convert_subgroup(&tmp) convert_subgroup(&tmp)
} }
pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self { pub fn rand<R: Rng>(rng: &mut R, params: &E::Params) -> Self
{
loop { loop {
let y = E::Fr::random(rng); let y: E::Fr = rng.gen();
let sign = rng.next_u32() % 2 != 0;
if let Some(p) = Self::get_for_y(y, sign, params) { if let Some(p) = Self::get_for_y(y, rng.gen(), params) {
return p; return p;
} }
} }
@@ -167,7 +198,11 @@ impl<E: JubjubEngine> Point<E, Unknown> {
} }
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> { impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> { pub fn write<W: Write>(
&self,
writer: W
) -> io::Result<()>
{
let (x, y) = self.into_xy(); let (x, y) = self.into_xy();
assert_eq!(E::Fr::NUM_BITS, 255); assert_eq!(E::Fr::NUM_BITS, 255);
@@ -182,12 +217,16 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
} }
/// Convert from a Montgomery point /// Convert from a Montgomery point
pub fn from_montgomery(m: &montgomery::Point<E, Subgroup>, params: &E::Params) -> Self { pub fn from_montgomery(
m: &montgomery::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
match m.into_xy() { match m.into_xy() {
None => { None => {
// Map the point at infinity to the neutral element. // Map the point at infinity to the neutral element.
Point::zero() Point::zero()
} },
Some((x, y)) => { Some((x, y)) => {
// The map from a Montgomery curve is defined as: // The map from a Montgomery curve is defined as:
// (x, y) -> (u, v) where // (x, y) -> (u, v) where
@@ -220,7 +259,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: neg1, y: neg1,
t: E::Fr::zero(), t: E::Fr::zero(),
z: E::Fr::one(), z: E::Fr::one(),
_marker: PhantomData, _marker: PhantomData
} }
} else { } else {
// Otherwise, as stated above, the mapping is still // Otherwise, as stated above, the mapping is still
@@ -279,7 +318,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: v, y: v,
t: t, t: t,
z: z, z: z,
_marker: PhantomData, _marker: PhantomData
} }
} }
} }
@@ -302,11 +341,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: E::Fr::one(), y: E::Fr::one(),
t: E::Fr::zero(), t: E::Fr::zero(),
z: E::Fr::one(), z: E::Fr::one(),
_marker: PhantomData, _marker: PhantomData
} }
} }
pub fn into_xy(&self) -> (E::Fr, E::Fr) { pub fn into_xy(&self) -> (E::Fr, E::Fr)
{
let zinv = self.z.inverse().unwrap(); let zinv = self.z.inverse().unwrap();
let mut x = self.x; let mut x = self.x;
@@ -393,12 +433,13 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: y3, y: y3,
t: t3, t: t3,
z: z3, z: z3,
_marker: PhantomData, _marker: PhantomData
} }
} }
#[must_use] #[must_use]
pub fn add(&self, other: &Self, params: &E::Params) -> Self { pub fn add(&self, other: &Self, params: &E::Params) -> Self
{
// See "Twisted Edwards Curves Revisited" // See "Twisted Edwards Curves Revisited"
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
// 3.1 Unified Addition in E^e // 3.1 Unified Addition in E^e
@@ -465,12 +506,17 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: y3, y: y3,
t: t3, t: t3,
z: z3, z: z3,
_marker: PhantomData, _marker: PhantomData
} }
} }
#[must_use] #[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(&self, scalar: S, params: &E::Params) -> Self { pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
&self,
scalar: S,
params: &E::Params
) -> Self
{
// Standard double-and-add scalar multiplication // Standard double-and-add scalar multiplication
let mut res = Self::zero(); let mut res = Self::zero();

View File

@@ -24,7 +24,10 @@ use group_hash::group_hash;
use constants; use constants;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{
Bls12,
Fr
};
/// This is an implementation of the twisted Edwards Jubjub curve. /// This is an implementation of the twisted Edwards Jubjub curve.
pub mod edwards; pub mod edwards;
@@ -77,7 +80,7 @@ pub enum FixedGenerators {
/// base at spend time. /// base at spend time.
SpendingKeyGenerator = 5, SpendingKeyGenerator = 5,
Max = 6, Max = 6
} }
pub trait ToUniform { pub trait ToUniform {
@@ -148,18 +151,10 @@ pub struct JubjubBls12 {
} }
impl JubjubParams<Bls12> for JubjubBls12 { impl JubjubParams<Bls12> for JubjubBls12 {
fn edwards_d(&self) -> &Fr { fn edwards_d(&self) -> &Fr { &self.edwards_d }
&self.edwards_d fn montgomery_a(&self) -> &Fr { &self.montgomery_a }
} fn montgomery_2a(&self) -> &Fr { &self.montgomery_2a }
fn montgomery_a(&self) -> &Fr { fn scale(&self) -> &Fr { &self.scale }
&self.montgomery_a
}
fn montgomery_2a(&self) -> &Fr {
&self.montgomery_2a
}
fn scale(&self) -> &Fr {
&self.scale
}
fn pedersen_hash_generators(&self) -> &[edwards::Point<Bls12, PrimeOrder>] { fn pedersen_hash_generators(&self) -> &[edwards::Point<Bls12, PrimeOrder>] {
&self.pedersen_hash_generators &self.pedersen_hash_generators
} }
@@ -175,10 +170,12 @@ impl JubjubParams<Bls12> for JubjubBls12 {
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] { fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] {
&self.pedersen_circuit_generators &self.pedersen_circuit_generators
} }
fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bls12, PrimeOrder> { fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bls12, PrimeOrder>
{
&self.fixed_base_generators[base as usize] &self.fixed_base_generators[base as usize]
} }
fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>] { fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>]
{
&self.fixed_base_circuit_generators[base as usize][..] &self.fixed_base_circuit_generators[base as usize][..]
} }
fn pedersen_hash_exp_window_size() -> u32 { fn pedersen_hash_exp_window_size() -> u32 {
@@ -194,19 +191,13 @@ impl JubjubBls12 {
let mut tmp_params = JubjubBls12 { let mut tmp_params = JubjubBls12 {
// d = -(10240/10241) // d = -(10240/10241)
edwards_d: Fr::from_str( edwards_d: Fr::from_str("19257038036680949359750312669786877991949435402254120286184196891950884077233").unwrap(),
"19257038036680949359750312669786877991949435402254120286184196891950884077233",
)
.unwrap(),
// A = 40962 // A = 40962
montgomery_a: montgomery_a, montgomery_a: montgomery_a,
// 2A = 2.A // 2A = 2.A
montgomery_2a: montgomery_2a, montgomery_2a: montgomery_2a,
// scaling factor = sqrt(4 / (a - d)) // scaling factor = sqrt(4 / (a - d))
scale: Fr::from_str( scale: Fr::from_str("17814886934372412843466061268024708274627479829237077604635722030778476050649").unwrap(),
"17814886934372412843466061268024708274627479829237077604635722030778476050649",
)
.unwrap(),
// We'll initialize these below // We'll initialize these below
pedersen_hash_generators: vec![], pedersen_hash_generators: vec![],
@@ -219,14 +210,19 @@ impl JubjubBls12 {
fn find_group_hash<E: JubjubEngine>( fn find_group_hash<E: JubjubEngine>(
m: &[u8], m: &[u8],
personalization: &[u8; 8], personalization: &[u8; 8],
params: &E::Params, params: &E::Params
) -> edwards::Point<E, PrimeOrder> { ) -> edwards::Point<E, PrimeOrder>
{
let mut tag = m.to_vec(); let mut tag = m.to_vec();
let i = tag.len(); let i = tag.len();
tag.push(0u8); tag.push(0u8);
loop { loop {
let gh = group_hash(&tag, personalization, params); let gh = group_hash(
&tag,
personalization,
params
);
// We don't want to overflow and start reusing generators // We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value()); assert!(tag[i] != u8::max_value());
@@ -243,18 +239,18 @@ impl JubjubBls12 {
let mut pedersen_hash_generators = vec![]; let mut pedersen_hash_generators = vec![];
for m in 0..5 { for m in 0..5 {
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{WriteBytesExt, LittleEndian};
let mut segment_number = [0u8; 4]; let mut segment_number = [0u8; 4];
(&mut segment_number[0..4]) (&mut segment_number[0..4]).write_u32::<LittleEndian>(m).unwrap();
.write_u32::<LittleEndian>(m)
.unwrap();
pedersen_hash_generators.push(find_group_hash( pedersen_hash_generators.push(
find_group_hash(
&segment_number, &segment_number,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params, &tmp_params
)); )
);
} }
// Check for duplicates, far worse than spec inconsistencies! // Check for duplicates, far worse than spec inconsistencies!
@@ -311,46 +307,25 @@ impl JubjubBls12 {
// Create the bases for other parts of the protocol // Create the bases for other parts of the protocol
{ {
let mut fixed_base_generators = let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize];
vec![edwards::Point::zero(); FixedGenerators::Max as usize];
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = find_group_hash( fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
&[], find_group_hash(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params);
constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
find_group_hash( find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params);
b"r",
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NullifierPosition as usize] = find_group_hash( fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
&[], find_group_hash(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params);
constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = find_group_hash( fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] =
b"v", find_group_hash(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
find_group_hash( find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
b"r",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = find_group_hash( fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
&[], find_group_hash(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params);
constants::SPENDING_KEY_GENERATOR_PERSONALIZATION,
&tmp_params,
);
// Check for duplicates, far worse than spec inconsistencies! // Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in fixed_base_generators.iter().enumerate() { for (i, p1) in fixed_base_generators.iter().enumerate() {
@@ -438,14 +413,10 @@ fn test_jubjub_bls12() {
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31"); let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap(); let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y( let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str( Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
false, false,
&params, &params
) ).unwrap();
.unwrap();
assert!(p == q); assert!(p == q);
@@ -453,14 +424,10 @@ fn test_jubjub_bls12() {
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1"); let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap(); let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y( let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str( Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
true, true,
&params, &params
) ).unwrap();
.unwrap();
assert!(p == q); assert!(p == q);
} }

View File

@@ -1,8 +1,16 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; use super::{
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
edwards
};
use rand_core::RngCore; use rand::{
Rng
};
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -11,25 +19,29 @@ pub struct Point<E: JubjubEngine, Subgroup> {
x: E::Fr, x: E::Fr,
y: E::Fr, y: E::Fr,
infinity: bool, infinity: bool,
_marker: PhantomData<Subgroup>, _marker: PhantomData<Subgroup>
} }
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2> { fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
{
Point { Point {
x: from.x, x: from.x,
y: from.y, y: from.y,
infinity: from.infinity, infinity: from.infinity,
_marker: PhantomData, _marker: PhantomData
} }
} }
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown> { impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown> { {
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(&p) convert_subgroup(&p)
} }
} }
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> { impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
convert_subgroup(self) convert_subgroup(self)
} }
@@ -40,13 +52,16 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
match (self.infinity, other.infinity) { match (self.infinity, other.infinity) {
(true, true) => true, (true, true) => true,
(true, false) | (false, true) => false, (true, false) | (false, true) => false,
(false, false) => self.x == other.x && self.y == other.y, (false, false) => {
self.x == other.x && self.y == other.y
}
} }
} }
} }
impl<E: JubjubEngine> Point<E, Unknown> { impl<E: JubjubEngine> Point<E, Unknown> {
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self> { pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self>
{
// Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x)
let mut x2 = x; let mut x2 = x;
@@ -68,28 +83,33 @@ impl<E: JubjubEngine> Point<E, Unknown> {
x: x, x: x,
y: y, y: y,
infinity: false, infinity: false,
_marker: PhantomData, _marker: PhantomData
}); })
} },
None => None, None => None
} }
} }
/// This guarantees the point is in the prime order subgroup /// This guarantees the point is in the prime order subgroup
#[must_use] #[must_use]
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder> { pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder>
let tmp = self.double(params).double(params).double(params); {
let tmp = self.double(params)
.double(params)
.double(params);
convert_subgroup(&tmp) convert_subgroup(&tmp)
} }
pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self { pub fn rand<R: Rng>(rng: &mut R, params: &E::Params) -> Self
{
loop { loop {
let x = E::Fr::random(rng); let x: E::Fr = rng.gen();
let sign = rng.next_u32() % 2 != 0;
match Self::get_for_x(x, sign, params) { match Self::get_for_x(x, rng.gen(), params) {
Some(p) => return p, Some(p) => {
return p
},
None => {} None => {}
} }
} }
@@ -98,7 +118,11 @@ impl<E: JubjubEngine> Point<E, Unknown> {
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> { impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
/// Convert from an Edwards point /// Convert from an Edwards point
pub fn from_edwards(e: &edwards::Point<E, Subgroup>, params: &E::Params) -> Self { pub fn from_edwards(
e: &edwards::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
let (x, y) = e.into_xy(); let (x, y) = e.into_xy();
if y == E::Fr::one() { if y == E::Fr::one() {
@@ -126,7 +150,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: E::Fr::zero(), x: E::Fr::zero(),
y: E::Fr::zero(), y: E::Fr::zero(),
infinity: false, infinity: false,
_marker: PhantomData, _marker: PhantomData
} }
} else { } else {
// The mapping is defined as above. // The mapping is defined as above.
@@ -153,7 +177,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: u, x: u,
y: v, y: v,
infinity: false, infinity: false,
_marker: PhantomData, _marker: PhantomData
} }
} }
} }
@@ -174,11 +198,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: E::Fr::zero(), x: E::Fr::zero(),
y: E::Fr::zero(), y: E::Fr::zero(),
infinity: true, infinity: true,
_marker: PhantomData, _marker: PhantomData
} }
} }
pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)> { pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)>
{
if self.infinity { if self.infinity {
None None
} else { } else {
@@ -248,12 +273,13 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: x3, x: x3,
y: y3, y: y3,
infinity: false, infinity: false,
_marker: PhantomData, _marker: PhantomData
} }
} }
#[must_use] #[must_use]
pub fn add(&self, other: &Self, params: &E::Params) -> Self { pub fn add(&self, other: &Self, params: &E::Params) -> Self
{
// This is a standard affine point addition formula // This is a standard affine point addition formula
// See 4.3.2 The group law for Weierstrass curves // See 4.3.2 The group law for Weierstrass curves
// Montgomery curves and the Montgomery Ladder // Montgomery curves and the Montgomery Ladder
@@ -276,10 +302,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
{ {
let mut tmp = other.x; let mut tmp = other.x;
tmp.sub_assign(&self.x); tmp.sub_assign(&self.x);
delta.mul_assign( delta.mul_assign(&tmp.inverse().expect("self.x != other.x, so this must be nonzero"));
&tmp.inverse()
.expect("self.x != other.x, so this must be nonzero"),
);
} }
let mut x3 = delta; let mut x3 = delta;
@@ -298,7 +321,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: x3, x: x3,
y: y3, y: y3,
infinity: false, infinity: false,
_marker: PhantomData, _marker: PhantomData
} }
} }
} }
@@ -306,7 +329,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
} }
#[must_use] #[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(&self, scalar: S, params: &E::Params) -> Self { pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
&self,
scalar: S,
params: &E::Params
) -> Self
{
// Standard double-and-add scalar multiplication // Standard double-and-add scalar multiplication
let mut res = Self::zero(); let mut res = Self::zero();

View File

@@ -1,9 +1,20 @@
use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; use super::{
JubjubEngine,
JubjubParams,
PrimeOrder,
montgomery,
edwards
};
use ff::{Field, LegendreSymbol, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{
Field,
PrimeField,
PrimeFieldRepr,
SqrtField,
LegendreSymbol
};
use rand_core::{RngCore, SeedableRng}; use rand::{XorShiftRng, SeedableRng, Rand};
use rand_xorshift::XorShiftRng;
pub fn test_suite<E: JubjubEngine>(params: &E::Params) { pub fn test_suite<E: JubjubEngine>(params: &E::Params) {
test_back_and_forth::<E>(params); test_back_and_forth::<E>(params);
@@ -18,7 +29,12 @@ pub fn test_suite<E: JubjubEngine>(params: &E::Params) {
test_read_write::<E>(params); test_read_write::<E>(params);
} }
fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(x: E::Fr, y: E::Fr, params: &P) -> bool { fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(
x: E::Fr,
y: E::Fr,
params: &P
) -> bool
{
let mut lhs = y; let mut lhs = y;
lhs.square(); lhs.square();
@@ -39,8 +55,9 @@ fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(x: E::Fr, y: E::Fr, par
fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>( fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>(
x: E::Fr, x: E::Fr,
y: E::Fr, y: E::Fr,
params: &P, params: &P
) -> bool { ) -> bool
{
let mut x2 = x; let mut x2 = x;
x2.square(); x2.square();
@@ -61,10 +78,7 @@ fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>(
} }
fn test_loworder<E: JubjubEngine>(params: &E::Params) { fn test_loworder<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let inf = montgomery::Point::zero(); let inf = montgomery::Point::zero();
// try to find a point of order 8 // try to find a point of order 8
@@ -95,18 +109,15 @@ fn test_loworder<E: JubjubEngine>(params: &E::Params) {
fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) { fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) {
use self::edwards::Point; use self::edwards::Point;
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
// Pick a random point and multiply it by the cofactor // Pick a random point and multiply it by the cofactor
let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params); let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params);
let mut a = E::Fs::random(rng); let mut a = E::Fs::rand(rng);
let b = E::Fs::random(rng); let b = E::Fs::rand(rng);
let c = E::Fs::random(rng); let c = E::Fs::rand(rng);
let res1 = base.mul(a, params).mul(b, params).mul(c, params); let res1 = base.mul(a, params).mul(b, params).mul(c, params);
let res2 = base.mul(b, params).mul(c, params).mul(a, params); let res2 = base.mul(b, params).mul(c, params).mul(a, params);
@@ -132,15 +143,10 @@ fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) {
fn test_order<E: JubjubEngine>(params: &E::Params) { fn test_order<E: JubjubEngine>(params: &E::Params) {
use self::edwards::Point; use self::edwards::Point;
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// The neutral element is in the prime order subgroup. // The neutral element is in the prime order subgroup.
assert!(Point::<E, PrimeOrder>::zero() assert!(Point::<E, PrimeOrder>::zero().as_prime_order(params).is_some());
.as_prime_order(params)
.is_some());
for _ in 0..50 { for _ in 0..50 {
// Pick a random point and multiply it by the cofactor // Pick a random point and multiply it by the cofactor
@@ -164,10 +170,7 @@ fn test_order<E: JubjubEngine>(params: &E::Params) {
} }
fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) { fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
use self::montgomery::Point; use self::montgomery::Point;
@@ -191,10 +194,7 @@ fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) {
} }
fn test_identities<E: JubjubEngine>(params: &E::Params) { fn test_identities<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
{ {
use self::edwards::Point; use self::edwards::Point;
@@ -228,28 +228,26 @@ fn test_identities<E: JubjubEngine>(params: &E::Params) {
} }
fn test_get_for<E: JubjubEngine>(params: &E::Params) { fn test_get_for<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let y = E::Fr::random(rng); let y = E::Fr::rand(rng);
let sign = rng.next_u32() % 2 == 1; let sign = bool::rand(rng);
if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) { if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) {
assert!(p.into_xy().0.into_repr().is_odd() == sign); assert!(p.into_xy().0.into_repr().is_odd() == sign);
p = p.negate(); p = p.negate();
assert!(edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap() == p); assert!(
edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap()
==
p
);
} }
} }
} }
fn test_read_write<E: JubjubEngine>(params: &E::Params) { fn test_read_write<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let e = edwards::Point::<E, _>::rand(rng, params); let e = edwards::Point::<E, _>::rand(rng, params);
@@ -264,10 +262,7 @@ fn test_read_write<E: JubjubEngine>(params: &E::Params) {
} }
fn test_rand<E: JubjubEngine>(params: &E::Params) { fn test_rand<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let p = montgomery::Point::<E, _>::rand(rng, params); let p = montgomery::Point::<E, _>::rand(rng, params);
@@ -286,13 +281,10 @@ fn test_rand<E: JubjubEngine>(params: &E::Params) {
} }
fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) { fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x5d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let s = E::Fs::random(rng); let s = E::Fs::rand(rng);
let edwards_p1 = edwards::Point::<E, _>::rand(rng, params); let edwards_p1 = edwards::Point::<E, _>::rand(rng, params);
let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params); let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params);
let mont_p2 = montgomery::Point::<E, _>::rand(rng, params); let mont_p2 = montgomery::Point::<E, _>::rand(rng, params);
@@ -301,9 +293,13 @@ fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) {
let mont = mont_p1.add(&mont_p2, params).mul(s, params); let mont = mont_p1.add(&mont_p2, params).mul(s, params);
let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params); let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params);
assert!(montgomery::Point::from_edwards(&edwards, params) == mont); assert!(
montgomery::Point::from_edwards(&edwards, params) == mont
);
assert!(edwards::Point::from_montgomery(&mont, params) == edwards); assert!(
edwards::Point::from_montgomery(&mont, params) == edwards
);
} }
} }
@@ -387,7 +383,8 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
let mut pacc = E::Fs::zero().into_repr(); let mut pacc = E::Fs::zero().into_repr();
let mut nacc = E::Fs::char(); let mut nacc = E::Fs::char();
for _ in 0..params.pedersen_hash_chunks_per_generator() { for _ in 0..params.pedersen_hash_chunks_per_generator()
{
// tmp = cur * 4 // tmp = cur * 4
let mut tmp = cur; let mut tmp = cur;
tmp.mul2(); tmp.mul2();

23
sapling-crypto/src/lib.rs Normal file
View File

@@ -0,0 +1,23 @@
extern crate pairing;
extern crate bellman;
extern crate blake2_rfc;
extern crate digest;
extern crate ff;
extern crate rand;
extern crate byteorder;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[cfg(test)]
extern crate crypto;
pub mod jubjub;
pub mod group_hash;
pub mod circuit;
pub mod pedersen_hash;
pub mod primitives;
pub mod constants;
pub mod redjubjub;
pub mod util;

View File

@@ -4,13 +4,14 @@ use jubjub::*;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum Personalization { pub enum Personalization {
NoteCommitment, NoteCommitment,
MerkleTree(usize), MerkleTree(usize)
} }
impl Personalization { impl Personalization {
pub fn get_bits(&self) -> Vec<bool> { pub fn get_bits(&self) -> Vec<bool> {
match *self { match *self {
Personalization::NoteCommitment => vec![true, true, true, true, true, true], Personalization::NoteCommitment =>
vec![true, true, true, true, true, true],
Personalization::MerkleTree(num) => { Personalization::MerkleTree(num) => {
assert!(num < 63); assert!(num < 63);
@@ -23,16 +24,12 @@ impl Personalization {
pub fn pedersen_hash<E, I>( pub fn pedersen_hash<E, I>(
personalization: Personalization, personalization: Personalization,
bits: I, bits: I,
params: &E::Params, params: &E::Params
) -> edwards::Point<E, PrimeOrder> ) -> edwards::Point<E, PrimeOrder>
where where I: IntoIterator<Item=bool>,
I: IntoIterator<Item = bool>, E: JubjubEngine
E: JubjubEngine,
{ {
let mut bits = personalization let mut bits = personalization.get_bits().into_iter().chain(bits.into_iter());
.get_bits()
.into_iter()
.chain(bits.into_iter());
let mut result = edwards::Point::zero(); let mut result = edwards::Point::zero();
let mut generators = params.pedersen_hash_exp_table().iter(); let mut generators = params.pedersen_hash_exp_table().iter();
@@ -82,8 +79,7 @@ where
break; break;
} }
let mut table: &[Vec<edwards::Point<E, _>>] = let mut table: &[Vec<edwards::Point<E, _>>] = &generators.next().expect("we don't have enough generators");
&generators.next().expect("we don't have enough generators");
let window = JubjubBls12::pedersen_hash_exp_window_size(); let window = JubjubBls12::pedersen_hash_exp_window_size();
let window_mask = (1 << window) - 1; let window_mask = (1 << window) - 1;

View File

@@ -4,30 +4,44 @@ use constants;
use group_hash::group_hash; use group_hash::group_hash;
use pedersen_hash::{pedersen_hash, Personalization}; use pedersen_hash::{
pedersen_hash,
Personalization
};
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{
LittleEndian,
WriteBytesExt
};
use jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder}; use jubjub::{
JubjubEngine,
JubjubParams,
edwards,
PrimeOrder,
FixedGenerators
};
use blake2s_simd::Params as Blake2sParams; use blake2_rfc::blake2s::Blake2s;
#[derive(Clone)] #[derive(Clone)]
pub struct ValueCommitment<E: JubjubEngine> { pub struct ValueCommitment<E: JubjubEngine> {
pub value: u64, pub value: u64,
pub randomness: E::Fs, pub randomness: E::Fs
} }
impl<E: JubjubEngine> ValueCommitment<E> { impl<E: JubjubEngine> ValueCommitment<E> {
pub fn cm(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> { pub fn cm(
params &self,
.generator(FixedGenerators::ValueCommitmentValue) params: &E::Params
) -> edwards::Point<E, PrimeOrder>
{
params.generator(FixedGenerators::ValueCommitmentValue)
.mul(self.value, params) .mul(self.value, params)
.add( .add(
&params &params.generator(FixedGenerators::ValueCommitmentRandomness)
.generator(FixedGenerators::ValueCommitmentRandomness)
.mul(self.randomness, params), .mul(self.randomness, params),
params, params
) )
} }
} }
@@ -35,16 +49,15 @@ impl<E: JubjubEngine> ValueCommitment<E> {
#[derive(Clone)] #[derive(Clone)]
pub struct ProofGenerationKey<E: JubjubEngine> { pub struct ProofGenerationKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>, pub ak: edwards::Point<E, PrimeOrder>,
pub nsk: E::Fs, pub nsk: E::Fs
} }
impl<E: JubjubEngine> ProofGenerationKey<E> { impl<E: JubjubEngine> ProofGenerationKey<E> {
pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> { pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
ViewingKey { ViewingKey {
ak: self.ak.clone(), ak: self.ak.clone(),
nk: params nk: params.generator(FixedGenerators::ProofGenerationKey)
.generator(FixedGenerators::ProofGenerationKey) .mul(self.nsk, params)
.mul(self.nsk, params),
} }
} }
} }
@@ -52,16 +65,19 @@ impl<E: JubjubEngine> ProofGenerationKey<E> {
#[derive(Debug)] #[derive(Debug)]
pub struct ViewingKey<E: JubjubEngine> { pub struct ViewingKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>, pub ak: edwards::Point<E, PrimeOrder>,
pub nk: edwards::Point<E, PrimeOrder>, pub nk: edwards::Point<E, PrimeOrder>
} }
impl<E: JubjubEngine> ViewingKey<E> { impl<E: JubjubEngine> ViewingKey<E> {
pub fn rk(&self, ar: E::Fs, params: &E::Params) -> edwards::Point<E, PrimeOrder> { pub fn rk(
&self,
ar: E::Fs,
params: &E::Params
) -> edwards::Point<E, PrimeOrder> {
self.ak.add( self.ak.add(
&params &params.generator(FixedGenerators::SpendingKeyGenerator)
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(ar, params), .mul(ar, params),
params, params
) )
} }
@@ -71,14 +87,9 @@ impl<E: JubjubEngine> ViewingKey<E> {
self.ak.write(&mut preimage[0..32]).unwrap(); self.ak.write(&mut preimage[0..32]).unwrap();
self.nk.write(&mut preimage[32..64]).unwrap(); self.nk.write(&mut preimage[32..64]).unwrap();
let mut h = [0; 32]; let mut h = Blake2s::with_params(32, &[], &[], constants::CRH_IVK_PERSONALIZATION);
h.copy_from_slice( h.update(&preimage);
Blake2sParams::new() let mut h = h.finalize().as_ref().to_vec();
.hash_length(32)
.personal(constants::CRH_IVK_PERSONALIZATION)
.hash(&preimage)
.as_bytes(),
);
// Drop the most significant five bits, so it can be interpreted as a scalar. // Drop the most significant five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111; h[31] &= 0b0000_0111;
@@ -92,14 +103,15 @@ impl<E: JubjubEngine> ViewingKey<E> {
pub fn into_payment_address( pub fn into_payment_address(
&self, &self,
diversifier: Diversifier, diversifier: Diversifier,
params: &E::Params, params: &E::Params
) -> Option<PaymentAddress<E>> { ) -> Option<PaymentAddress<E>>
{
diversifier.g_d(params).map(|g_d| { diversifier.g_d(params).map(|g_d| {
let pk_d = g_d.mul(self.ivk(), params); let pk_d = g_d.mul(self.ivk(), params);
PaymentAddress { PaymentAddress {
pk_d: pk_d, pk_d: pk_d,
diversifier: diversifier, diversifier: diversifier
} }
}) })
} }
@@ -111,20 +123,17 @@ pub struct Diversifier(pub [u8; 11]);
impl Diversifier { impl Diversifier {
pub fn g_d<E: JubjubEngine>( pub fn g_d<E: JubjubEngine>(
&self, &self,
params: &E::Params, params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>> { ) -> Option<edwards::Point<E, PrimeOrder>>
group_hash::<E>( {
&self.0, group_hash::<E>(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params)
constants::KEY_DIVERSIFICATION_PERSONALIZATION,
params,
)
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PaymentAddress<E: JubjubEngine> { pub struct PaymentAddress<E: JubjubEngine> {
pub pk_d: edwards::Point<E, PrimeOrder>, pub pk_d: edwards::Point<E, PrimeOrder>,
pub diversifier: Diversifier, pub diversifier: Diversifier
} }
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> { impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
@@ -134,7 +143,11 @@ impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
} }
impl<E: JubjubEngine> PaymentAddress<E> { impl<E: JubjubEngine> PaymentAddress<E> {
pub fn g_d(&self, params: &E::Params) -> Option<edwards::Point<E, PrimeOrder>> { pub fn g_d(
&self,
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
self.diversifier.g_d(params) self.diversifier.g_d(params)
} }
@@ -142,13 +155,16 @@ impl<E: JubjubEngine> PaymentAddress<E> {
&self, &self,
value: u64, value: u64,
randomness: E::Fs, randomness: E::Fs,
params: &E::Params, params: &E::Params
) -> Option<Note<E>> { ) -> Option<Note<E>>
self.g_d(params).map(|g_d| Note { {
self.g_d(params).map(|g_d| {
Note {
value: value, value: value,
r: randomness, r: randomness,
g_d: g_d, g_d: g_d,
pk_d: self.pk_d.clone(), pk_d: self.pk_d.clone()
}
}) })
} }
} }
@@ -162,7 +178,7 @@ pub struct Note<E: JubjubEngine> {
/// The public key of the address, g_d^ivk /// The public key of the address, g_d^ivk
pub pk_d: edwards::Point<E, PrimeOrder>, pub pk_d: edwards::Point<E, PrimeOrder>,
/// The commitment randomness /// The commitment randomness
pub r: E::Fs, pub r: E::Fs
} }
impl<E: JubjubEngine> PartialEq for Note<E> { impl<E: JubjubEngine> PartialEq for Note<E> {
@@ -185,14 +201,13 @@ impl<E: JubjubEngine> Note<E> {
} }
/// Computes the note commitment, returning the full point. /// Computes the note commitment, returning the full point.
fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> { fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder>
{
// Calculate the note contents, as bytes // Calculate the note contents, as bytes
let mut note_contents = vec![]; let mut note_contents = vec![];
// Writing the value in little endian // Writing the value in little endian
(&mut note_contents) (&mut note_contents).write_u64::<LittleEndian>(self.value).unwrap();
.write_u64::<LittleEndian>(self.value)
.unwrap();
// Write g_d // Write g_d
self.g_d.write(&mut note_contents).unwrap(); self.g_d.write(&mut note_contents).unwrap();
@@ -205,44 +220,50 @@ impl<E: JubjubEngine> Note<E> {
// Compute the Pedersen hash of the note contents // Compute the Pedersen hash of the note contents
let hash_of_contents = pedersen_hash( let hash_of_contents = pedersen_hash(
Personalization::NoteCommitment, Personalization::NoteCommitment,
note_contents note_contents.into_iter()
.into_iter() .flat_map(|byte| {
.flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)), (0..8).map(move |i| ((byte >> i) & 1) == 1)
params, }),
params
); );
// Compute final commitment // Compute final commitment
params params.generator(FixedGenerators::NoteCommitmentRandomness)
.generator(FixedGenerators::NoteCommitmentRandomness)
.mul(self.r, params) .mul(self.r, params)
.add(&hash_of_contents, params) .add(&hash_of_contents, params)
} }
/// Computes the nullifier given the viewing key and /// Computes the nullifier given the viewing key and
/// note position /// note position
pub fn nf(&self, viewing_key: &ViewingKey<E>, position: u64, params: &E::Params) -> Vec<u8> { pub fn nf(
&self,
viewing_key: &ViewingKey<E>,
position: u64,
params: &E::Params
) -> Vec<u8>
{
// Compute rho = cm + position.G // Compute rho = cm + position.G
let rho = self.cm_full_point(params).add( let rho = self
&params .cm_full_point(params)
.generator(FixedGenerators::NullifierPosition) .add(
&params.generator(FixedGenerators::NullifierPosition)
.mul(position, params), .mul(position, params),
params, params
); );
// Compute nf = BLAKE2s(nk | rho) // Compute nf = BLAKE2s(nk | rho)
let mut nf_preimage = [0u8; 64]; let mut nf_preimage = [0u8; 64];
viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap(); viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap();
rho.write(&mut nf_preimage[32..64]).unwrap(); rho.write(&mut nf_preimage[32..64]).unwrap();
Blake2sParams::new() let mut h = Blake2s::with_params(32, &[], &[], constants::PRF_NF_PERSONALIZATION);
.hash_length(32) h.update(&nf_preimage);
.personal(constants::PRF_NF_PERSONALIZATION)
.hash(&nf_preimage) h.finalize().as_ref().to_vec()
.as_bytes()
.to_vec()
} }
/// Computes the note commitment /// Computes the note commitment
pub fn cm(&self, params: &E::Params) -> E::Fr { pub fn cm(&self, params: &E::Params) -> E::Fr
{
// The commitment is in the prime order subgroup, so mapping the // The commitment is in the prime order subgroup, so mapping the
// commitment to the x-coordinate is an injective encoding. // commitment to the x-coordinate is an injective encoding.
self.cm_full_point(params).into_xy().0 self.cm_full_point(params).into_xy().0

View File

@@ -1,12 +1,12 @@
//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve. //! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve.
//! See section 5.4.6 of the Sapling protocol specification. //! See section 5.4.6 of the Sapling protocol specification.
use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown};
use ff::{Field, PrimeField, PrimeFieldRepr}; use ff::{Field, PrimeField, PrimeFieldRepr};
use rand_core::RngCore; use rand::{Rng, Rand};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use util::hash_to_scalar; use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point};
use util::{hash_to_scalar};
fn read_scalar<E: JubjubEngine, R: Read>(reader: R) -> io::Result<E::Fs> { fn read_scalar<E: JubjubEngine, R: Read>(reader: R) -> io::Result<E::Fs> {
let mut s_repr = <E::Fs as PrimeField>::Repr::default(); let mut s_repr = <E::Fs as PrimeField>::Repr::default();
@@ -71,7 +71,7 @@ impl<E: JubjubEngine> PrivateKey<E> {
write_scalar::<E, W>(&self.0, writer) write_scalar::<E, W>(&self.0, writer)
} }
pub fn sign<R: RngCore>( pub fn sign<R: Rng>(
&self, &self,
msg: &[u8], msg: &[u8],
rng: &mut R, rng: &mut R,
@@ -148,15 +148,10 @@ impl<E: JubjubEngine> PublicKey<E> {
Err(_) => return false, Err(_) => return false,
}; };
// 0 = h_G(-S . P_G + R + c . vk) // 0 = h_G(-S . P_G + R + c . vk)
self.0 self.0.mul(c, params).add(&r, params).add(
.mul(c, params)
.add(&r, params)
.add(
&params.generator(p_g).mul(s, params).negate().into(), &params.generator(p_g).mul(s, params).negate().into(),
params, params
) ).mul_by_cofactor(params).eq(&Point::zero())
.mul_by_cofactor(params)
.eq(&Point::zero())
} }
} }
@@ -168,12 +163,13 @@ pub struct BatchEntry<'a, E: JubjubEngine> {
// TODO: #82: This is a naive implementation currently, // TODO: #82: This is a naive implementation currently,
// and doesn't use multiexp. // and doesn't use multiexp.
pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>( pub fn batch_verify<'a, E: JubjubEngine, R: Rng>(
rng: &mut R, rng: &mut R,
batch: &[BatchEntry<'a, E>], batch: &[BatchEntry<'a, E>],
p_g: FixedGenerators, p_g: FixedGenerators,
params: &E::Params, params: &E::Params,
) -> bool { ) -> bool
{
let mut acc = Point::<E, Unknown>::zero(); let mut acc = Point::<E, Unknown>::zero();
for entry in batch { for entry in batch {
@@ -188,7 +184,7 @@ pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>(
let mut c = h_star::<E>(&entry.sig.rbar[..], entry.msg); let mut c = h_star::<E>(&entry.sig.rbar[..], entry.msg);
let z = E::Fs::random(rng); let z = E::Fs::rand(rng);
s.mul_assign(&z); s.mul_assign(&z);
s.negate(); s.negate();
@@ -210,45 +206,33 @@ pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::SeedableRng; use rand::thread_rng;
use rand_xorshift::XorShiftRng;
use crate::jubjub::{edwards, fs::Fs, JubjubBls12}; use jubjub::{JubjubBls12, fs::Fs, edwards};
use super::*; use super::*;
#[test] #[test]
fn test_batch_verify() { fn test_batch_verify() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut thread_rng();
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let p_g = FixedGenerators::SpendingKeyGenerator; let p_g = FixedGenerators::SpendingKeyGenerator;
let sk1 = PrivateKey::<Bls12>(Fs::random(rng)); let sk1 = PrivateKey::<Bls12>(rng.gen());
let vk1 = PublicKey::from_private(&sk1, p_g, params); let vk1 = PublicKey::from_private(&sk1, p_g, params);
let msg1 = b"Foo bar"; let msg1 = b"Foo bar";
let sig1 = sk1.sign(msg1, rng, p_g, params); let sig1 = sk1.sign(msg1, rng, p_g, params);
assert!(vk1.verify(msg1, &sig1, p_g, params)); assert!(vk1.verify(msg1, &sig1, p_g, params));
let sk2 = PrivateKey::<Bls12>(Fs::random(rng)); let sk2 = PrivateKey::<Bls12>(rng.gen());
let vk2 = PublicKey::from_private(&sk2, p_g, params); let vk2 = PublicKey::from_private(&sk2, p_g, params);
let msg2 = b"Foo bar"; let msg2 = b"Foo bar";
let sig2 = sk2.sign(msg2, rng, p_g, params); let sig2 = sk2.sign(msg2, rng, p_g, params);
assert!(vk2.verify(msg2, &sig2, p_g, params)); assert!(vk2.verify(msg2, &sig2, p_g, params));
let mut batch = vec![ let mut batch = vec![
BatchEntry { BatchEntry { vk: vk1, msg: msg1, sig: sig1 },
vk: vk1, BatchEntry { vk: vk2, msg: msg2, sig: sig2 }
msg: msg1,
sig: sig1,
},
BatchEntry {
vk: vk2,
msg: msg2,
sig: sig2,
},
]; ];
assert!(batch_verify(rng, &batch, p_g, params)); assert!(batch_verify(rng, &batch, p_g, params));
@@ -260,10 +244,7 @@ mod tests {
#[test] #[test]
fn cofactor_check() { fn cofactor_check() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut thread_rng();
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let zero = edwards::Point::zero(); let zero = edwards::Point::zero();
let p_g = FixedGenerators::SpendingKeyGenerator; let p_g = FixedGenerators::SpendingKeyGenerator;
@@ -281,7 +262,7 @@ mod tests {
} }
}; };
let sk = PrivateKey::<Bls12>(Fs::random(rng)); let sk = PrivateKey::<Bls12>(rng.gen());
let vk = PublicKey::from_private(&sk, p_g, params); let vk = PublicKey::from_private(&sk, p_g, params);
// TODO: This test will need to change when #77 is fixed // TODO: This test will need to change when #77 is fixed
@@ -295,15 +276,12 @@ mod tests {
#[test] #[test]
fn round_trip_serialization() { fn round_trip_serialization() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut thread_rng();
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let p_g = FixedGenerators::SpendingKeyGenerator; let p_g = FixedGenerators::SpendingKeyGenerator;
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
for _ in 0..1000 { for _ in 0..1000 {
let sk = PrivateKey::<Bls12>(Fs::random(rng)); let sk = PrivateKey::<Bls12>(rng.gen());
let vk = PublicKey::from_private(&sk, p_g, params); let vk = PublicKey::from_private(&sk, p_g, params);
let msg = b"Foo bar"; let msg = b"Foo bar";
let sig = sk.sign(msg, rng, p_g, params); let sig = sk.sign(msg, rng, p_g, params);
@@ -331,15 +309,12 @@ mod tests {
#[test] #[test]
fn random_signatures() { fn random_signatures() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut thread_rng();
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let p_g = FixedGenerators::SpendingKeyGenerator; let p_g = FixedGenerators::SpendingKeyGenerator;
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
for _ in 0..1000 { for _ in 0..1000 {
let sk = PrivateKey::<Bls12>(Fs::random(rng)); let sk = PrivateKey::<Bls12>(rng.gen());
let vk = PublicKey::from_private(&sk, p_g, params); let vk = PublicKey::from_private(&sk, p_g, params);
let msg1 = b"Foo bar"; let msg1 = b"Foo bar";
@@ -353,7 +328,7 @@ mod tests {
assert!(!vk.verify(msg1, &sig2, p_g, params)); assert!(!vk.verify(msg1, &sig2, p_g, params));
assert!(!vk.verify(msg2, &sig1, p_g, params)); assert!(!vk.verify(msg2, &sig1, p_g, params));
let alpha = Fs::random(rng); let alpha = rng.gen();
let rsk = sk.randomize(alpha); let rsk = sk.randomize(alpha);
let rvk = vk.randomize(alpha, p_g, params); let rvk = vk.randomize(alpha, p_g, params);

View File

@@ -1,9 +1,9 @@
use blake2b_simd::Params; use blake2_rfc::blake2b::Blake2b;
use crate::jubjub::{JubjubEngine, ToUniform}; use jubjub::{JubjubEngine, ToUniform};
pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs { pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs {
let mut hasher = Params::new().hash_length(64).personal(persona).to_state(); let mut hasher = Blake2b::with_params(64, &[], &[], persona);
hasher.update(a); hasher.update(a);
hasher.update(b); hasher.update(b);
let ret = hasher.finalize(); let ret = hasher.finalize();

View File

@@ -1,2 +0,0 @@
# Protobufs
src/proto/

View File

@@ -1,25 +0,0 @@
[package]
name = "zcash_client_backend"
version = "0.0.0"
authors = [
"Jack Grigg <jack@z.cash>",
]
edition = "2018"
[dependencies]
bech32 = "0.7"
bs58 = { version = "0.2", features = ["check"] }
ff = { path = "../ff" }
hex = "0.3"
pairing = { path = "../pairing" }
protobuf = "2"
subtle = "2"
zcash_primitives = { path = "../zcash_primitives" }
[build-dependencies]
protobuf-codegen-pure = "2"
[dev-dependencies]
rand_core = "0.5"
rand_os = "0.2"
rand_xorshift = "0.2"

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2017-2019 Electric Coin Company
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,11 +0,0 @@
use protobuf_codegen_pure;
fn main() {
protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
out_dir: "src/proto",
input: &["proto/compact_formats.proto"],
includes: &["proto"],
customize: Default::default(),
})
.expect("protoc");
}

View File

@@ -1,48 +0,0 @@
syntax = "proto3";
package cash.z.wallet.sdk.rpc;
option go_package = "walletrpc";
// Remember that proto3 fields are all optional. A field that is not present will be set to its zero value.
// bytes fields of hashes are in canonical little-endian format.
// CompactBlock is a packaging of ONLY the data from a block that's needed to:
// 1. Detect a payment to your shielded Sapling address
// 2. Detect a spend of your shielded Sapling notes
// 3. Update your witnesses to generate new Sapling spend proofs.
message CompactBlock {
uint32 protoVersion = 1; // the version of this wire format, for storage
uint64 height = 2; // the height of this block
bytes hash = 3;
bytes prevHash = 4;
uint32 time = 5;
bytes header = 6; // (hash, prevHash, and time) OR (full header)
repeated CompactTx vtx = 7; // compact transactions from this block
}
message CompactTx {
// Index and hash will allow the receiver to call out to chain
// explorers or other data structures to retrieve more information
// about this transaction.
uint64 index = 1;
bytes hash = 2;
// The transaction fee: present if server can provide. In the case of a
// stateless server and a transaction with transparent inputs, this will be
// unset because the calculation requires reference to prior transactions.
// in a pure-Sapling context, the fee will be calculable as:
// valueBalance + (sum(vPubNew) - sum(vPubOld) - sum(tOut))
uint32 fee = 3;
repeated CompactSpend spends = 4;
repeated CompactOutput outputs = 5;
}
message CompactSpend {
bytes nf = 1;
}
message CompactOutput {
bytes cmu = 1;
bytes epk = 2;
bytes ciphertext = 3;
}

View File

@@ -1,9 +0,0 @@
//! Zcash global and per-network constants.
pub mod mainnet;
pub mod testnet;
pub mod regtest;
pub const SPROUT_CONSENSUS_BRANCH_ID: u32 = 0;
pub const OVERWINTER_CONSENSUS_BRANCH_ID: u32 = 0x5ba8_1b19;
pub const SAPLING_CONSENSUS_BRANCH_ID: u32 = 0x76b8_09bb;

View File

@@ -1,38 +0,0 @@
/// The mainnet coin type for ZEC, as defined by [SLIP 44].
///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
pub const COIN_TYPE: u32 = 141;
/// The HRP for a Bech32-encoded mainnet [`ExtendedSpendingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-main";
/// The HRP for a Bech32-encoded mainnet [`ExtendedFullViewingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews";
/// The HRP for a Bech32-encoded mainnet [`PaymentAddress`].
///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
///
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs";
/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::PublicKey`].
///
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 1] = [0x3c];
/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::Script`].
///
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 1] = [0x55];

View File

@@ -1,38 +0,0 @@
/// The testnet coin type for ZEC, as defined by [SLIP 44].
///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
pub const COIN_TYPE: u32 = 1;
/// The HRP for a Bech32-encoded testnet [`ExtendedSpendingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-regtest";
/// The HRP for a Bech32-encoded testnet [`ExtendedFullViewingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewregtestsapling";
/// The HRP for a Bech32-encoded testnet [`PaymentAddress`].
///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
///
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zregtestsapling";
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`].
///
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25];
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`].
///
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba];

View File

@@ -1,38 +0,0 @@
/// The testnet coin type for ZEC, as defined by [SLIP 44].
///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
pub const COIN_TYPE: u32 = 1;
/// The HRP for a Bech32-encoded testnet [`ExtendedSpendingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-test";
/// The HRP for a Bech32-encoded testnet [`ExtendedFullViewingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling";
/// The HRP for a Bech32-encoded testnet [`PaymentAddress`].
///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
///
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling";
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`].
///
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25];
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`].
///
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba];

View File

@@ -1,376 +0,0 @@
//! Encoding and decoding functions for Zcash key and address structs.
//!
//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`]
//! module.
use bech32::{self, Error, FromBase32, ToBase32};
use bs58::{self, decode::DecodeError};
use pairing::bls12_381::Bls12;
use std::io::{self, Write};
use zcash_primitives::{
jubjub::edwards,
primitives::{Diversifier, PaymentAddress},
};
use zcash_primitives::{
legacy::TransparentAddress,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
fn bech32_encode<F>(hrp: &str, write: F) -> String
where
F: Fn(&mut dyn Write) -> io::Result<()>,
{
let mut data: Vec<u8> = vec![];
write(&mut data).expect("Should be able to write to a Vec");
bech32::encode(hrp, data.to_base32()).expect("hrp is invalid")
}
fn bech32_decode<T, F>(hrp: &str, s: &str, read: F) -> Result<Option<T>, Error>
where
F: Fn(Vec<u8>) -> Option<T>,
{
let (decoded_hrp, data) = bech32::decode(s)?;
if decoded_hrp == hrp {
Vec::<u8>::from_base32(&data).map(|data| read(data))
} else {
Ok(None)
}
}
/// Writes an [`ExtendedSpendingKey`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_SPENDING_KEY},
/// encoding::encode_extended_spending_key,
/// keys::spending_key,
/// };
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// let encoded = encode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, &extsk);
/// ```
pub fn encode_extended_spending_key(hrp: &str, extsk: &ExtendedSpendingKey) -> String {
bech32_encode(hrp, |w| extsk.write(w))
}
/// Decodes an [`ExtendedSpendingKey`] from a Bech32-encoded string.
pub fn decode_extended_spending_key(
hrp: &str,
s: &str,
) -> Result<Option<ExtendedSpendingKey>, Error> {
bech32_decode(hrp, s, |data| ExtendedSpendingKey::read(&data[..]).ok())
}
/// Writes an [`ExtendedFullViewingKey`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY},
/// encoding::encode_extended_full_viewing_key,
/// keys::spending_key,
/// };
/// use zcash_primitives::zip32::ExtendedFullViewingKey;
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// let extfvk = ExtendedFullViewingKey::from(&extsk);
/// let encoded = encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk);
/// ```
pub fn encode_extended_full_viewing_key(hrp: &str, extfvk: &ExtendedFullViewingKey) -> String {
bech32_encode(hrp, |w| extfvk.write(w))
}
/// Decodes an [`ExtendedFullViewingKey`] from a Bech32-encoded string.
pub fn decode_extended_full_viewing_key(
hrp: &str,
s: &str,
) -> Result<Option<ExtendedFullViewingKey>, Error> {
bech32_decode(hrp, s, |data| ExtendedFullViewingKey::read(&data[..]).ok())
}
/// Writes a [`PaymentAddress`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::encode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress {
/// diversifier: Diversifier([0u8; 11]),
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// };
///
/// assert_eq!(
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// );
/// ```
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
bech32_encode(hrp, |w| {
w.write_all(&addr.diversifier.0)?;
addr.pk_d.write(w)
})
}
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::decode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress {
/// diversifier: Diversifier([0u8; 11]),
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// };
///
/// assert_eq!(
/// decode_payment_address(
/// HRP_SAPLING_PAYMENT_ADDRESS,
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// ),
/// Ok(Some(pa)),
/// );
/// ```
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
bech32_decode(hrp, s, |data| {
let mut diversifier = Diversifier([0; 11]);
diversifier.0.copy_from_slice(&data[0..11]);
// Check that the diversifier is valid
if diversifier.g_d::<Bls12>(&JUBJUB).is_none() {
return None;
}
edwards::Point::<Bls12, _>::read(&data[11..], &JUBJUB)
.ok()?
.as_prime_order(&JUBJUB)
.map(|pk_d| PaymentAddress { pk_d, diversifier })
})
}
/// Writes a [`TransparentAddress`] as a Base58Check-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX},
/// encoding::encode_transparent_address,
/// };
/// use zcash_primitives::legacy::TransparentAddress;
///
/// assert_eq!(
/// encode_transparent_address(
/// &B58_PUBKEY_ADDRESS_PREFIX,
/// &B58_SCRIPT_ADDRESS_PREFIX,
/// &TransparentAddress::PublicKey([0; 20]),
/// ),
/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma",
/// );
///
/// assert_eq!(
/// encode_transparent_address(
/// &B58_PUBKEY_ADDRESS_PREFIX,
/// &B58_SCRIPT_ADDRESS_PREFIX,
/// &TransparentAddress::Script([0; 20]),
/// ),
/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2",
/// );
/// ```
pub fn encode_transparent_address(
pubkey_version: &[u8],
script_version: &[u8],
addr: &TransparentAddress,
) -> String {
let decoded = match addr {
TransparentAddress::PublicKey(key_id) => {
let mut decoded = vec![0; pubkey_version.len() + 20];
decoded[..pubkey_version.len()].copy_from_slice(pubkey_version);
decoded[pubkey_version.len()..].copy_from_slice(key_id);
decoded
}
TransparentAddress::Script(script_id) => {
let mut decoded = vec![0; script_version.len() + 20];
decoded[..script_version.len()].copy_from_slice(script_version);
decoded[script_version.len()..].copy_from_slice(script_id);
decoded
}
};
bs58::encode(decoded).with_check().into_string()
}
/// Decodes a [`TransparentAddress`] from a Base58Check-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX},
/// encoding::decode_transparent_address,
/// };
/// use zcash_primitives::legacy::TransparentAddress;
///
/// assert_eq!(
/// decode_transparent_address(
/// &B58_PUBKEY_ADDRESS_PREFIX,
/// &B58_SCRIPT_ADDRESS_PREFIX,
/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma",
/// ),
/// Ok(Some(TransparentAddress::PublicKey([0; 20]))),
/// );
///
/// assert_eq!(
/// decode_transparent_address(
/// &B58_PUBKEY_ADDRESS_PREFIX,
/// &B58_SCRIPT_ADDRESS_PREFIX,
/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2",
/// ),
/// Ok(Some(TransparentAddress::Script([0; 20]))),
/// );
/// ```
pub fn decode_transparent_address(
pubkey_version: &[u8],
script_version: &[u8],
s: &str,
) -> Result<Option<TransparentAddress>, DecodeError> {
let decoded = bs58::decode(s).with_check(None).into_vec()?;
if &decoded[..pubkey_version.len()] == pubkey_version {
if decoded.len() == pubkey_version.len() + 20 {
let mut data = [0; 20];
data.copy_from_slice(&decoded[pubkey_version.len()..]);
Ok(Some(TransparentAddress::PublicKey(data)))
} else {
Ok(None)
}
} else if &decoded[..script_version.len()] == script_version {
if decoded.len() == script_version.len() + 20 {
let mut data = [0; 20];
data.copy_from_slice(&decoded[script_version.len()..]);
Ok(Some(TransparentAddress::Script(data)))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
#[cfg(test)]
mod tests {
use pairing::bls12_381::Bls12;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use zcash_primitives::JUBJUB;
use zcash_primitives::{
jubjub::edwards,
primitives::{Diversifier, PaymentAddress},
};
use super::{decode_payment_address, encode_payment_address};
use crate::constants;
#[test]
fn payment_address() {
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let addr = PaymentAddress {
diversifier: Diversifier([0u8; 11]),
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
};
let encoded_main =
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
let encoded_test =
"ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe";
assert_eq!(
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_main
);
assert_eq!(
decode_payment_address(
constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
encoded_main
)
.unwrap(),
Some(addr.clone())
);
assert_eq!(
encode_payment_address(constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_test
);
assert_eq!(
decode_payment_address(
constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
encoded_test
)
.unwrap(),
Some(addr)
);
}
#[test]
fn invalid_diversifier() {
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let addr = PaymentAddress {
diversifier: Diversifier([1u8; 11]),
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
};
let encoded_main =
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);
assert_eq!(
decode_payment_address(
constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
&encoded_main
)
.unwrap(),
None
);
}
}

View File

@@ -1,24 +0,0 @@
//! Helper functions for managing light client key material.
use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
/// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the
/// given seed.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{constants::testnet::COIN_TYPE, keys::spending_key};
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// ```
pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey {
ExtendedSpendingKey::from_path(
&ExtendedSpendingKey::master(&seed),
&[
ChildIndex::Hardened(32),
ChildIndex::Hardened(coin_type),
ChildIndex::Hardened(account),
],
)
}

View File

@@ -1,11 +0,0 @@
//! *A crate for implementing Zcash light clients.*
//!
//! `zcash_client_backend` contains Rust structs and traits for creating shielded Zcash
//! light clients.
pub mod constants;
pub mod encoding;
pub mod keys;
pub mod proto;
pub mod wallet;
pub mod welding_rig;

View File

@@ -1,83 +0,0 @@
//! Generated code for handling light client protobuf structs.
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use zcash_primitives::{
block::{BlockHash, BlockHeader},
jubjub::{edwards, PrimeOrder},
JUBJUB,
};
pub mod compact_formats;
impl compact_formats::CompactBlock {
/// Returns the [`BlockHash`] for this block.
///
/// # Panics
///
/// This function will panic if [`CompactBlock.header`] is not set and
/// [`CompactBlock.hash`] is not exactly 32 bytes.
///
/// [`CompactBlock.header`]: #structfield.header
/// [`CompactBlock.hash`]: #structfield.hash
pub fn hash(&self) -> BlockHash {
if let Some(header) = self.header() {
header.hash()
} else {
BlockHash::from_slice(&self.hash)
}
}
/// Returns the [`BlockHash`] for this block's parent.
///
/// # Panics
///
/// This function will panic if [`CompactBlock.header`] is not set and
/// [`CompactBlock.prevHash`] is not exactly 32 bytes.
///
/// [`CompactBlock.header`]: #structfield.header
/// [`CompactBlock.prevHash`]: #structfield.prevHash
pub fn prev_hash(&self) -> BlockHash {
if let Some(header) = self.header() {
header.prev_block
} else {
BlockHash::from_slice(&self.prevHash)
}
}
/// Returns the [`BlockHeader`] for this block if present.
///
/// A convenience method that parses [`CompactBlock.header`] if present.
///
/// [`CompactBlock.header`]: #structfield.header
pub fn header(&self) -> Option<BlockHeader> {
if self.header.is_empty() {
None
} else {
BlockHeader::read(&self.header[..]).ok()
}
}
}
impl compact_formats::CompactOutput {
/// Returns the note commitment for this output.
///
/// A convenience method that parses [`CompactOutput.cmu`].
///
/// [`CompactOutput.cmu`]: #structfield.cmu
pub fn cmu(&self) -> Result<Fr, ()> {
let mut repr = FrRepr::default();
repr.read_le(&self.cmu[..]).map_err(|_| ())?;
Fr::from_repr(repr).map_err(|_| ())
}
/// Returns the ephemeral public key for this output.
///
/// A convenience method that parses [`CompactOutput.epk`].
///
/// [`CompactOutput.epk`]: #structfield.epk
pub fn epk(&self) -> Result<edwards::Point<Bls12, PrimeOrder>, ()> {
let p = edwards::Point::<Bls12, _>::read(&self.epk[..], &JUBJUB).map_err(|_| ())?;
p.as_prime_order(&JUBJUB).ok_or(())
}
}

View File

@@ -1,46 +0,0 @@
//! Structs representing transaction data scanned from the block chain by a wallet or
//! light client.
use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::{
jubjub::{edwards, PrimeOrder},
merkle_tree::IncrementalWitness,
primitives::{Note, PaymentAddress},
sapling::Node,
transaction::TxId,
};
/// A subset of a [`Transaction`] relevant to wallets and light clients.
///
/// [`Transaction`]: zcash_primitives::transaction::Transaction
pub struct WalletTx {
pub txid: TxId,
pub index: usize,
pub num_spends: usize,
pub num_outputs: usize,
pub shielded_spends: Vec<WalletShieldedSpend>,
pub shielded_outputs: Vec<WalletShieldedOutput>,
}
/// A subset of a [`SpendDescription`] relevant to wallets and light clients.
///
/// [`SpendDescription`]: zcash_primitives::transaction::components::SpendDescription
pub struct WalletShieldedSpend {
pub index: usize,
pub nf: Vec<u8>,
pub account: usize,
}
/// A subset of an [`OutputDescription`] relevant to wallets and light clients.
///
/// [`OutputDescription`]: zcash_primitives::transaction::components::OutputDescription
pub struct WalletShieldedOutput {
pub index: usize,
pub cmu: Fr,
pub epk: edwards::Point<Bls12, PrimeOrder>,
pub account: usize,
pub note: Note<Bls12>,
pub to: PaymentAddress<Bls12>,
pub is_change: bool,
pub witness: IncrementalWitness<Node>,
}

View File

@@ -1,399 +0,0 @@
//! Tools for scanning a compact representation of the Zcash block chain.
use ff::PrimeField;
use std::collections::HashSet;
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_primitives::{
jubjub::fs::Fs,
merkle_tree::{CommitmentTree, IncrementalWitness},
note_encryption::try_sapling_compact_note_decryption,
sapling::Node,
transaction::TxId,
zip32::ExtendedFullViewingKey,
};
use crate::proto::compact_formats::{CompactBlock, CompactOutput};
use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx};
/// Scans a [`CompactOutput`] with a set of [`ExtendedFullViewingKey`]s.
///
/// Returns a [`WalletShieldedOutput`] and corresponding [`IncrementalWitness`] if this
/// output belongs to any of the given [`ExtendedFullViewingKey`]s.
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented
/// with this output's commitment.
fn scan_output(
(index, output): (usize, CompactOutput),
ivks: &[Fs],
spent_from_accounts: &HashSet<usize>,
tree: &mut CommitmentTree<Node>,
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
block_witnesses: &mut [&mut IncrementalWitness<Node>],
new_witnesses: &mut [&mut IncrementalWitness<Node>],
) -> Option<WalletShieldedOutput> {
let cmu = output.cmu().ok()?;
let epk = output.epk().ok()?;
let ct = output.ciphertext;
// Increment tree and witnesses
let node = Node::new(cmu.into_repr());
for witness in existing_witnesses {
witness.append(node).unwrap();
}
for witness in block_witnesses {
witness.append(node).unwrap();
}
for witness in new_witnesses {
witness.append(node).unwrap();
}
tree.append(node).unwrap();
for (account, ivk) in ivks.iter().enumerate() {
let (note, to) = match try_sapling_compact_note_decryption(ivk, &epk, &cmu, &ct) {
Some(ret) => ret,
None => continue,
};
// A note is marked as "change" if the account that received it
// also spent notes in the same transaction. This will catch,
// for instance:
// - Change created by spending fractions of notes.
// - Notes created by consolidation transactions.
// - Notes sent from one account to itself.
let is_change = spent_from_accounts.contains(&account);
return Some(WalletShieldedOutput {
index,
cmu,
epk,
account,
note,
to,
is_change,
witness: IncrementalWitness::from_tree(tree),
});
}
None
}
/// Scans a [`CompactBlock`] with a set of [`ExtendedFullViewingKey`]s.
///
/// Returns a vector of [`WalletTx`]s belonging to any of the given
/// [`ExtendedFullViewingKey`]s, and the corresponding new [`IncrementalWitness`]es.
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are
/// incremented appropriately.
pub fn scan_block(
block: CompactBlock,
extfvks: &[ExtendedFullViewingKey],
nullifiers: &[(&[u8], usize)],
tree: &mut CommitmentTree<Node>,
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
) -> Vec<WalletTx> {
let mut wtxs: Vec<WalletTx> = vec![];
let ivks: Vec<_> = extfvks.iter().map(|extfvk| extfvk.fvk.vk.ivk()).collect();
for tx in block.vtx.into_iter() {
let num_spends = tx.spends.len();
let num_outputs = tx.outputs.len();
// Check for spent notes
// The only step that is not constant-time is the filter() at the end.
let shielded_spends: Vec<_> = tx
.spends
.into_iter()
.enumerate()
.map(|(index, spend)| {
// Find the first tracked nullifier that matches this spend, and produce
// a WalletShieldedSpend if there is a match, in constant time.
nullifiers
.iter()
.map(|&(nf, account)| CtOption::new(account as u64, nf.ct_eq(&spend.nf[..])))
.fold(CtOption::new(0, 0.into()), |first, next| {
CtOption::conditional_select(&next, &first, first.is_some())
})
.map(|account| WalletShieldedSpend {
index,
nf: spend.nf,
account: account as usize,
})
})
.filter(|spend| spend.is_some().into())
.map(|spend| spend.unwrap())
.collect();
// Collect the set of accounts that were spent from in this transaction
let spent_from_accounts: HashSet<_> =
shielded_spends.iter().map(|spend| spend.account).collect();
// Check for incoming notes while incrementing tree and witnesses
let mut shielded_outputs: Vec<WalletShieldedOutput> = vec![];
{
// Grab mutable references to new witnesses from previous transactions
// in this block so that we can update them. Scoped so we don't hold
// mutable references to wtxs for too long.
let mut block_witnesses: Vec<_> = wtxs
.iter_mut()
.map(|tx| {
tx.shielded_outputs
.iter_mut()
.map(|output| &mut output.witness)
})
.flatten()
.collect();
for to_scan in tx.outputs.into_iter().enumerate() {
// Grab mutable references to new witnesses from previous outputs
// in this transaction so that we can update them. Scoped so we
// don't hold mutable references to shielded_outputs for too long.
let mut new_witnesses: Vec<_> = shielded_outputs
.iter_mut()
.map(|output| &mut output.witness)
.collect();
if let Some(output) = scan_output(
to_scan,
&ivks,
&spent_from_accounts,
tree,
existing_witnesses,
&mut block_witnesses,
&mut new_witnesses,
) {
shielded_outputs.push(output);
}
}
}
if !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
let mut txid = TxId([0u8; 32]);
txid.0.copy_from_slice(&tx.hash);
wtxs.push(WalletTx {
txid,
index: tx.index as usize,
num_spends,
num_outputs,
shielded_spends,
shielded_outputs,
});
}
}
wtxs
}
#[cfg(test)]
mod tests {
use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr};
use rand_core::RngCore;
use rand_os::OsRng;
use zcash_primitives::{
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
merkle_tree::CommitmentTree,
note_encryption::{Memo, SaplingNoteEncryption},
primitives::Note,
transaction::components::Amount,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
use super::scan_block;
use crate::proto::compact_formats::{CompactBlock, CompactOutput, CompactSpend, CompactTx};
fn random_compact_tx<R: RngCore>(rng: &mut R) -> CompactTx {
let fake_nf = {
let mut nf = vec![0; 32];
rng.fill_bytes(&mut nf);
nf
};
let fake_cmu = {
let fake_cmu = Fr::random(rng);
let mut bytes = vec![];
fake_cmu.into_repr().write_le(&mut bytes).unwrap();
bytes
};
let fake_epk = {
let mut buffer = vec![0; 64];
rng.fill_bytes(&mut buffer);
let fake_esk = Fs::to_uniform(&buffer[..]);
let fake_epk = JUBJUB
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(fake_esk, &JUBJUB);
let mut bytes = vec![];
fake_epk.write(&mut bytes).unwrap();
bytes
};
let mut cspend = CompactSpend::new();
cspend.set_nf(fake_nf);
let mut cout = CompactOutput::new();
cout.set_cmu(fake_cmu);
cout.set_epk(fake_epk);
cout.set_ciphertext(vec![0; 52]);
let mut ctx = CompactTx::new();
let mut txid = vec![0; 32];
rng.fill_bytes(&mut txid);
ctx.set_hash(txid);
ctx.spends.push(cspend);
ctx.outputs.push(cout);
ctx
}
/// Create a fake CompactBlock at the given height, with a transaction containing a
/// single spend of the given nullifier and a single output paying the given address.
/// Returns the CompactBlock.
fn fake_compact_block(
height: i32,
nf: [u8; 32],
extfvk: ExtendedFullViewingKey,
value: Amount,
tx_after: bool,
) -> CompactBlock {
let to = extfvk.default_address().unwrap().1;
// Create a fake Note for the account
let mut rng = OsRng;
let note = Note {
g_d: to.diversifier.g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d.clone(),
value: value.into(),
r: Fs::random(&mut rng),
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
note.clone(),
to.clone(),
Memo::default(),
&mut rng,
);
let mut cmu = vec![];
note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap();
let mut epk = vec![];
encryptor.epk().write(&mut epk).unwrap();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
// Create a fake CompactBlock containing the note
let mut cb = CompactBlock::new();
cb.set_height(height as u64);
// Add a random Sapling tx before ours
{
let mut tx = random_compact_tx(&mut rng);
tx.index = cb.vtx.len() as u64;
cb.vtx.push(tx);
}
let mut cspend = CompactSpend::new();
cspend.set_nf(nf.to_vec());
let mut cout = CompactOutput::new();
cout.set_cmu(cmu);
cout.set_epk(epk);
cout.set_ciphertext(enc_ciphertext[..52].to_vec());
let mut ctx = CompactTx::new();
let mut txid = vec![0; 32];
rng.fill_bytes(&mut txid);
ctx.set_hash(txid);
ctx.spends.push(cspend);
ctx.outputs.push(cout);
ctx.index = cb.vtx.len() as u64;
cb.vtx.push(ctx);
// Optionally add another random Sapling tx after ours
if tx_after {
let mut tx = random_compact_tx(&mut rng);
tx.index = cb.vtx.len() as u64;
cb.vtx.push(tx);
}
cb
}
#[test]
fn scan_block_with_my_tx() {
let extsk = ExtendedSpendingKey::master(&[]);
let extfvk = ExtendedFullViewingKey::from(&extsk);
let cb = fake_compact_block(
1,
[0; 32],
extfvk.clone(),
Amount::from_u64(5).unwrap(),
false,
);
assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
assert_eq!(tx.index, 1);
assert_eq!(tx.num_spends, 1);
assert_eq!(tx.num_outputs, 1);
assert_eq!(tx.shielded_spends.len(), 0);
assert_eq!(tx.shielded_outputs.len(), 1);
assert_eq!(tx.shielded_outputs[0].index, 0);
assert_eq!(tx.shielded_outputs[0].account, 0);
assert_eq!(tx.shielded_outputs[0].note.value, 5);
// Check that the witness root matches
assert_eq!(tx.shielded_outputs[0].witness.root(), tree.root());
}
#[test]
fn scan_block_with_txs_after_my_tx() {
let extsk = ExtendedSpendingKey::master(&[]);
let extfvk = ExtendedFullViewingKey::from(&extsk);
let cb = fake_compact_block(
1,
[0; 32],
extfvk.clone(),
Amount::from_u64(5).unwrap(),
true,
);
assert_eq!(cb.vtx.len(), 3);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
assert_eq!(tx.index, 1);
assert_eq!(tx.num_spends, 1);
assert_eq!(tx.num_outputs, 1);
assert_eq!(tx.shielded_spends.len(), 0);
assert_eq!(tx.shielded_outputs.len(), 1);
assert_eq!(tx.shielded_outputs[0].index, 0);
assert_eq!(tx.shielded_outputs[0].account, 0);
assert_eq!(tx.shielded_outputs[0].note.value, 5);
// Check that the witness root matches
assert_eq!(tx.shielded_outputs[0].witness.root(), tree.root());
}
#[test]
fn scan_block_with_my_spend() {
let extsk = ExtendedSpendingKey::master(&[]);
let extfvk = ExtendedFullViewingKey::from(&extsk);
let nf = [7; 32];
let account = 12;
let cb = fake_compact_block(1, nf, extfvk, Amount::from_u64(5).unwrap(), false);
assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[], &[(&nf, account)], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
assert_eq!(tx.index, 1);
assert_eq!(tx.num_spends, 1);
assert_eq!(tx.num_outputs, 1);
assert_eq!(tx.shielded_spends.len(), 1);
assert_eq!(tx.shielded_outputs.len(), 0);
assert_eq!(tx.shielded_spends[0].index, 0);
assert_eq!(tx.shielded_spends[0].nf, nf);
assert_eq!(tx.shielded_spends[0].account, account);
}
}

View File

@@ -1,27 +0,0 @@
[package]
name = "zcash_client_sqlite"
version = "0.0.0"
authors = [
"Jack Grigg <jack@z.cash>",
]
edition = "2018"
[dependencies]
bech32 = "0.7"
bs58 = { version = "0.2", features = ["check"] }
ff = { path = "../ff" }
pairing = { path = "../pairing" }
protobuf = "2"
rusqlite = { version = "0.20", features = ["bundled"] }
time = "0.1"
zcash_client_backend = { path = "../zcash_client_backend" }
zcash_primitives = { path = "../zcash_primitives" }
[dev-dependencies]
rand_core = "0.5"
rand_os = "0.2"
tempfile = "3"
zcash_proofs = { path = "../zcash_proofs" }
[features]
mainnet = []

View File

@@ -1,60 +0,0 @@
# Security Disclaimer
#### :warning: WARNING: This is an *early preview*
----
In the spirit of transparency, we provide this as a window into what we are actively
developing. This is an alpha build, not yet intended for 3rd party use. Please be advised
of the following:
* 🛑 This code currently is not audited. 🛑
* ❌ This is a public, active branch with **no support**.
* ❌ The code **does not have** documentation that is reviewed and approved by our Documentation team.
* ❌ The code **does not have** adequate unit tests, acceptance tests and stress tests.
* ❌ The code **does not have** automated tests that use the officially supported CI system.
* ❌ The code **has not been subjected to thorough review** by engineers at the Electric Coin Company.
* :warning: This library **is** compatible with the latest version of zcashd, but there **is no** automated testing of this.
* :heavy_check_mark: The library **is not** majorly broken in some way.
* :heavy_check_mark: The library **does run** on mainnet and testnet.
* ❌ We **are actively rebasing** this branch and adding features where/when needed.
* ❌ We **do not** undertake appropriate security coverage (threat models, review, response, etc.).
* :heavy_check_mark: There is a product manager for this library.
* :heavy_check_mark: Electric Coin Company maintains the library as we discover bugs and do network upgrades/minor releases.
* :heavy_check_mark: Users can expect to get a response within a few weeks after submitting an issue.
* ❌ The User Support team **has not yet been briefed** on the features provided to users and the functionality of the associated test-framework.
* ❌ The code is **not fully-documented**.
### 🛑 Use of this code may lead to a loss of funds 🛑
Use of this code in its current form or with modifications may lead to loss of funds, loss
of "expected" privacy, or denial of service for a large portion of users, or a bug which
could leverage any of those kinds of attacks (especially a "0 day" where we suspect few
people know about the vulnerability).
### :eyes: At this time, this is for preview purposes only. :eyes:
----
# zcash_client_sqlite
This library contains APIs that collectively implement a Zcash light client in
an SQLite database.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

Some files were not shown because too many files have changed in this diff Show More