Compare commits
22 Commits
@collinjac
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
e084396969 | ||
|
d9507aa2ac | ||
|
f7fe621554 | ||
|
c41425c55c | ||
|
b0698ec2d2 | ||
|
b0b1c6fd1e | ||
|
f6d168cd7c | ||
|
26f1652952 | ||
|
0bff6e31df | ||
|
7b3383123c | ||
|
9006b955d2 | ||
|
a48c121a3c | ||
|
1435971226 | ||
|
daec35491c | ||
|
4e7066d972 | ||
|
3af03e5889 | ||
|
b5aad54d42 | ||
|
a613ae9bfa | ||
|
4327e46bf3 | ||
|
8607fa91c6 | ||
|
a49a35c191 | ||
|
401d512ab5 |
64
.github/workflows/ci.yml
vendored
Normal file
64
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Lint CLI
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
clients/cli
|
||||
proto
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install protoc
|
||||
uses: arduino/setup-protoc@v3
|
||||
|
||||
- name: Set up Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: ./clients/cli
|
||||
|
||||
- name: Format
|
||||
working-directory: clients/cli
|
||||
run: |
|
||||
rustfmt src/**/*.rs --check --edition 2021
|
||||
|
||||
- name: Build
|
||||
working-directory: clients/cli
|
||||
run: |
|
||||
cargo build --profile=ci-build
|
||||
|
||||
- name: Run cargo clippy
|
||||
working-directory: clients/cli
|
||||
run: |
|
||||
cargo clippy --profile=ci-build --no-deps --all-targets --workspace -- -D warnings
|
||||
|
||||
- name: Test
|
||||
working-directory: clients/cli
|
||||
run: |
|
||||
cargo test --profile=ci-build --tests
|
||||
|
||||
- name: Ensure checked in generated files are up to date
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then \
|
||||
echo "There are uncommitted changes in working tree after building."; \
|
||||
git status; \
|
||||
git --no-pager diff; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Git working tree is clean"; \
|
||||
fi;
|
96
CONTRIBUTING.md
Normal file
96
CONTRIBUTING.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Contributing to the Nexus network
|
||||
|
||||
The Nexus network is contributor-friendly.
|
||||
We welcome all contributions, no matter your experience with Rust or cryptography.
|
||||
|
||||
This document will help you get started. But first, **thank you for your interest in contributing!** We immensely appreciate quality contributions. This guide is intended to help you navigate the process.
|
||||
|
||||
The [Discord][discord] is always available for any concerns you may have that are not covered in this guide, or for any other questions or discussions you want to raise with the Nexus team or broader Nexus community.
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
The Nexus network project adheres to the [Rust Code of Conduct][rust-coc]. This code of conduct describes the _minimum_ behavior
|
||||
expected from all contributors.
|
||||
|
||||
Instances of violations of the Code of Conduct can be reported by contacting the Nexus team.
|
||||
|
||||
### Ways to contribute
|
||||
|
||||
There are three main ways to contribute:
|
||||
|
||||
1. **By reporting an issue:** If you believe that you have uncovered a bug in the Nexus zkVM, report it by creating a new issue in the [Github][gh] issue tracker. See below for an extended discussion on how to make a bug report most helpful.
|
||||
2. **By adding information:** Even if collaborators are already aware of your issue, you can always provide additional context, such as further evidence in the form of reproduction steps, screenshots, code snippets, or logging outputs.
|
||||
3. **By resolving an issue:** Typically this is done in the form of either demonstrating that the issue reported is not a problem after all in a polite, thoughtfully explained, and evidence supported manner, or by opening a pull request that fixes the underlying problem and participating in its review and refinement.
|
||||
|
||||
**Anybody can participate in any stage of contribution**. We urge you to participate in all discussions around bugs, feature requests, existing code, and PRs.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
#### Asking for help
|
||||
|
||||
If you have reviewed this document and existing documentation and still have questions or are still having problems, but don't quite know enough to create a bug report, then
|
||||
you can get help by **starting a discussion**.
|
||||
|
||||
You can do so on the [Discord][discord].
|
||||
|
||||
#### Submitting a bug report
|
||||
|
||||
If you believe that you have uncovered a bug, please describe it to the best of your ability, and provide whatever context and evidence you have. Don't worry if you cannot provide every detail, just give us what you can. Contributors will ask follow-up questions if something is unclear.
|
||||
|
||||
As a starting point, in a bug report we will pretty much always want:
|
||||
|
||||
- the platform you are on, ideally both the operating system (Windows, macOS, or Linux) and the machine architecture (_e.g.,_ if you're using an M-series Mac) if you know them;
|
||||
- console logs from the CLI or web application showing errors and status messages;
|
||||
- concrete and comprehensive steps to reproduce the bug.
|
||||
|
||||
Code snippets should be as minimal as possible. It is always better if you can reproduce the bug with a small snippet that focuses on your Nexus zkVM usage rather than on the surrounding code in your project. This will help collaborators verify, reproduce, and zero in on a fix.
|
||||
|
||||
See [this guide][mcve] on how to create a minimal, complete, and verifiable example.
|
||||
|
||||
#### Submitting a feature request
|
||||
|
||||
Please include as detailed of an explanation as possible of the feature you would like, and add any additional context you think may be necessary or just helpful.
|
||||
|
||||
If you have examples of other tools that have the feature you are requesting, please include references/links to them as well.
|
||||
|
||||
## Resolving Issues
|
||||
|
||||
Pull requests are the way concrete changes are made to the code, documentation, and dependencies of the Nexus network.
|
||||
|
||||
Before making a large change, it is usually a good idea to first open an issue describing the change to solicit feedback and guidance.
|
||||
This will increase the likelihood of the PR getting merged. Striking up a discussion the [Discord][discord] to let the community know
|
||||
what you'll be working on can also be helpful for getting early feedback before diving in.
|
||||
|
||||
If you are working on a larger feature, we encourage you to open up a draft pull request and also check in with the [Discord][discord], to make sure that other
|
||||
contributors are not duplicating work.
|
||||
|
||||
#### Discussion
|
||||
|
||||
You will probably get feedback or requests for changes to your pull request.
|
||||
This is a regular and important part of the submission process, so don't be discouraged! Some reviewers may sign off on the pull
|
||||
request right away, others may have more detailed comments or feedback. This is a necessary part of the process in order
|
||||
to evaluate whether the changes are correct and necessary.
|
||||
|
||||
Remember to **always be aware of the person behind the code**. _How_ you communicate during reviews (of your code or others!) can have a significant impact on the success
|
||||
of the pull request. We never want the cost of a change that makes the Nexus network better to be a valued contributor not
|
||||
wanting to have anything to do with the project ever again. The goal is not just having good code. It's having a positive community that continues to turn good code into better code.
|
||||
|
||||
#### Abandoned or stale pull requests
|
||||
|
||||
If a pull request appears to be abandoned or stalled, it is polite to first check with the contributor to see if they
|
||||
intend to continue the work before checking if they would mind if you took it over (especially if it just has minor revisions
|
||||
remaining). When doing so, it is courteous to give the original contributor credit for the work they started, either by
|
||||
preserving their name and e-mail address in the commit log, or by using the `Author: ` or `Co-authored-by: ` metadata
|
||||
tag in the commits.
|
||||
|
||||
<sub><sup>_Adapted from the [Reth contributing guide][reth-contributing]_.</sub></sup>
|
||||
|
||||
[rust-coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md
|
||||
|
||||
[gh]: https://github.com/nexus-xyz/network-api
|
||||
|
||||
[discord]: https://discord.com/invite/nexus-xyz
|
||||
|
||||
[mcve]: https://stackoverflow.com/help/mcve
|
||||
|
||||
[reth-contributing]: https://github.com/paradigmxyz/reth/blob/main/CONTRIBUTING.md
|
176
LICENSE-APACHE
Normal file
176
LICENSE-APACHE
Normal file
@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
23
LICENSE-MIT
Normal file
23
LICENSE-MIT
Normal 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.
|
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# network-api
|
||||
|
||||
This repository contains the Nexus network command-line interface and
|
||||
the interface it uses to communicate with Nexus servers.
|
||||
|
||||
See the [CLI readme](./clients/cli/README.md) to get started.
|
7
clients/cli/.gitignore
vendored
Normal file
7
clients/cli/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
3277
clients/cli/Cargo.lock
generated
Normal file
3277
clients/cli/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
102
clients/cli/Cargo.toml
Normal file
102
clients/cli/Cargo.toml
Normal file
@ -0,0 +1,102 @@
|
||||
[package]
|
||||
name = "nexus-network"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "prover"
|
||||
path = "src/prover.rs"
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = "0.13"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
||||
[profile.ci-build]
|
||||
inherits = "dev"
|
||||
opt-level = 0
|
||||
debug = 0
|
||||
strip = "none"
|
||||
lto = false
|
||||
codegen-units = 256
|
||||
incremental = true
|
||||
|
||||
[dependencies]
|
||||
async-stream = "0.3"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
futures = "0.3"
|
||||
prost = "0.13"
|
||||
rand = "0.8.5"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
tokio = { version = "1.38", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.23", features = ["native-tls"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
|
||||
uuid = { version = "1.9", features = ["v4", "fast-rng"] }
|
||||
home = "0.5.9"
|
||||
random_word = { version = "0.4.3", features = ["en"] }
|
||||
nexus-core = { git = "https://github.com/nexus-xyz/nexus-zkvm.git" }
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
# Workaround for "failed to resolve patches for `https://github.com/rust-lang/crates.io-index`"
|
||||
zstd = { version = "=0.13.2", git = "https://github.com/gyscos/zstd-rs", features = ["wasm"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
elf = { version = "0.7", default-features = false, features = ["std"] }
|
||||
|
||||
jsonrpsee = { version = "0.23", default-features = false }
|
||||
|
||||
sha3 = { version = "0.10", default-features = false }
|
||||
hex = { version = "0.4.3" }
|
||||
|
||||
ark-crypto-primitives = { version = "0.4.0", features = [
|
||||
"r1cs",
|
||||
"sponge",
|
||||
"crh",
|
||||
"merkle_tree",
|
||||
] }
|
||||
ark-std = "0.4.0"
|
||||
|
||||
ark-relations = { version = "0.4.0" }
|
||||
ark-r1cs-std = { version = "0.4.0" }
|
||||
|
||||
ark-ff = "0.4.0"
|
||||
ark-ec = { version = "0.4.0", default-features = false }
|
||||
ark-serialize = { version = "0.4.0", features = ["derive"] }
|
||||
ark-poly = "0.4.0"
|
||||
ark-poly-commit = "0.4.0"
|
||||
|
||||
ark-bn254 = "0.4.0"
|
||||
ark-grumpkin = "0.4.0"
|
||||
ark-pallas = "0.4.0"
|
||||
ark-vesta = "0.4.0"
|
||||
ark-test-curves = { version = "0.4.2", features = ["bls12_381_curve"] }
|
||||
iana-time-zone = "0.1.60"
|
||||
chrono = "0.4.38"
|
||||
|
||||
[patch.crates-io]
|
||||
ark-crypto-primitives = { git = "https://github.com/arkworks-rs/crypto-primitives", rev = "d27a5c8" }
|
||||
|
||||
ark-r1cs-std = { git = "https://github.com/arkworks-rs/r1cs-std/", rev = "2ca3bd7" }
|
||||
|
||||
ark-ff = { git = "https://github.com/arkworks-rs/algebra/", rev = "2a80c54" }
|
||||
ark-ec = { git = "https://github.com/arkworks-rs/algebra/", rev = "2a80c54" }
|
||||
ark-serialize = { git = "https://github.com/arkworks-rs/algebra/", rev = "2a80c54" }
|
||||
ark-poly = { git = "https://github.com/arkworks-rs/algebra/", rev = "2a80c54" }
|
||||
ark-test-curves = { git = "https://github.com/arkworks-rs/algebra/", rev = "2a80c54" }
|
||||
|
||||
ark-poly-commit = { git = "https://github.com/arkworks-rs/poly-commit/", rev = "12f5529" }
|
||||
|
||||
# note bls is using a different commit from the other curves
|
||||
ark-bn254 = { git = "https://github.com/arkworks-rs/curves/", rev = "8c0256a" }
|
||||
ark-grumpkin = { git = "https://github.com/arkworks-rs/curves/", rev = "8c0256a" }
|
||||
ark-pallas = { git = "https://github.com/arkworks-rs/curves/", rev = "8c0256a" }
|
||||
ark-vesta = { git = "https://github.com/arkworks-rs/curves/", rev = "8c0256a" }
|
||||
ark-bls12-381 = { git = "https://github.com/arkworks-rs/curves/", rev = "3fded1f" }
|
||||
|
||||
zstd-sys = { git = "https://github.com/gyscos/zstd-rs" }
|
59
clients/cli/README.md
Normal file
59
clients/cli/README.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Network CLI
|
||||
|
||||
The command line interface (CLI) lets you run a prover node and contribute proofs to the Nexus network.
|
||||
It is the highest-performance option for proving.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
If you don't have these dependencies already, install them first.
|
||||
|
||||
### Linux
|
||||
|
||||
```
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
sudo apt install build-essential pkg-config libssl-dev git-all
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
If you have [installed Homebrew](https://brew.sh/) to manage packages on OS X,
|
||||
run this command to install Git.
|
||||
|
||||
```
|
||||
brew install git
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
[Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install),
|
||||
then see Linux instructions above.
|
||||
|
||||
## Quick start
|
||||
|
||||
```
|
||||
curl https://cli.nexus.xyz/ | sh
|
||||
```
|
||||
|
||||
If you do not already have Rust, you will be prompted to install it.
|
||||
|
||||
## Terms of Use
|
||||
|
||||
Use of the CLI is subject to the [Terms of Use](https://nexus.xyz/terms-of-use).
|
||||
The first time you run it, it prompts you to accept the terms. To accept the terms
|
||||
noninteractively (for example, in a continuous integration environment),
|
||||
add `NONINTERACTIVE=1` before `sh`.
|
||||
|
||||
## Known issues
|
||||
|
||||
* Only the latest version of the CLI is currently supported.
|
||||
* Prebuilt binaries are not yet available.
|
||||
* Linking email to prover id is currently available on the web version only.
|
||||
* Counting cycles proved is not yet available in the CLI.
|
||||
* Only proving is supported. Submitting programs to the network is in private beta.
|
||||
To request an API key, contact us at growth@nexus.xyz.
|
||||
|
||||
## Resources
|
||||
|
||||
* [Network FAQ](https://nexus.xyz/network#network-faqs)
|
||||
* [Discord server](https://discord.gg/nexus-xyz)
|
19
clients/cli/build.rs
Normal file
19
clients/cli/build.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use std::{error::Error, path::PathBuf, process::Command};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let out_dir: PathBuf = "./src/generated/".into();
|
||||
let proto_file: PathBuf = "../../proto/orchestrator.proto".into();
|
||||
let proto_dir = proto_file.parent().unwrap();
|
||||
|
||||
match Command::new("protoc --version").spawn() {
|
||||
Ok(_) => prost_build::Config::new()
|
||||
.out_dir(out_dir)
|
||||
.protoc_arg("--experimental_allow_proto3_optional")
|
||||
.compile_protos(&[&proto_file], &[proto_dir])?,
|
||||
Err(_) => {
|
||||
// Skipping protobuf compilation.
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
82
clients/cli/src/analytics.rs
Normal file
82
clients/cli/src/analytics.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use crate::config::{analytics_id, analytics_api_key};
|
||||
use chrono::Datelike;
|
||||
use chrono::Timelike;
|
||||
use reqwest::header::{ACCEPT, CONTENT_TYPE};
|
||||
use serde_json::{json, Value};
|
||||
use std::{
|
||||
env,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
pub fn track(
|
||||
event_name: String,
|
||||
description: String,
|
||||
ws_addr_string: &str,
|
||||
event_properties: Value,
|
||||
) {
|
||||
println!("{}", description);
|
||||
|
||||
let firebase_app_id = analytics_id(ws_addr_string);
|
||||
let firebase_api_key = analytics_api_key(ws_addr_string);
|
||||
if firebase_app_id.is_empty() {
|
||||
return;
|
||||
}
|
||||
let local_now = chrono::offset::Local::now();
|
||||
|
||||
// For tracking events, we use the Firebase Measurement Protocol
|
||||
// Firebase is mostly designed for mobile and web apps, but for our use case of a CLI,
|
||||
// we can use the Measurement Protocol to track events by POST to a URL.
|
||||
// The only thing that may be unexpected is that the URL we use includes a firebase key
|
||||
|
||||
// Firebase format for properties for Measurement protocol:
|
||||
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=firebase#payload
|
||||
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=firebase#payload_query_parameters
|
||||
let mut properties = json!({
|
||||
"time": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(),
|
||||
// app_instance_id is the standard key Firebase uses this key to track the same user across sessions
|
||||
// It is a bit redundant, but I wanted to keep the recommended format Firebase uses to minimize surprises
|
||||
// I still left the distinct_id key as well for backwards compatibility
|
||||
"app_instance_id": event_properties["prover_id"],
|
||||
"distinct_id": event_properties["prover_id"],
|
||||
"prover_type": "volunteer",
|
||||
"client_type": "cli",
|
||||
"operating_system": env::consts::OS,
|
||||
"time_zone": iana_time_zone::get_timezone().unwrap(),
|
||||
"local_hour": local_now.hour(),
|
||||
"local_weekday_number_from_monday": local_now.weekday().number_from_monday(),
|
||||
"ws_addr_string": ws_addr_string,
|
||||
});
|
||||
for (k, v) in event_properties.as_object().unwrap() {
|
||||
properties[k] = v.clone();
|
||||
}
|
||||
|
||||
// Firebase format for events
|
||||
let body = json!({
|
||||
"app_instance_id": event_properties["prover_id"],
|
||||
"events": [{
|
||||
"name": event_name,
|
||||
"params": properties
|
||||
}],
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let _ = client
|
||||
// URL is the Google Analytics endpoint for Firebase: https://stackoverflow.com/questions/50355752/firebase-analytics-from-remote-rest-api
|
||||
.post(format!(
|
||||
"https://www.google-analytics.com/mp/collect?firebase_app_id={}&api_secret={}",
|
||||
firebase_app_id,
|
||||
firebase_api_key
|
||||
))
|
||||
.body(format!("[{}]", body))
|
||||
.header(ACCEPT, "text/plain")
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
}
|
78
clients/cli/src/config.rs
Normal file
78
clients/cli/src/config.rs
Normal file
@ -0,0 +1,78 @@
|
||||
// Debug version of analytics_id
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn analytics_id(_ws_addr_string: &str) -> String {
|
||||
// Use one of the tokens in the release version if debugging analytics
|
||||
"".into()
|
||||
}
|
||||
|
||||
// Debug version of analytics_api_key
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn analytics_api_key(_ws_addr_string: &str) -> String {
|
||||
// Use one of the tokens in the release version if debugging analytics
|
||||
"".into()
|
||||
}
|
||||
|
||||
// The following enum is used to determine the environment from the web socket string
|
||||
#[derive(Debug)]
|
||||
#[cfg(not(debug_assertions))]
|
||||
enum Environment {
|
||||
Dev,
|
||||
Staging,
|
||||
Beta,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
// The web socket addresses for the different environments
|
||||
#[cfg(not(debug_assertions))]
|
||||
mod web_socket_urls {
|
||||
pub const DEV: &str = "wss://dev.orchestrator.nexus.xyz:443/";
|
||||
pub const STAGING: &str = "wss://staging.orchestrator.nexus.xyz:443/";
|
||||
pub const BETA: &str = "wss://beta.orchestrator.nexus.xyz:443/";
|
||||
}
|
||||
|
||||
// the firebase APP IDS by environment
|
||||
#[cfg(not(debug_assertions))]
|
||||
mod firebase {
|
||||
pub const DEV_APP_ID: &str = "1:954530464230:web:f0a14de14ef7bcdaa99627";
|
||||
pub const STAGING_APP_ID: &str = "1:222794630996:web:1758d64a85eba687eaaac1";
|
||||
pub const BETA_APP_ID: &str = "1:279395003658:web:04ee2c524474d683d75ef3";
|
||||
|
||||
// Analytics keys for the different environments
|
||||
// These are keys that allow the measurement protocol to write to the analytics database
|
||||
// They are not sensitive. Worst case, if a malicious actor obtains the secret, they could potentially send false or misleading data to your GA4 property
|
||||
pub const DEV_API_SECRET: &str = "8ySxiKrtT8a76zClqqO8IQ";
|
||||
pub const STAGING_API_SECRET: &str = "OI7H53soRMSDWfJf1ittHQ";
|
||||
pub const BETA_API_SECRET: &str = "gxxzKAQLSl-uYI0eKbIi_Q";
|
||||
}
|
||||
|
||||
// Release versions (existing code)
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub fn analytics_id(ws_addr_string: &str) -> String {
|
||||
|
||||
// Determine the environment from the web socket string (ws_addr_string)
|
||||
let env = match ws_addr_string {
|
||||
web_socket_urls::DEV => Environment::Dev,
|
||||
web_socket_urls::STAGING => Environment::Staging,
|
||||
web_socket_urls::BETA => Environment::Beta,
|
||||
_ => Environment::Unknown,
|
||||
};
|
||||
|
||||
// Return the appropriate Firebase App ID based on the environment
|
||||
match env {
|
||||
Environment::Dev => firebase::DEV_APP_ID.to_string(),
|
||||
Environment::Staging => firebase::STAGING_APP_ID.to_string(),
|
||||
Environment::Beta => firebase::BETA_APP_ID.to_string(),
|
||||
Environment::Unknown => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub fn analytics_api_key(ws_addr_string: &str) -> String {
|
||||
match ws_addr_string {
|
||||
web_socket_urls::DEV => firebase::DEV_API_SECRET.to_string(),
|
||||
web_socket_urls::STAGING => firebase::STAGING_API_SECRET.to_string(),
|
||||
web_socket_urls::BETA => firebase::BETA_API_SECRET.to_string(),
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
4
clients/cli/src/generated/mod.rs
Normal file
4
clients/cli/src/generated/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
pub mod pb {
|
||||
include!("nexus.orchestrator.rs");
|
||||
}
|
213
clients/cli/src/generated/nexus.orchestrator.rs
Normal file
213
clients/cli/src/generated/nexus.orchestrator.rs
Normal file
@ -0,0 +1,213 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProofRequest {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub program: ::core::option::Option<CompiledProgram>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub input: ::core::option::Option<VmProgramInput>,
|
||||
/// Step of the trace to start the proof, inclusive.
|
||||
///
|
||||
/// If missing, proving starts at the beginning of the trace.
|
||||
#[prost(int32, optional, tag = "3")]
|
||||
pub step_to_start: ::core::option::Option<i32>,
|
||||
/// Number of steps for this proof request.
|
||||
///
|
||||
/// If zero, proving is skipped. If missing, all steps are proved.
|
||||
#[prost(int32, optional, tag = "4")]
|
||||
pub steps_to_prove: ::core::option::Option<i32>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProofResponse {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub proof: ::core::option::Option<Proof>,
|
||||
}
|
||||
/// A message that always represents a program runnable on the Nexus VM.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CompiledProgram {
|
||||
#[prost(oneof = "compiled_program::Program", tags = "1")]
|
||||
pub program: ::core::option::Option<compiled_program::Program>,
|
||||
}
|
||||
/// Nested message and enum types in `CompiledProgram`.
|
||||
pub mod compiled_program {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Program {
|
||||
/// ELF binary containing a program to be proved, expressed in the RV32I ISA.
|
||||
#[prost(bytes, tag = "1")]
|
||||
Rv32iElfBytes(::prost::alloc::vec::Vec<u8>),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct VmProgramInput {
|
||||
#[prost(oneof = "vm_program_input::Input", tags = "1")]
|
||||
pub input: ::core::option::Option<vm_program_input::Input>,
|
||||
}
|
||||
/// Nested message and enum types in `VMProgramInput`.
|
||||
pub mod vm_program_input {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Input {
|
||||
/// Input expressed as raw bytes to be read as-is off of the input tape.
|
||||
#[prost(bytes, tag = "1")]
|
||||
RawBytes(::prost::alloc::vec::Vec<u8>),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Proof {
|
||||
#[prost(oneof = "proof::Proof", tags = "1")]
|
||||
pub proof: ::core::option::Option<proof::Proof>,
|
||||
}
|
||||
/// Nested message and enum types in `Proof`.
|
||||
pub mod proof {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Proof {
|
||||
#[prost(bytes, tag = "1")]
|
||||
NovaBytes(::prost::alloc::vec::Vec<u8>),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProgramSource {
|
||||
/// The source code to be compiled. There will be a variety of languages and
|
||||
/// ways to express everything a program needs for compilation (dependencies,
|
||||
/// multiple files, etc.) as our scope expands.
|
||||
#[prost(oneof = "program_source::Source", tags = "1")]
|
||||
pub source: ::core::option::Option<program_source::Source>,
|
||||
}
|
||||
/// Nested message and enum types in `ProgramSource`.
|
||||
pub mod program_source {
|
||||
/// The source code to be compiled. There will be a variety of languages and
|
||||
/// ways to express everything a program needs for compilation (dependencies,
|
||||
/// multiple files, etc.) as our scope expands.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Source {
|
||||
/// Option to use when the program in question can be expressed as a single
|
||||
/// rust file (i.e., a program written in the playground).
|
||||
#[prost(string, tag = "1")]
|
||||
RustSingleFile(::prost::alloc::string::String),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CompileRequest {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub source: ::core::option::Option<ProgramSource>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CompileResponse {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub program: ::core::option::Option<CompiledProgram>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct Progress {
|
||||
/// Completion status expressed as a number between zero and one,
|
||||
/// inclusive.
|
||||
#[prost(float, tag = "1")]
|
||||
pub completed_fraction: f32,
|
||||
/// The total size of the execution trace in steps.
|
||||
#[prost(int32, tag = "2")]
|
||||
pub steps_in_trace: i32,
|
||||
/// The number of steps of the execution trace to be proven.
|
||||
#[prost(int32, tag = "3")]
|
||||
pub steps_to_prove: i32,
|
||||
/// The number of steps proven so far.
|
||||
#[prost(int32, tag = "4")]
|
||||
pub steps_proven: i32,
|
||||
}
|
||||
/// Streamed messages sent to the orchestrator to keep it updated with the
|
||||
/// prover's status.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProverRequest {
|
||||
#[prost(oneof = "prover_request::Contents", tags = "1, 2, 3, 4")]
|
||||
pub contents: ::core::option::Option<prover_request::Contents>,
|
||||
}
|
||||
/// Nested message and enum types in `ProverRequest`.
|
||||
pub mod prover_request {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Contents {
|
||||
/// Details about this supply node for use by the orchestrator.
|
||||
#[prost(message, tag = "1")]
|
||||
Registration(super::ProverRequestRegistration),
|
||||
/// A completed proof.
|
||||
#[prost(message, tag = "2")]
|
||||
Proof(super::Proof),
|
||||
/// Periodic progress update for the current proof.
|
||||
#[prost(message, tag = "3")]
|
||||
Progress(super::Progress),
|
||||
/// Periodic liveness indicator when no proof is being computed.
|
||||
#[prost(message, tag = "4")]
|
||||
Heartbeat(super::Heartbeat),
|
||||
}
|
||||
}
|
||||
/// Metadata that helps the orchestrator schedule work to the requesting compute
|
||||
/// supplier.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProverRequestRegistration {
|
||||
/// What type of prover this is.
|
||||
#[prost(enumeration = "ProverType", tag = "1")]
|
||||
pub prover_type: i32,
|
||||
/// A unique identifier for this prover, generated by the prover.
|
||||
///
|
||||
/// Distict provers must not share an identifier; do not use a constant value.
|
||||
#[prost(string, tag = "2")]
|
||||
pub prover_id: ::prost::alloc::string::String,
|
||||
/// The number of proof cycles that this prover expects to compute
|
||||
/// over the course of one second. Proof cycles are proof steps times k.
|
||||
#[prost(double, optional, tag = "3")]
|
||||
pub estimated_proof_cycles_hertz: ::core::option::Option<f64>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProverResponse {
|
||||
/// Forward the literal request for now
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub to_prove: ::core::option::Option<ProofRequest>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct Heartbeat {}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum ProverType {
|
||||
/// Experimental new prover types should leave the prover type unspecified.
|
||||
Unspecified = 0,
|
||||
/// The default prover type, used for volunteered compute resources.
|
||||
Volunteer = 1,
|
||||
/// Provers running on public continuous integration.
|
||||
/// May restrict the types of programs that can be assigned.
|
||||
Ci = 2,
|
||||
}
|
||||
impl ProverType {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
ProverType::Unspecified => "PROVER_TYPE_UNSPECIFIED",
|
||||
ProverType::Volunteer => "PROVER_TYPE_VOLUNTEER",
|
||||
ProverType::Ci => "PROVER_TYPE_CI",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"PROVER_TYPE_UNSPECIFIED" => Some(Self::Unspecified),
|
||||
"PROVER_TYPE_VOLUNTEER" => Some(Self::Volunteer),
|
||||
"PROVER_TYPE_CI" => Some(Self::Ci),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
361
clients/cli/src/prover.rs
Normal file
361
clients/cli/src/prover.rs
Normal file
@ -0,0 +1,361 @@
|
||||
// Copyright (c) 2024 Nexus. All rights reserved.
|
||||
|
||||
mod analytics;
|
||||
mod config;
|
||||
mod generated;
|
||||
|
||||
use crate::analytics::track;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use clap::Parser;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use generated::pb::{
|
||||
self, compiled_program::Program, proof, prover_request, vm_program_input::Input, Progress,
|
||||
ProverRequest, ProverRequestRegistration, ProverResponse, ProverType,
|
||||
};
|
||||
use prost::Message as _;
|
||||
use random_word::Lang;
|
||||
use serde_json::json;
|
||||
use std::{fs, path::Path, time::SystemTime};
|
||||
use tokio_tungstenite::tungstenite::protocol::{frame::coding::CloseCode, CloseFrame, Message};
|
||||
use tracing_subscriber::fmt::format::FmtSpan;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use nexus_core::{
|
||||
nvm::{
|
||||
interactive::{parse_elf, trace},
|
||||
memory::MerkleTrie,
|
||||
NexusVM,
|
||||
},
|
||||
prover::nova::{
|
||||
init_circuit_trace, key::CanonicalSerialize, pp::gen_vm_pp, prove_seq_step, types::*,
|
||||
},
|
||||
};
|
||||
use rand::RngCore;
|
||||
use zstd::stream::Encoder;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
/// Hostname at which Orchestrator can be reached
|
||||
hostname: String,
|
||||
|
||||
/// Port over which to communicate with Orchestrator
|
||||
#[arg(short, long, default_value_t = 443u16)]
|
||||
port: u16,
|
||||
|
||||
/// Whether to hang up after the first proof
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
just_once: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Configure the tracing subscriber
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(EnvFilter::from_default_env())
|
||||
.with_span_events(FmtSpan::CLOSE)
|
||||
.init();
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
let ws_addr_string = format!(
|
||||
"{}://{}:{}/prove",
|
||||
if args.port == 443 { "wss" } else { "ws" },
|
||||
args.hostname,
|
||||
args.port
|
||||
);
|
||||
|
||||
let k = 4;
|
||||
// TODO(collinjackson): Get parameters from a file or URL.
|
||||
let pp = gen_vm_pp::<C1, seq::SetupParams<(G1, G2, C1, C2, RO, SC)>>(k as usize, &())
|
||||
.expect("error generating public parameters");
|
||||
|
||||
// If the prover_id file is found, use the contents, otherwise generate a new random id
|
||||
// and store it.
|
||||
let mut prover_id = format!(
|
||||
"{}-{}-{}",
|
||||
random_word::gen(Lang::En),
|
||||
random_word::gen(Lang::En),
|
||||
rand::thread_rng().next_u32() % 100,
|
||||
);
|
||||
match home::home_dir() {
|
||||
Some(path) if !path.as_os_str().is_empty() => {
|
||||
let nexus_dir = Path::new(&path).join(".nexus");
|
||||
prover_id = match fs::read(nexus_dir.join("prover-id")) {
|
||||
Ok(buf) => String::from_utf8(buf).unwrap(),
|
||||
Err(_) => {
|
||||
let _ = fs::create_dir(nexus_dir.clone());
|
||||
fs::write(nexus_dir.join("prover-id"), prover_id.clone()).unwrap();
|
||||
prover_id
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Unable to get home dir.");
|
||||
}
|
||||
};
|
||||
|
||||
track(
|
||||
"connect".into(),
|
||||
format!("Connecting to {}...", &ws_addr_string),
|
||||
&ws_addr_string,
|
||||
json!({"prover_id": prover_id}),
|
||||
);
|
||||
|
||||
let (mut client, _) = tokio_tungstenite::connect_async(&ws_addr_string)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
track(
|
||||
"connected".into(),
|
||||
"Connected.".into(),
|
||||
&ws_addr_string,
|
||||
json!({"prover_id": prover_id}),
|
||||
);
|
||||
|
||||
let registration = ProverRequest {
|
||||
contents: Some(prover_request::Contents::Registration(
|
||||
ProverRequestRegistration {
|
||||
prover_type: ProverType::Volunteer.into(),
|
||||
prover_id: prover_id.clone(),
|
||||
estimated_proof_cycles_hertz: None,
|
||||
},
|
||||
)),
|
||||
};
|
||||
|
||||
let mut retries = 0;
|
||||
let max_retries = 5;
|
||||
|
||||
while let Err(e) = client
|
||||
.send(Message::Binary(registration.encode_to_vec()))
|
||||
.await
|
||||
{
|
||||
eprintln!(
|
||||
"Failed to send message: {:?}, attempt {}/{}",
|
||||
e,
|
||||
retries + 1,
|
||||
max_retries
|
||||
);
|
||||
|
||||
retries += 1;
|
||||
if retries >= max_retries {
|
||||
eprintln!("Max retries reached, exiting...");
|
||||
break;
|
||||
}
|
||||
|
||||
// Add a delay before retrying
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(u64::pow(2, retries))).await;
|
||||
}
|
||||
|
||||
track(
|
||||
"register".into(),
|
||||
format!("Your assigned prover identifier is {}.", prover_id),
|
||||
&ws_addr_string,
|
||||
json!({"ws_addr_string": ws_addr_string, "prover_id": prover_id}),
|
||||
);
|
||||
loop {
|
||||
let program_message = match client.next().await.unwrap().unwrap() {
|
||||
Message::Binary(b) => b,
|
||||
_ => panic!("Unexpected message type"),
|
||||
};
|
||||
let program = ProverResponse::decode(program_message.as_slice()).unwrap();
|
||||
|
||||
let Program::Rv32iElfBytes(elf_bytes) = program
|
||||
.to_prove
|
||||
.clone()
|
||||
.unwrap()
|
||||
.program
|
||||
.unwrap()
|
||||
.program
|
||||
.unwrap();
|
||||
let to_prove = program.to_prove.unwrap();
|
||||
let Input::RawBytes(input) = to_prove.input.unwrap().input.unwrap();
|
||||
|
||||
track(
|
||||
"program".into(),
|
||||
format!(
|
||||
"Received a {} byte program to prove with {} bytes of input",
|
||||
elf_bytes.len(),
|
||||
input.len()
|
||||
),
|
||||
&ws_addr_string,
|
||||
json!({"prover_id": prover_id}),
|
||||
);
|
||||
|
||||
let mut vm: NexusVM<MerkleTrie> =
|
||||
parse_elf(elf_bytes.as_ref()).expect("error loading and parsing RISC-V instruction");
|
||||
vm.syscalls.set_input(&input);
|
||||
|
||||
// TODO(collinjackson): Get outputs
|
||||
let completed_trace = trace(&mut vm, k as usize, false).expect("error generating trace");
|
||||
let tr = init_circuit_trace(completed_trace).expect("error initializing circuit trace");
|
||||
|
||||
let total_steps = tr.steps();
|
||||
let start: usize = match to_prove.step_to_start {
|
||||
Some(step) => step as usize,
|
||||
None => 0,
|
||||
};
|
||||
let steps_to_prove = match to_prove.steps_to_prove {
|
||||
Some(steps) => steps as usize,
|
||||
None => total_steps,
|
||||
};
|
||||
let mut end: usize = start + steps_to_prove;
|
||||
if end > total_steps {
|
||||
end = total_steps
|
||||
}
|
||||
|
||||
let initial_progress = ProverRequest {
|
||||
contents: Some(prover_request::Contents::Progress(Progress {
|
||||
completed_fraction: 0.0,
|
||||
steps_in_trace: total_steps as i32,
|
||||
steps_to_prove: (end - start) as i32,
|
||||
steps_proven: 0,
|
||||
})),
|
||||
};
|
||||
client
|
||||
.send(Message::Binary(initial_progress.encode_to_vec()))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let z_st = tr.input(start).expect("error starting circuit trace");
|
||||
let mut proof = IVCProof::new(&z_st);
|
||||
|
||||
let mut completed_fraction = 0.0;
|
||||
let mut steps_proven = 0;
|
||||
track(
|
||||
"progress".into(),
|
||||
format!(
|
||||
"Program trace is {} steps. Proving {} steps starting at {}...",
|
||||
total_steps, steps_to_prove, start
|
||||
),
|
||||
&ws_addr_string,
|
||||
json!({
|
||||
"completed_fraction": completed_fraction,
|
||||
"steps_in_trace": total_steps,
|
||||
"steps_to_prove": steps_to_prove,
|
||||
"steps_proven": steps_proven,
|
||||
"cycles_proven": steps_proven * k,
|
||||
"k": k,
|
||||
"prover_id": prover_id,
|
||||
}),
|
||||
);
|
||||
let start_time = SystemTime::now();
|
||||
let mut progress_time = start_time;
|
||||
for step in start..end {
|
||||
proof = prove_seq_step(Some(proof), &pp, &tr).expect("error proving step");
|
||||
steps_proven += 1;
|
||||
completed_fraction = steps_proven as f32 / steps_to_prove as f32;
|
||||
let progress = ProverRequest {
|
||||
contents: Some(prover_request::Contents::Progress(Progress {
|
||||
completed_fraction,
|
||||
steps_in_trace: total_steps as i32,
|
||||
steps_to_prove: steps_to_prove as i32,
|
||||
steps_proven,
|
||||
})),
|
||||
};
|
||||
let progress_duration = SystemTime::now().duration_since(progress_time).unwrap();
|
||||
let cycles_proven = steps_proven * 4;
|
||||
let proof_cycles_hertz = k as f64 * 1000.0 / progress_duration.as_millis() as f64;
|
||||
track(
|
||||
"progress".into(),
|
||||
format!(
|
||||
"Proved step {} at {:.2} proof cycles/sec.",
|
||||
step, proof_cycles_hertz
|
||||
),
|
||||
&ws_addr_string,
|
||||
json!({
|
||||
"completed_fraction": completed_fraction,
|
||||
"steps_in_trace": total_steps,
|
||||
"steps_to_prove": steps_to_prove,
|
||||
"steps_proven": steps_proven,
|
||||
"cycles_proven": steps_proven * 4,
|
||||
"k": k,
|
||||
"progress_duration_millis": progress_duration.as_millis(),
|
||||
"proof_cycles_hertz": proof_cycles_hertz,
|
||||
"prover_id": prover_id,
|
||||
}),
|
||||
);
|
||||
progress_time = SystemTime::now();
|
||||
|
||||
let mut retries = 0;
|
||||
let max_retries = 5;
|
||||
while let Err(e) = client.send(Message::Binary(progress.encode_to_vec())).await {
|
||||
eprintln!(
|
||||
"Failed to send message: {:?}, attempt {}/{}",
|
||||
e,
|
||||
retries + 1,
|
||||
max_retries
|
||||
);
|
||||
|
||||
retries += 1;
|
||||
if retries >= max_retries {
|
||||
eprintln!("Max retries reached, exiting...");
|
||||
break;
|
||||
}
|
||||
|
||||
// Add a delay before retrying
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(u64::pow(2, retries))).await;
|
||||
}
|
||||
|
||||
if step == end - 1 {
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Box::new(&mut buf);
|
||||
let mut encoder = Encoder::new(&mut writer, 0).expect("failed to create encoder");
|
||||
proof
|
||||
.serialize_compressed(&mut encoder)
|
||||
.expect("failed to compress proof");
|
||||
encoder.finish().expect("failed to finish encoder");
|
||||
|
||||
let response = ProverRequest {
|
||||
contents: Some(prover_request::Contents::Proof(pb::Proof {
|
||||
proof: Some(proof::Proof::NovaBytes(buf)),
|
||||
})),
|
||||
};
|
||||
let duration = SystemTime::now().duration_since(start_time).unwrap();
|
||||
let proof_cycles_hertz =
|
||||
cycles_proven as f64 * 1000.0 / duration.as_millis() as f64;
|
||||
client
|
||||
.send(Message::Binary(response.encode_to_vec()))
|
||||
.await
|
||||
.unwrap();
|
||||
track(
|
||||
"proof".into(),
|
||||
format!(
|
||||
"Proof sent! Overall speed was {:.2} proof cycles/sec.",
|
||||
proof_cycles_hertz
|
||||
),
|
||||
&ws_addr_string,
|
||||
json!({
|
||||
"proof_duration_sec": duration.as_secs(),
|
||||
"proof_duration_millis": duration.as_millis(),
|
||||
"proof_cycles_hertz": proof_cycles_hertz,
|
||||
"prover_id": prover_id,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
// TODO(collinjackson): Consider verifying the proof before sending it
|
||||
// proof.verify(&public_params, proof.step_num() as _).expect("error verifying execution")
|
||||
|
||||
if args.just_once {
|
||||
break;
|
||||
} else {
|
||||
println!("Waiting for another program to prove...");
|
||||
}
|
||||
}
|
||||
|
||||
client
|
||||
.close(Some(CloseFrame {
|
||||
code: CloseCode::Normal,
|
||||
reason: Cow::Borrowed("Finished proving."),
|
||||
}))
|
||||
.await
|
||||
.unwrap();
|
||||
track(
|
||||
"disconnect".into(),
|
||||
"Sent proof and closed connection...".into(),
|
||||
&ws_addr_string,
|
||||
json!({ "prover_id": prover_id }),
|
||||
);
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
{
|
||||
"hosting": {
|
||||
"public": "public",
|
||||
"rewrites": [ {
|
||||
"source": "**",
|
||||
"destination": "/install.sh"
|
||||
} ],
|
||||
"ignore": [
|
||||
"firebase.json",
|
||||
"**/.*",
|
||||
|
150
proto/orchestrator.proto
Normal file
150
proto/orchestrator.proto
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright (c) 2024 Nexus. All rights reserved.
|
||||
//
|
||||
// If you use this protocol to communicate with Nexus's servers,
|
||||
// you must agree to the Terms of Service: https://nexus.xyz/terms-of-use
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package nexus.orchestrator;
|
||||
|
||||
service Orchestrator {
|
||||
// Request a proof for a program.
|
||||
rpc RequestProof(ProofRequest) returns (ProofResponse) {}
|
||||
|
||||
// Compile a Rust program to an ELF binary for use by clients that don't
|
||||
// support compiling programs themselves.
|
||||
rpc Compile(CompileRequest) returns (CompileResponse) {}
|
||||
|
||||
// Register a supply node with the network of compute governed by the
|
||||
// orchestrator.
|
||||
rpc AddProver(stream ProverRequest) returns (stream ProverResponse) {}
|
||||
}
|
||||
|
||||
message ProofRequest {
|
||||
CompiledProgram program = 1;
|
||||
|
||||
VMProgramInput input = 2;
|
||||
|
||||
// Step of the trace to start the proof, inclusive.
|
||||
//
|
||||
// If missing, proving starts at the beginning of the trace.
|
||||
optional int32 step_to_start = 3;
|
||||
|
||||
// Number of steps for this proof request.
|
||||
//
|
||||
// If zero, proving is skipped. If missing, all steps are proved.
|
||||
optional int32 steps_to_prove = 4;
|
||||
}
|
||||
|
||||
message ProofResponse {
|
||||
Proof proof = 1;
|
||||
}
|
||||
|
||||
// A message that always represents a program runnable on the Nexus VM.
|
||||
message CompiledProgram {
|
||||
oneof program {
|
||||
// ELF binary containing a program to be proved, expressed in the RV32I ISA.
|
||||
bytes rv32i_elf_bytes = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message VMProgramInput {
|
||||
oneof input {
|
||||
// Input expressed as raw bytes to be read as-is off of the input tape.
|
||||
bytes raw_bytes = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message Proof {
|
||||
oneof proof {
|
||||
bytes nova_bytes = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message ProgramSource {
|
||||
// The source code to be compiled. There will be a variety of languages and
|
||||
// ways to express everything a program needs for compilation (dependencies,
|
||||
// multiple files, etc.) as our scope expands.
|
||||
oneof source {
|
||||
// Option to use when the program in question can be expressed as a single
|
||||
// rust file (i.e., a program written in the playground).
|
||||
string rust_single_file = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message CompileRequest {
|
||||
ProgramSource source = 1;
|
||||
}
|
||||
|
||||
message CompileResponse {
|
||||
CompiledProgram program = 1;
|
||||
}
|
||||
|
||||
message Progress {
|
||||
// Completion status expressed as a number between zero and one,
|
||||
// inclusive.
|
||||
float completed_fraction = 1;
|
||||
|
||||
// The total size of the execution trace in steps.
|
||||
int32 steps_in_trace = 2;
|
||||
|
||||
// The number of steps of the execution trace to be proven.
|
||||
int32 steps_to_prove = 3;
|
||||
|
||||
// The number of steps proven so far.
|
||||
int32 steps_proven = 4;
|
||||
}
|
||||
|
||||
// Streamed messages sent to the orchestrator to keep it updated with the
|
||||
// prover's status.
|
||||
message ProverRequest {
|
||||
oneof contents {
|
||||
// Details about this supply node for use by the orchestrator.
|
||||
ProverRequestRegistration registration = 1;
|
||||
|
||||
// A completed proof.
|
||||
Proof proof = 2;
|
||||
|
||||
// Periodic progress update for the current proof.
|
||||
Progress progress = 3;
|
||||
|
||||
// Periodic liveness indicator when no proof is being computed.
|
||||
Heartbeat heartbeat = 4;
|
||||
}
|
||||
}
|
||||
|
||||
enum ProverType {
|
||||
// Experimental new prover types should leave the prover type unspecified.
|
||||
PROVER_TYPE_UNSPECIFIED = 0;
|
||||
|
||||
// The default prover type, used for volunteered compute resources.
|
||||
PROVER_TYPE_VOLUNTEER = 1;
|
||||
|
||||
// Provers running on public continuous integration.
|
||||
// May restrict the types of programs that can be assigned.
|
||||
PROVER_TYPE_CI = 2;
|
||||
}
|
||||
|
||||
// Metadata that helps the orchestrator schedule work to the requesting compute
|
||||
// supplier.
|
||||
message ProverRequestRegistration {
|
||||
// What type of prover this is.
|
||||
ProverType prover_type = 1;
|
||||
|
||||
// A unique identifier for this prover, generated by the prover.
|
||||
//
|
||||
// Distict provers must not share an identifier; do not use a constant value.
|
||||
string prover_id = 2;
|
||||
|
||||
// The number of proof cycles that this prover expects to compute
|
||||
// over the course of one second. Proof cycles are proof steps times k.
|
||||
optional double estimated_proof_cycles_hertz = 3;
|
||||
}
|
||||
|
||||
message ProverResponse {
|
||||
// Forward the literal request for now
|
||||
ProofRequest to_prove = 1;
|
||||
}
|
||||
|
||||
message Heartbeat {
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Welcome to Firebase Hosting</title>
|
||||
|
||||
<!-- update the version number as needed -->
|
||||
<script defer src="/__/firebase/10.12.5/firebase-app-compat.js"></script>
|
||||
<!-- include only the Firebase features as you need -->
|
||||
<script defer src="/__/firebase/10.12.5/firebase-auth-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-database-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-firestore-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-functions-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-messaging-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-storage-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-analytics-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-remote-config-compat.js"></script>
|
||||
<script defer src="/__/firebase/10.12.5/firebase-performance-compat.js"></script>
|
||||
<!--
|
||||
initialize the SDK after all desired features are loaded, set useEmulator to false
|
||||
to avoid connecting the SDK to running emulators.
|
||||
-->
|
||||
<script defer src="/__/firebase/init.js?useEmulator=true"></script>
|
||||
|
||||
<style media="screen">
|
||||
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
|
||||
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px; border-radius: 3px; }
|
||||
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
|
||||
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
|
||||
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
|
||||
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
|
||||
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
|
||||
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
|
||||
@media (max-width: 600px) {
|
||||
body, #message { margin-top: 0; background: white; box-shadow: none; }
|
||||
body { border-top: 16px solid #ffa100; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="message">
|
||||
<h2>Welcome</h2>
|
||||
<h1>Firebase Hosting Setup Complete</h1>
|
||||
<p>You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!</p>
|
||||
<a target="_blank" href="https://firebase.google.com/docs/hosting/">Open Hosting Documentation</a>
|
||||
</div>
|
||||
<p id="load">Firebase SDK Loading…</p>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const loadEl = document.querySelector('#load');
|
||||
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
|
||||
// // The Firebase SDK is initialized and available here!
|
||||
//
|
||||
// firebase.auth().onAuthStateChanged(user => { });
|
||||
// firebase.database().ref('/path/to/ref').on('value', snapshot => { });
|
||||
// firebase.firestore().doc('/foo/bar').get().then(() => { });
|
||||
// firebase.functions().httpsCallable('yourFunction')().then(() => { });
|
||||
// firebase.messaging().requestPermission().then(() => { });
|
||||
// firebase.storage().ref('/path/to/ref').getDownloadURL().then(() => { });
|
||||
// firebase.analytics(); // call to activate
|
||||
// firebase.analytics().logEvent('tutorial_completed');
|
||||
// firebase.performance(); // call to activate
|
||||
//
|
||||
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
|
||||
|
||||
try {
|
||||
let app = firebase.app();
|
||||
let features = [
|
||||
'auth',
|
||||
'database',
|
||||
'firestore',
|
||||
'functions',
|
||||
'messaging',
|
||||
'storage',
|
||||
'analytics',
|
||||
'remoteConfig',
|
||||
'performance',
|
||||
].filter(feature => typeof app[feature] === 'function');
|
||||
loadEl.textContent = `Firebase SDK loaded with ${features.join(', ')}`;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
loadEl.textContent = 'Error loading the Firebase SDK, check the console.';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
31
public/install.sh
Executable file
31
public/install.sh
Executable file
@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
rustc --version || curl https://sh.rustup.rs -sSf | sh
|
||||
NEXUS_HOME=$HOME/.nexus
|
||||
|
||||
while [ -z "$NONINTERACTIVE" ]; do
|
||||
read -p "Do you agree to the Nexus Beta Terms of Use (https://nexus.xyz/terms-of-use)? (Y/n) " yn </dev/tty
|
||||
case $yn in
|
||||
[Nn]* ) exit;;
|
||||
[Yy]* ) break;;
|
||||
"" ) break;;
|
||||
* ) echo "Please answer yes or no.";;
|
||||
esac
|
||||
done
|
||||
|
||||
git --version 2>&1 >/dev/null
|
||||
GIT_IS_AVAILABLE=$?
|
||||
if [ $GIT_IS_AVAILABLE != 0 ]; then
|
||||
echo Unable to find git. Please install it and try again.
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [ -d "$NEXUS_HOME/network-api" ]; then
|
||||
echo "$NEXUS_HOME/network-api exists. Updating.";
|
||||
(cd $NEXUS_HOME/network-api && git pull)
|
||||
else
|
||||
mkdir -p $NEXUS_HOME
|
||||
(cd $NEXUS_HOME && git clone https://github.com/nexus-xyz/network-api)
|
||||
fi
|
||||
|
||||
(cd $NEXUS_HOME/network-api/clients/cli && cargo run --release --bin prover -- beta.orchestrator.nexus.xyz)
|
Loading…
Reference in New Issue
Block a user