Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c54b23b9d5 | ||
|
04aaa714e6 | ||
|
2cc0ab2c5f | ||
|
aac0d7f2f5 | ||
|
9fd29b0575 | ||
|
6abe3c2804 | ||
|
01ce5beb19 | ||
|
2bd72318f6 | ||
|
68a9224604 | ||
|
181cad233c | ||
|
0b66529f11 | ||
|
e20e4180a9 | ||
|
de4309a905 | ||
|
d5248c936f | ||
|
40bc37266d | ||
|
1819f263f1 | ||
|
24a055e490 | ||
|
dfbd77f18d | ||
|
7c5557d69b | ||
|
d180eedd17 | ||
|
3b274ca8db |
19
.buildkite/env/secrets.ejson
vendored
@@ -1,15 +1,14 @@
|
||||
{
|
||||
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
|
||||
"environment": {
|
||||
"CODECOV_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:JnxhrIxh09AvqdJgrVSYmb7PxSrh19aE:07WzVExCHEd1lJ1m8QizRRthGri+WBNeZRKjjEvsy5eo4gv3HD7zVEm42tVTGkqITKkBNQ==]",
|
||||
"CRATES_IO_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:d0jJqC32/axwzq/N7kMRmpxKhnRrhtpt:zvcPHwkOzGnjhNkAQSejwdy1Jkr9wR1qXFFCnfIjyt/XQYubzB1tLkoly/qdmeb5]",
|
||||
"GEOLOCATION_API_KEY": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:R4gfB6Ey4i50HyfLt4UZDLBqg3qHEUye:UfZCOgt8XI6Y2g+ivCRVoS1fjFycFs7/GSevvCqh1B50mG0+hzpEyzXQLuKG5OeI]",
|
||||
"GITHUB_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:Vq2dkGTOzfEpRht0BAGHFp/hDogMvXJe:tFXHg1epVt2mq9hkuc5sRHe+KAnVREi/p8S+IZu67XRyzdiA/nGak1k860FXYuuzuaE0QWekaEc=]",
|
||||
"INFLUX_DATABASE": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:5KI9WBkXx3R/W4m256mU5MJOE7N8aAT9:Cb8QFELZ9I60t5zhJ9h55Kcs]",
|
||||
"INFLUX_PASSWORD": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:hQRMpLCrav+OYkNphkeM4hagdVoZv5Iw:AUO76rr6+gF1OLJA8ZLSG8wHKXgYCPNk6gRCV8rBhZBJ4KwDaxpvOhMl7bxxXG6jol7v4aRa/Lk=]",
|
||||
"INFLUX_USERNAME": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:R7BNmQjfeqoGDAFTJu9bYTGHol2NgnYN:Q2tOT/EBcFvhFk+DKLKmVU7tLCpVC3Ui]",
|
||||
"SOLANA_INSTALL_UPDATE_MANIFEST_KEYPAIR_x86_64_unknown_linux_gnu": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:Egc2dMrHDU0NcZ71LwGv/V66shUhwYUE:04VoIb8CKy7KYhQ5W4cEW9SDKZltxWBL5Hob106lMBbUOD/yUvKYcG3Ep8JfTMwO3K8zowW5HpU/IdGoilX0XWLiJJ6t+p05WWK0TA16nOEtwrEG+UK8wm3sN+xCO20i4jDhpNpgg3FYFHT5rKTHW8+zaBTNUX/SFxkN67Lm+92IM28CXYE43SU1WV6H99hGFFVpTK5JVM3JuYU1ex/dHRE+xCzTr4MYUB/F+nGoNFW8HUDV/y0e1jxT9to3x0SmnytEEuk+5RUzFuEt9cKNFeNml3fOCi4qL+sfj/Y5pjH9xDiUxsvH/8NL35jbLP244aFHgWcp]",
|
||||
"SOLANA_INSTALL_UPDATE_MANIFEST_KEYPAIR_x86_64_apple_darwin": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:NeOxSoWCvXB9AL4H6OK26l/7bmsKd/oz:Ijfoxtvk2CHlN1ZXHup3Gg/914kbbAkEGWJfvozA8UIe+aUzUObMyTrKkVOeNAH8Q8YH9tNzk7RRnrTcpnzeCCBLlWcVEeruMxHox3mPRzmSeDLxtbzCl9VePlRO3T7jg90K5hW+ZAkd5J/WJNzpAcmr93ts/of3MbvGHSujId/efCTzJEcP6JInnBb8Vrj7TlgKbzUlnqpq1+NjYPSXN3maKa9pKeo2JWxZlGBMoy6QWUUY5GbYEylw9smwh1LJcHZjlaZNMuOl4gNKtaSr38IXQkAXaRUJDPAmPras00YObKzXU8RkTrP4EoP/jx5LPR7f]",
|
||||
"SOLANA_INSTALL_UPDATE_MANIFEST_KEYPAIR_x86_64_pc_windows_msvc": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:7t+56twjW+jR7fpFNNeRFLPd7E4lbmyN:JuviDpkQrfVcNUGRGsa2e/UhvH6tTYyk1s4cHHE5xZH1NByL7Kpqx36VG/+o1AUGEeSQdsBnKgzYdMoFYbO8o50DoRPc86QIEVXCupD6J9avxLFtQgOWgJp+/mCdUVXlqXiFs/vQgS/L4psrcKdF6WHd77BeUr6ll8DjH+9m5FC9Rcai2pXno6VbPpunHQ0oUdYzhFR64+LiRacBaefQ9igZ+nSEWDLqbaZSyfm9viWkijoVFTq8gAgdXXEh7g0QdxVE5T6bPristJhT6jWBhWunPUCDNFFErWIsbRGctepl4pbCWqh2hNTw9btSgVfeY6uGCOsdy9E=]"
|
||||
"CODECOV_TOKEN": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:ks2/ElgxwgxqgmFcxTHANNLmj23YH74h:U4uzRONRfiQyqy6HrPQ/e7OnBUY4HkW37R0iekkF3KJ9UGnHqT1UvwgVbDqLahtDIJ4rWw==]",
|
||||
"CRATES_IO_TOKEN": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:lKMh3aLW+jyRrfS/c7yvkpB+TaPhXqLq:j0v27EbaPgwRdHZAbsM0FlAnt3r9ScQrFbWJYOAZtM3qestEiByTlKpZ0eyF/823]",
|
||||
"GITHUB_TOKEN": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:Ll78c3jGpYqnTwR7HJq3mNNUC7pOv9Lu:GrInO2r8MjmP5c54szkyygdsrW5KQYkDgJQUVyFEPyG8SWfchyM9Gur8RV0a+cdwuxNkHLi4U2M=]",
|
||||
"INFLUX_DATABASE": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:IlH/ZLTXv3SwlY3TVyAPCX2KzLRY6iG3:gGmUGSU/kCfR/mTwKONaUC/X]",
|
||||
"INFLUX_PASSWORD": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:o2qm95GU4VrrcC4OU06jjPvCwKZy/CZF:OW2ga3kLOQJvaDEdGRJ+gn3L2ckFm8AJZtv9wj/GeUIKDH2A4uBPTHsAH9PMe6zujpuHGk3qbeg=]",
|
||||
"INFLUX_USERNAME": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:yDWW/uIHsJqOTDYskZoSx3pzoB1vztWY:2z31oTA3g0Xs9fCczGNJRcx8xf/hFCed]",
|
||||
"SOLANA_INSTALL_UPDATE_MANIFEST_KEYPAIR_x86_64_unknown_linux_gnu": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:RqRaHlYUvGPNFJa6gmciaYM3tRJTURUH:q78/3GTHCN3Uqx9z4nOBjPZcO1lOazNoB/mdhGRDFsnAqVd2hU8zbKkqLrZfLlGqyD8WQOFuw5oTJR9qWg6L9LcOyj3pGL8jWF2yjgZxdtNMXnkbSrCWLooWBBLT61jYQnEwg73gT8ld3Q8EVv3T+MeSMu6FnPz+0+bqQCAGgfqksP4hsUAJGzgZu+i0tNOdlT7fxnh5KJK/yFM/CKgN2sRwEjukA9hXsffyB61g2zqzTDJxCUDLbCVrCkA/bfUk7Of/t0W5t0nK1H3oyGZEc/lRMauCknDBka3Gz11dVss2QT19WQNh0u7bHVaT/U4lepX1j9Zv]",
|
||||
"SOLANA_INSTALL_UPDATE_MANIFEST_KEYPAIR_x86_64_apple_darwin": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:wFDl3INEnA3EQDHRX40avqGe1OMoJxyy:6ncCRVRTIRuYI5o/gayeuWCudWvmKNYr8KEHAWeTq34a5bdcKInBdKhjmjX+wLHqsEwQ5gcyhcxy4Ri2mbuN6AHazfZOZlubQkGlyUOAIYO5D5jkbyIh40DAtjVzo1MD/0HsW9zdGOzqUKp5xJJeDsbR4F153jbxa7fvwF90Q4UQjYFTKAtExEmHtDGSJG48ToVwTabTV/OnISMIggDZBviIv2QWHvXgK07b2mUj34rHJywEDGN1nj5rITTDdUeRcB1x4BAMOe94kTFPSTaj/OszvYlGECt8rkKFqbm092qL+XLfiBaImqe/WJHRCnAj6Don]",
|
||||
"SOLANA_INSTALL_UPDATE_MANIFEST_KEYPAIR_x86_64_pc_windows_msvc": "EJ[1:8iZ6baJB4fbBV+XDsrUooyGAnGL/8Ol+4Qd0zKh5YjI=:wAh+dBuZopv6vruVOYegUcq/aBnbksT1:qIJfCfDvDWiqicMOkmbJs/0n7UJLKNmgMQaKzeQ8J7Q60YpXbtWzKVW3tS6lzlgf64m3MrPXyo1C+mWh6jkjsb18T/OfggZy1ZHM4AcsOC6/ldUkV5YtuxUQuAmd5jCuV/R7iuYY8Z66AcfAevlb+bnLpgIifdA8fh/IktOo58nZUQwZDdppAacmftsLc6Frn5Er6A6+EXpxK1nmnlmLJ4AJztqlh6X0r+JvE2O7qeoZUXrIegnkxo7Aay7I/dd8zdYpp7ICSiTEtfVN/xNIu/5QmTRU7gWoz7cPl9epq4aiEALzPOzb6KVOiRcsOg+TlFvLQ71Ik5o=]"
|
||||
}
|
||||
}
|
||||
|
2
.github/stale.yml
vendored
@@ -1,7 +1,7 @@
|
||||
only: pulls
|
||||
|
||||
# Number of days of inactivity before a pull request becomes stale
|
||||
daysUntilStale: 7
|
||||
daysUntilStale: 30
|
||||
|
||||
# Number of days of inactivity before a stale pull request is closed
|
||||
daysUntilClose: 7
|
||||
|
2
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
/book/html/
|
||||
/book/src/tests.ok
|
||||
/book/src/.gitbook/assets/*.svg
|
||||
/farf/
|
||||
/solana-release/
|
||||
/solana-release.tar.bz2
|
||||
@@ -16,7 +15,6 @@
|
||||
# log files
|
||||
*.log
|
||||
log-*.txt
|
||||
log-*/
|
||||
|
||||
# intellij files
|
||||
/.idea/
|
||||
|
64
.mergify.yml
@@ -19,6 +19,54 @@ pull_request_rules:
|
||||
label:
|
||||
add:
|
||||
- automerge
|
||||
- name: v0.16 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.16
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.16
|
||||
- name: v0.17 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.17
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.17
|
||||
- name: v0.18 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.18
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.18
|
||||
- name: v0.19 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.19
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.19
|
||||
- name: v0.20 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.20
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.20
|
||||
- name: v0.21 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.21
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.21
|
||||
- name: v0.22 backport
|
||||
conditions:
|
||||
- base=master
|
||||
@@ -27,19 +75,3 @@ pull_request_rules:
|
||||
backport:
|
||||
branches:
|
||||
- v0.22
|
||||
- name: v0.23 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.23
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.23
|
||||
- name: v0.24 backport
|
||||
conditions:
|
||||
- base=master
|
||||
- label=v0.24
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- v0.24
|
||||
|
@@ -3,10 +3,11 @@ os:
|
||||
|
||||
language: rust
|
||||
rust:
|
||||
- stable
|
||||
- 1.37.0
|
||||
|
||||
install:
|
||||
- source ci/rust-version.sh
|
||||
- test $rust_stable = $TRAVIS_RUST_VERSION # Update .travis.yml rust version above when this fails
|
||||
|
||||
script:
|
||||
- source ci/env.sh
|
||||
@@ -15,7 +16,7 @@ script:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^v\d+\.\d+/
|
||||
- /^v\d+\.\d+$/
|
||||
|
||||
notifications:
|
||||
slack:
|
||||
|
259
CONTRIBUTING.md
@@ -1,41 +1,23 @@
|
||||
# Solana Coding Guidelines
|
||||
Solana Coding Guidelines
|
||||
===
|
||||
|
||||
The goal of these guidelines is to improve developer productivity by allowing
|
||||
developers to jump into any file in the codebase and not need to adapt to
|
||||
inconsistencies in how the code is written. The codebase should appear as if it
|
||||
had been authored by a single developer. If you don't agree with a convention,
|
||||
submit a PR patching this document and let's discuss! Once the PR is accepted,
|
||||
*all* code should be updated as soon as possible to reflect the new
|
||||
The goal of these guidelines is to improve developer productivity by allowing developers to
|
||||
jump any file in the codebase and not need to adapt to inconsistencies in how the code is
|
||||
written. The codebase should appear as if it had been authored by a single developer. If you
|
||||
don't agree with a convention, submit a PR patching this document and let's discuss! Once
|
||||
the PR is accepted, *all* code should be updated as soon as possible to reflect the new
|
||||
conventions.
|
||||
|
||||
## Pull Requests
|
||||
Pull Requests
|
||||
---
|
||||
|
||||
Small, frequent PRs are much preferred to large, infrequent ones. A large PR is
|
||||
difficult to review, can block others from making progress, and can quickly get
|
||||
its author into "rebase hell". A large PR oftentimes arises when one change
|
||||
requires another, which requires another, and then another. When you notice
|
||||
those dependencies, put the fix into a commit of its own, then checkout a new
|
||||
branch, and cherry-pick it.
|
||||
|
||||
```bash
|
||||
$ git commit -am "Fix foo, needed by bar"
|
||||
$ git checkout master
|
||||
$ git checkout -b fix-foo
|
||||
$ git cherry-pick fix-bar
|
||||
$ git push --set-upstream origin fix-foo
|
||||
```
|
||||
|
||||
Open a PR to start the review process and then jump back to your original
|
||||
branch to keep making progress. Consider rebasing to make your fix the first
|
||||
commit:
|
||||
|
||||
```bash
|
||||
$ git checkout fix-bar
|
||||
$ git rebase -i master <Move fix-foo to top>
|
||||
```
|
||||
|
||||
Once the commit is merged, rebase the original branch to purge the
|
||||
cherry-picked commit:
|
||||
Small, frequent PRs are much preferred to large, infrequent ones. A large PR is difficult
|
||||
to review, can block others from making progress, and can quickly get its author into
|
||||
"rebase hell". A large PR oftentimes arises when one change requires another, which requires
|
||||
another, and then another. When you notice those dependencies, put the fix into a commit of
|
||||
its own, then checkout a new branch, and cherrypick it. Open a PR to start the review
|
||||
process and then jump back to your original branch to keep making progress. Once the commit
|
||||
is merged, you can use git-rebase to purge it from your original branch.
|
||||
|
||||
```bash
|
||||
$ git pull --rebase upstream master
|
||||
@@ -43,137 +25,26 @@ $ git pull --rebase upstream master
|
||||
|
||||
### How big is too big?
|
||||
|
||||
If there are no functional changes, PRs can be very large and that's no
|
||||
problem. If, however, your changes are making meaningful changes or additions,
|
||||
then about 1,000 lines of changes is about the most you should ask a Solana
|
||||
maintainer to review.
|
||||
If there are no functional changes, PRs can be very large and that's no problem. If,
|
||||
however, your changes are making meaningful changes or additions, then about 1,000 lines of
|
||||
changes is about the most you should ask a Solana maintainer to review.
|
||||
|
||||
### Should I send small PRs as I develop large, new components?
|
||||
|
||||
Add only code to the codebase that is ready to be deployed. If you are building
|
||||
a large library, consider developing it in a separate git repository. When it
|
||||
is ready to be integrated, the Solana maintainers will work with you to decide
|
||||
on a path forward. Smaller libraries may be copied in whereas very large ones
|
||||
may be pulled in with a package manager.
|
||||
|
||||
## Getting Pull Requests Merged
|
||||
|
||||
There is no single person assigned to watching GitHub PR queue and ushering you
|
||||
through the process. Typically, you will ask the person that wrote a component
|
||||
to review changes to it. You can find the author using `git blame` or asking on
|
||||
Discord. When working to get your PR merged, it's most important to understand
|
||||
that changing the code is your priority and not necessarily a priority of the
|
||||
person you need an approval from. Also, while you may interact the most with
|
||||
the component author, you should aim to be inclusive of others. Providing a
|
||||
detailed problem description is the most effective means of engaging both the
|
||||
component author and other potentially interested parties.
|
||||
|
||||
Consider opening all PRs as Draft Pull Requests first. Using a draft PR allows
|
||||
you to kickstart the CI automation, which typically takes between 10 and 30
|
||||
minutes to execute. Use that time to write a detailed problem description. Once
|
||||
the description is written and CI succeeds, click the "Ready to Review" button
|
||||
and add reviewers. Adding reviewers before CI succeeds is a fast path to losing
|
||||
reviewer engagement. Not only will they be notified and see the PR is not yet
|
||||
ready for them, they will also be bombarded them with additional notifications
|
||||
each time you push a commit to get past CI or until they "mute" the PR. Once
|
||||
muted, you'll need to reach out over some other medium, such as Discord, to
|
||||
request they have another look. When you use draft PRs, no notifications are
|
||||
sent when you push commits and edit the PR description. Use draft PRs
|
||||
liberally. Don't bug the humans until you have gotten past the bots.
|
||||
|
||||
### What should be in my PR description?
|
||||
|
||||
Reviewing code is hard work and generally involves an attempt to guess the
|
||||
author's intent at various levels. Please assume reviewer time is scarce and do
|
||||
what you can to make your PR as consumable as possible. Inspired by techniques
|
||||
for writing good whitepapers, the guidance here aims to maximize reviewer
|
||||
engagement.
|
||||
|
||||
Assume the reviewer will spend no more than a few seconds reading the PR title.
|
||||
If it doesn't describe a noteworthy change, don't expect the reviewer to click
|
||||
to see more.
|
||||
|
||||
Next, like the abstract of a whitepaper, the reviewer will spend ~30 seconds
|
||||
reading the PR problem description. If what is described there doesn't look
|
||||
more important than competing issues, don't expect the reviewer to read on.
|
||||
|
||||
Next, the reviewer will read the proposed changes. At this point, the reviewer
|
||||
needs to be convinced the proposed changes are a *good* solution to the problem
|
||||
described above. If the proposed changes, not the code changes, generates
|
||||
discussion, consider closing the PR and returning with a design proposal
|
||||
instead.
|
||||
|
||||
Finally, once the reviewer understands the problem and agrees with the approach
|
||||
to solving it, the reviewer will view the code changes. At this point, the
|
||||
reviewer is simply looking to see if the implementation actually implements
|
||||
what was proposed and if that implementation is maintainable. When a concise,
|
||||
readable test for each new code path is present, the reviewer can safely ignore
|
||||
the details of its implementation. When those tests are missing, expect to
|
||||
either lose engagement or get a pile of review comments as the reviewer
|
||||
attempts to consider every ambiguity in your implementation.
|
||||
|
||||
### The PR Title
|
||||
|
||||
The PR title should contain a brief summary of the change, from the perspective
|
||||
of the user. Examples of good titles:
|
||||
|
||||
* Add rent to accounts
|
||||
* Fix out-of-memory error in validator
|
||||
* Clean up `process_message()` in runtime
|
||||
|
||||
The conventions here are all the same as a good git commit title:
|
||||
|
||||
* First word capitalized and in the imperative mood, not past tense ("add", not
|
||||
"added")
|
||||
* No trailing period
|
||||
* What was done, whom it was done to, and in what context
|
||||
|
||||
### The PR Problem Statement
|
||||
|
||||
The git repo implements a product with various features. The problem statement
|
||||
should describe how the product is missing a feature, how a feature is
|
||||
incomplete, or how the implementation of a feature is somehow undesirable. If
|
||||
an issue being fixed already describes the problem, go ahead and copy-paste it.
|
||||
As mentioned above, reviewer time is scarce. Given a queue of PRs to review,
|
||||
the reviewer may ignore PRs that expect them to click through links to see if
|
||||
the PR warrants attention.
|
||||
|
||||
### The Proposed Changes
|
||||
|
||||
Typically the content under the "Proposed changes" section will be a bulleted
|
||||
list of steps taken to solve the problem. Oftentimes, the list is identical to
|
||||
the subject lines of the git commits contained in the PR. It's especially
|
||||
generous (and not expected) to rebase or reword commits such that each change
|
||||
matches the logical flow in your PR description.
|
||||
Add only code to the codebase that is ready to be deployed. If you are building a large
|
||||
library, consider developing it in a separate git repository. When it is ready to be
|
||||
integrated, the Solana maintainers will work with you to decide on a path forward. Smaller
|
||||
libraries may be copied in whereas very large ones may be pulled in with a package manager.
|
||||
|
||||
### When will my PR be reviewed?
|
||||
|
||||
PRs are typically reviewed and merged in under 7 days. If your PR has been open
|
||||
for longer, it's a strong indicator that the reviewers aren't confident the
|
||||
change meets the quality standards of the codebase. You might consider closing
|
||||
it and coming back with smaller PRs and longer descriptions detailing what
|
||||
problem it solves and how it solves it. Old PRs will be marked stale and then
|
||||
closed automatically 7 days later.
|
||||
PRs are typically reviewed and merged in under 7 days. If your PR has been open for longer,
|
||||
it's a strong indicator that the reviewers aren't confident the change meets the quality
|
||||
standards of the codebase. You might consider closing it and coming back with smaller PRs
|
||||
and longer descriptions detailing what problem it solves and how it solves it.
|
||||
|
||||
### How to manage review feedback?
|
||||
|
||||
After a reviewer provides feedback, you can quickly say "acknowledged, will
|
||||
fix" using a thumb's up emoji. If you're confident your fix is exactly as
|
||||
prescribed, add a reply "Fixed in COMMIT\_HASH" and mark the comment as
|
||||
resolved. If you're not sure, reply "Is this what you had in mind?
|
||||
COMMIT\_HASH" and if so, the reviewer will reply and mark the conversation as
|
||||
resolved. Marking conversations as resolved is an excellent way to engage more
|
||||
reviewers. Leaving conversations open may imply the PR is not yet ready for
|
||||
additional review.
|
||||
|
||||
### When will my PR be re-reviewed?
|
||||
|
||||
Recall that once your PR is opened, a notification is sent every time you push
|
||||
a commit. After a reviewer adds feedback, they won't be checking on the status
|
||||
of that feedback after every new commit. Instead, directly mention the reviewer
|
||||
when you feel your PR is ready for another pass.
|
||||
|
||||
## Draft Pull Requests
|
||||
Draft Pull Requests
|
||||
---
|
||||
|
||||
If you want early feedback on your PR, use GitHub's "Draft Pull Request"
|
||||
mechanism. Draft PRs are a convenient way to collaborate with the Solana
|
||||
@@ -181,69 +52,67 @@ maintainers without triggering notifications as you make changes. When you feel
|
||||
your PR is ready for a broader audience, you can transition your draft PR to a
|
||||
standard PR with the click of a button.
|
||||
|
||||
Do not add reviewers to draft PRs. GitHub doesn't automatically clear
|
||||
approvals when you click "Ready for Review", so a review that meant "I approve
|
||||
of the direction" suddenly has the appearance of "I approve of these changes."
|
||||
Instead, add a comment that mentions the usernames that you would like a review
|
||||
from. Ask explicitly what you would like feedback on.
|
||||
Do not add reviewers to draft PRs. GitHub doesn't automatically clear approvals
|
||||
when you click "Ready for Review", so a review that meant "I approve of the
|
||||
direction" suddenly has the appearance of "I approve of these changes." Instead,
|
||||
add a comment that mentions the usernames that you would like a review from. Ask
|
||||
explicitly what you would like feedback on.
|
||||
|
||||
## Rust coding conventions
|
||||
Rust coding conventions
|
||||
---
|
||||
|
||||
* All Rust code is formatted using the latest version of `rustfmt`. Once
|
||||
installed, it will be updated automatically when you update the compiler with
|
||||
`rustup`.
|
||||
* All Rust code is formatted using the latest version of `rustfmt`. Once installed, it will be
|
||||
updated automatically when you update the compiler with `rustup`.
|
||||
|
||||
* All Rust code is linted with Clippy. If you'd prefer to ignore its advice, do
|
||||
so explicitly:
|
||||
* All Rust code is linted with Clippy. If you'd prefer to ignore its advice, do so explicitly:
|
||||
|
||||
```rust #[allow(clippy::too_many_arguments)] ```
|
||||
```rust
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
```
|
||||
|
||||
Note: Clippy defaults can be overridden in the top-level file `.clippy.toml`.
|
||||
|
||||
* For variable names, when in doubt, spell it out. The mapping from type names
|
||||
to variable names is to lowercase the type name, putting an underscore before
|
||||
each capital letter. Variable names should *not* be abbreviated unless being
|
||||
used as closure arguments and the brevity improves readability. When a function
|
||||
has multiple instances of the same type, qualify each with a prefix and
|
||||
underscore (i.e. alice\_keypair) or a numeric suffix (i.e. tx0).
|
||||
* For variable names, when in doubt, spell it out. The mapping from type names to variable names
|
||||
is to lowercase the type name, putting an underscore before each capital letter. Variable names
|
||||
should *not* be abbreviated unless being used as closure arguments and the brevity improves
|
||||
readability. When a function has multiple instances of the same type, qualify each with a
|
||||
prefix and underscore (i.e. alice_keypair) or a numeric suffix (i.e. tx0).
|
||||
|
||||
* For function and method names, use `<verb>_<subject>`. For unit tests, that
|
||||
verb should always be `test` and for benchmarks the verb should always be
|
||||
`bench`. Avoid namespacing function names with some arbitrary word. Avoid
|
||||
abbreviating words in function names.
|
||||
* For function and method names, use `<verb>_<subject>`. For unit tests, that verb should
|
||||
always be `test` and for benchmarks the verb should always be `bench`. Avoid namespacing
|
||||
function names with some arbitrary word. Avoid abbreviating words in function names.
|
||||
|
||||
* As they say, "When in Rome, do as the Romans do." A good patch should
|
||||
acknowledge the coding conventions of the code that surrounds it, even in the
|
||||
case where that code has not yet been updated to meet the conventions described
|
||||
here.
|
||||
* As they say, "When in Rome, do as the Romans do." A good patch should acknowledge the coding
|
||||
conventions of the code that surrounds it, even in the case where that code has not yet been
|
||||
updated to meet the conventions described here.
|
||||
|
||||
|
||||
## Terminology
|
||||
Terminology
|
||||
---
|
||||
|
||||
Inventing new terms is allowed, but should only be done when the term is widely
|
||||
used and understood. Avoid introducing new 3-letter terms, which can be
|
||||
confused with 3-letter acronyms.
|
||||
Inventing new terms is allowed, but should only be done when the term is widely used and
|
||||
understood. Avoid introducing new 3-letter terms, which can be confused with 3-letter acronyms.
|
||||
|
||||
[Terms currently in use](book/src/terminology.md)
|
||||
|
||||
|
||||
## Design Proposals
|
||||
Design Proposals
|
||||
---
|
||||
|
||||
Solana's architecture is described by a book generated from markdown files in
|
||||
the `book/src/` directory, maintained by an *editor* (currently @garious). To
|
||||
add a design proposal, you'll need to at least propose a change the content
|
||||
under the [Accepted Design
|
||||
Proposals](https://docs.solana.com/book/v/master/proposals) chapter. Here's
|
||||
the full process:
|
||||
Proposals](https://docs.solana.com/book/v/master/proposals) chapter.
|
||||
Here's the full process:
|
||||
|
||||
1. Propose a design by creating a PR that adds a markdown document to the
|
||||
directory `book/src/` and references it from the [table of
|
||||
contents](book/src/SUMMARY.md). Add any relevant *maintainers* to the PR
|
||||
review.
|
||||
contents](book/src/SUMMARY.md). Add any relevant *maintainers* to the PR review.
|
||||
2. The PR being merged indicates your proposed change was accepted and that the
|
||||
maintainers support your plan of attack.
|
||||
3. Submit PRs that implement the proposal. When the implementation reveals the
|
||||
need for tweaks to the proposal, be sure to update the proposal and have that
|
||||
change reviewed by the same people as in step 1.
|
||||
need for tweaks to the proposal, be sure to update the proposal and have
|
||||
that change reviewed by the same people as in step 1.
|
||||
4. Once the implementation is complete, submit a PR that moves the link from
|
||||
the Accepted Proposals to the Implemented Proposals section.
|
||||
|
3706
Cargo.lock
generated
129
Cargo.toml
@@ -1,64 +1,127 @@
|
||||
[workspace]
|
||||
# The members list excluding the `validator-cuda` crate
|
||||
default-members = [
|
||||
"bench-exchange",
|
||||
"bench-streamer",
|
||||
"bench-tps",
|
||||
"banking_bench",
|
||||
"chacha-sys",
|
||||
"client",
|
||||
"core",
|
||||
"drone",
|
||||
"validator",
|
||||
"genesis",
|
||||
"genesis_programs",
|
||||
"gossip",
|
||||
"install",
|
||||
"keygen",
|
||||
"kvstore",
|
||||
"ledger-tool",
|
||||
"local_cluster",
|
||||
"logger",
|
||||
"merkle-tree",
|
||||
"measure",
|
||||
"metrics",
|
||||
"programs/bpf_loader_api",
|
||||
"programs/bpf_loader_program",
|
||||
"programs/budget_api",
|
||||
"programs/budget_program",
|
||||
"programs/btc_spv_program",
|
||||
"programs/btc_spv_api",
|
||||
"programs/btc_spv_bin",
|
||||
"programs/config_api",
|
||||
"programs/config_program",
|
||||
"programs/config_tests",
|
||||
"programs/exchange_api",
|
||||
"programs/exchange_program",
|
||||
"programs/failure_program",
|
||||
"programs/move_loader_api",
|
||||
"programs/move_loader_program",
|
||||
"programs/librapay_api",
|
||||
"programs/noop_program",
|
||||
"programs/stake_api",
|
||||
"programs/stake_program",
|
||||
"programs/stake_tests",
|
||||
"programs/storage_api",
|
||||
"programs/storage_program",
|
||||
"programs/token_api",
|
||||
"programs/token_program",
|
||||
"programs/vote_api",
|
||||
"programs/vote_program",
|
||||
"replicator",
|
||||
"runtime",
|
||||
"sdk",
|
||||
"sdk-c",
|
||||
"upload-perf",
|
||||
"netutil",
|
||||
"fixed-buf",
|
||||
"vote-signer",
|
||||
"cli",
|
||||
"rayon-threadlimit",
|
||||
]
|
||||
|
||||
# The default-members list and the `validator-cuda` crate
|
||||
members = [
|
||||
"bench-exchange",
|
||||
"bench-streamer",
|
||||
"bench-tps",
|
||||
"banking-bench",
|
||||
"chacha",
|
||||
"chacha-cuda",
|
||||
"banking_bench",
|
||||
"chacha-sys",
|
||||
"cli-config",
|
||||
"client",
|
||||
"core",
|
||||
"faucet",
|
||||
"perf",
|
||||
"drone",
|
||||
"validator",
|
||||
"genesis",
|
||||
"genesis-programs",
|
||||
"genesis_programs",
|
||||
"gossip",
|
||||
"install",
|
||||
"keygen",
|
||||
"ledger",
|
||||
"kvstore",
|
||||
"ledger-tool",
|
||||
"local-cluster",
|
||||
"local_cluster",
|
||||
"logger",
|
||||
"log-analyzer",
|
||||
"merkle-tree",
|
||||
"measure",
|
||||
"metrics",
|
||||
"net-shaper",
|
||||
"programs/bpf_loader",
|
||||
"programs/budget",
|
||||
"programs/btc_spv",
|
||||
"programs/bpf_loader_api",
|
||||
"programs/bpf_loader_program",
|
||||
"programs/budget_api",
|
||||
"programs/budget_program",
|
||||
"programs/btc_spv_program",
|
||||
"programs/btc_spv_api",
|
||||
"programs/btc_spv_bin",
|
||||
"programs/config",
|
||||
"programs/exchange",
|
||||
"programs/failure",
|
||||
"programs/noop",
|
||||
"programs/ownable",
|
||||
"programs/stake",
|
||||
"programs/storage",
|
||||
"programs/vest",
|
||||
"programs/vote",
|
||||
"archiver",
|
||||
"archiver-lib",
|
||||
"archiver-utils",
|
||||
"remote-wallet",
|
||||
"programs/config_api",
|
||||
"programs/config_program",
|
||||
"programs/config_tests",
|
||||
"programs/exchange_api",
|
||||
"programs/exchange_program",
|
||||
"programs/failure_program",
|
||||
"programs/move_loader_api",
|
||||
"programs/move_loader_program",
|
||||
"programs/librapay_api",
|
||||
"programs/noop_program",
|
||||
"programs/stake_api",
|
||||
"programs/stake_program",
|
||||
"programs/stake_tests",
|
||||
"programs/storage_api",
|
||||
"programs/storage_program",
|
||||
"programs/token_api",
|
||||
"programs/token_program",
|
||||
"programs/vote_api",
|
||||
"programs/vote_program",
|
||||
"replicator",
|
||||
"runtime",
|
||||
"sdk",
|
||||
"sdk-c",
|
||||
"scripts",
|
||||
"sys-tuner",
|
||||
"upload-perf",
|
||||
"net-utils",
|
||||
"netutil",
|
||||
"fixed-buf",
|
||||
"vote-signer",
|
||||
"cli",
|
||||
"rayon-threadlimit",
|
||||
"watchtower",
|
||||
"validator-cuda",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
"programs/bpf",
|
||||
"programs/move_loader",
|
||||
"programs/librapay",
|
||||
]
|
||||
|
@@ -78,7 +78,7 @@ $ source $HOME/.cargo/env
|
||||
$ rustup component add rustfmt
|
||||
```
|
||||
|
||||
If your rustc version is lower than 1.39.0, please update it:
|
||||
If your rustc version is lower than 1.37.0, please update it:
|
||||
|
||||
```bash
|
||||
$ rustup update
|
||||
@@ -126,7 +126,10 @@ Remote Testnets
|
||||
---
|
||||
|
||||
We maintain several testnets:
|
||||
* `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
|
||||
|
||||
* `testnet` - public stable testnet accessible via testnet.solana.com. Runs 24/7
|
||||
* `testnet-beta` - public beta channel testnet accessible via beta.testnet.solana.com. Runs 24/7
|
||||
* `testnet-edge` - public edge channel testnet accessible via edge.testnet.solana.com. Runs 24/7
|
||||
|
||||
## Deploy process
|
||||
|
||||
|
27
RELEASE.md
@@ -138,11 +138,30 @@ There are three release channels that map to branches as follows:
|
||||
### Update documentation
|
||||
TODO: Documentation update procedure is WIP as we move to gitbook
|
||||
|
||||
Document the new recommended version by updating `book/src/running-archiver.md` and `book/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
|
||||
Document the new recommended version by updating `book/src/running-replicator.md` and `book/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
|
||||
|
||||
### Update software on devnet.solana.com
|
||||
#### Publish updated Book
|
||||
We maintain three copies of the "book" as official documentation:
|
||||
|
||||
The testnet running on devnet.solana.com is set to use a fixed release tag
|
||||
1) "Book" is the documentation for the latest official release. This should get manually updated whenever a new release is made. It is published here:
|
||||
https://solana-labs.github.io/book/
|
||||
|
||||
2) "Book-edge" tracks the tip of the master branch and updates automatically.
|
||||
https://solana-labs.github.io/book-edge/
|
||||
|
||||
3) "Book-beta" tracks the tip of the beta branch and updates automatically.
|
||||
https://solana-labs.github.io/book-beta/
|
||||
|
||||
To manually trigger an update of the "Book", create a new job of the manual-update-book pipeline.
|
||||
Set the tag of the latest release as the PUBLISH_BOOK_TAG environment variable.
|
||||
```bash
|
||||
PUBLISH_BOOK_TAG=v0.16.6
|
||||
```
|
||||
https://buildkite.com/solana-labs/manual-update-book
|
||||
|
||||
### Update software on testnet.solana.com
|
||||
|
||||
The testnet running on testnet.solana.com is set to use a fixed release tag
|
||||
which is set in the Buildkite testnet-management pipeline.
|
||||
This tag needs to be updated and the testnet restarted after a new release
|
||||
tag is created.
|
||||
@@ -182,4 +201,4 @@ TESTNET_OP=create-and-start
|
||||
### Alert the community
|
||||
|
||||
Notify Discord users on #validator-support that a new release for
|
||||
devnet.solana.com is available
|
||||
testnet.solana.com is available
|
||||
|
@@ -1,39 +0,0 @@
|
||||
[package]
|
||||
name = "solana-archiver-lib"
|
||||
version = "0.23.6"
|
||||
description = "Solana Archiver Library"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.2.1"
|
||||
crossbeam-channel = "0.3"
|
||||
ed25519-dalek = "=1.0.0-pre.1"
|
||||
log = "0.4.8"
|
||||
rand = "0.6.5"
|
||||
rand_chacha = "0.1.1"
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "0.23.6" }
|
||||
thiserror = "1.0"
|
||||
serde = "1.0.104"
|
||||
serde_json = "1.0.44"
|
||||
serde_derive = "1.0.103"
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-chacha = { path = "../chacha", version = "0.23.6" }
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-perf = { path = "../perf", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.0"
|
||||
|
||||
[lib]
|
||||
name = "solana_archiver_lib"
|
@@ -1,11 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate solana_metrics;
|
||||
|
||||
pub mod archiver;
|
||||
mod result;
|
@@ -1,48 +0,0 @@
|
||||
use serde_json;
|
||||
use solana_client::client_error;
|
||||
use solana_ledger::blockstore;
|
||||
use solana_sdk::transport;
|
||||
use std::any::Any;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ArchiverError {
|
||||
#[error("IO error")]
|
||||
IO(#[from] std::io::Error),
|
||||
|
||||
#[error("blockstore error")]
|
||||
BlockstoreError(#[from] blockstore::BlockstoreError),
|
||||
|
||||
#[error("crossbeam error")]
|
||||
CrossbeamSendError(#[from] crossbeam_channel::SendError<u64>),
|
||||
|
||||
#[error("send error")]
|
||||
SendError(#[from] std::sync::mpsc::SendError<u64>),
|
||||
|
||||
#[error("join error")]
|
||||
JoinError(Box<dyn Any + Send + 'static>),
|
||||
|
||||
#[error("transport error")]
|
||||
TransportError(#[from] transport::TransportError),
|
||||
|
||||
#[error("client error")]
|
||||
ClientError(#[from] client_error::ClientError),
|
||||
|
||||
#[error("Json parsing error")]
|
||||
JsonError(#[from] serde_json::error::Error),
|
||||
|
||||
#[error("Storage account has no balance")]
|
||||
EmptyStorageAccountBalance,
|
||||
|
||||
#[error("No RPC peers..")]
|
||||
NoRpcPeers,
|
||||
|
||||
#[error("Couldn't download full segment")]
|
||||
SegmentDownloadError,
|
||||
}
|
||||
|
||||
impl std::convert::From<Box<dyn Any + Send + 'static>> for ArchiverError {
|
||||
fn from(e: Box<dyn Any + Send + 'static>) -> ArchiverError {
|
||||
ArchiverError::JoinError(e)
|
||||
}
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
[package]
|
||||
name = "solana-archiver-utils"
|
||||
version = "0.23.6"
|
||||
description = "Solana Archiver Utils"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.8"
|
||||
rand = "0.6.5"
|
||||
rand_chacha = "0.1.1"
|
||||
solana-chacha = { path = "../chacha", version = "0.23.6" }
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-perf = { path = "../perf", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.0"
|
||||
|
||||
[lib]
|
||||
name = "solana_archiver_utils"
|
@@ -1,120 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use solana_sdk::hash::{Hash, Hasher};
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom};
|
||||
use std::mem::size_of;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> {
|
||||
let in_file = File::open(in_path)?;
|
||||
let metadata = in_file.metadata()?;
|
||||
let mut buffer_file = BufReader::new(in_file);
|
||||
|
||||
let mut hasher = Hasher::default();
|
||||
let sample_size = size_of::<Hash>();
|
||||
let sample_size64 = sample_size as u64;
|
||||
let mut buf = vec![0; sample_size];
|
||||
|
||||
let file_len = metadata.len();
|
||||
if file_len < sample_size64 {
|
||||
return Err(io::Error::new(ErrorKind::Other, "file too short!"));
|
||||
}
|
||||
for offset in sample_offsets {
|
||||
if *offset > (file_len - sample_size64) / sample_size64 {
|
||||
return Err(io::Error::new(ErrorKind::Other, "offset too large"));
|
||||
}
|
||||
buffer_file.seek(SeekFrom::Start(*offset * sample_size64))?;
|
||||
trace!("sampling @ {} ", *offset);
|
||||
match buffer_file.read(&mut buf) {
|
||||
Ok(size) => {
|
||||
assert_eq!(size, buf.len());
|
||||
hasher.hash(&buf);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error sampling file");
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(hasher.result())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::fs::{create_dir_all, remove_file};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
extern crate hex;
|
||||
|
||||
fn tmp_file_path(name: &str) -> PathBuf {
|
||||
use std::env;
|
||||
let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
|
||||
let mut rand_bits = [0u8; 32];
|
||||
thread_rng().fill(&mut rand_bits[..]);
|
||||
|
||||
let mut path = PathBuf::new();
|
||||
path.push(out_dir);
|
||||
path.push("tmp");
|
||||
create_dir_all(&path).unwrap();
|
||||
|
||||
path.push(format!("{}-{:?}", name, hex::encode(rand_bits)));
|
||||
println!("path: {:?}", path);
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample_file() {
|
||||
solana_logger::setup();
|
||||
let in_path = tmp_file_path("test_sample_file_input.txt");
|
||||
let num_strings = 4096;
|
||||
let string = "12foobar";
|
||||
{
|
||||
let mut in_file = File::create(&in_path).unwrap();
|
||||
for _ in 0..num_strings {
|
||||
in_file.write(string.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
let num_samples = (string.len() * num_strings / size_of::<Hash>()) as u64;
|
||||
let samples: Vec<_> = (0..num_samples).collect();
|
||||
let res = sample_file(&in_path, samples.as_slice());
|
||||
let ref_hash: Hash = Hash::new(&[
|
||||
173, 251, 182, 165, 10, 54, 33, 150, 133, 226, 106, 150, 99, 192, 179, 1, 230, 144,
|
||||
151, 126, 18, 191, 54, 67, 249, 140, 230, 160, 56, 30, 170, 52,
|
||||
]);
|
||||
let res = res.unwrap();
|
||||
assert_eq!(res, ref_hash);
|
||||
|
||||
// Sample just past the end
|
||||
assert!(sample_file(&in_path, &[num_samples]).is_err());
|
||||
remove_file(&in_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample_file_invalid_offset() {
|
||||
let in_path = tmp_file_path("test_sample_file_invalid_offset_input.txt");
|
||||
{
|
||||
let mut in_file = File::create(&in_path).unwrap();
|
||||
for _ in 0..4096 {
|
||||
in_file.write("123456foobar".as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
let samples = [0, 200000];
|
||||
let res = sample_file(&in_path, &samples);
|
||||
assert!(res.is_err());
|
||||
remove_file(in_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample_file_missing_file() {
|
||||
let in_path = tmp_file_path("test_sample_file_that_doesnt_exist.txt");
|
||||
let samples = [0, 5];
|
||||
let res = sample_file(&in_path, &samples);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-archiver"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
console = "0.9.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
|
@@ -1,147 +0,0 @@
|
||||
use clap::{crate_description, crate_name, App, Arg};
|
||||
use console::style;
|
||||
use solana_archiver_lib::archiver::Archiver;
|
||||
use solana_clap_utils::{
|
||||
input_validators::is_keypair,
|
||||
keypair::{
|
||||
self, keypair_input, KeypairWithSource, ASK_SEED_PHRASE_ARG,
|
||||
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
},
|
||||
};
|
||||
use solana_core::{
|
||||
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
||||
contact_info::ContactInfo,
|
||||
};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Signer};
|
||||
use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc};
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
|
||||
let matches = App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
.version(solana_clap_utils::version!())
|
||||
.arg(
|
||||
Arg::with_name("identity_keypair")
|
||||
.short("i")
|
||||
.long("identity-keypair")
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.validator(is_keypair)
|
||||
.help("File containing an identity (keypair)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("entrypoint")
|
||||
.short("n")
|
||||
.long("entrypoint")
|
||||
.value_name("HOST:PORT")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(solana_net_utils::is_host_port)
|
||||
.help("Rendezvous with the cluster at this entry point"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("ledger")
|
||||
.short("l")
|
||||
.long("ledger")
|
||||
.value_name("DIR")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("use DIR as persistent ledger location"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("storage_keypair")
|
||||
.short("s")
|
||||
.long("storage-keypair")
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.validator(is_keypair)
|
||||
.help("File containing the storage account keypair"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(ASK_SEED_PHRASE_ARG.name)
|
||||
.long(ASK_SEED_PHRASE_ARG.long)
|
||||
.value_name("KEYPAIR NAME")
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.possible_values(&["identity-keypair", "storage-keypair"])
|
||||
.help(ASK_SEED_PHRASE_ARG.help),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
||||
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
||||
.requires(ASK_SEED_PHRASE_ARG.name)
|
||||
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let ledger_path = PathBuf::from(matches.value_of("ledger").unwrap());
|
||||
|
||||
let identity_keypair = keypair_input(&matches, "identity_keypair")
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Identity keypair input failed: {}", err);
|
||||
exit(1);
|
||||
})
|
||||
.keypair;
|
||||
let KeypairWithSource {
|
||||
keypair: storage_keypair,
|
||||
source: storage_keypair_source,
|
||||
} = keypair_input(&matches, "storage_keypair").unwrap_or_else(|err| {
|
||||
eprintln!("Storage keypair input failed: {}", err);
|
||||
exit(1);
|
||||
});
|
||||
if storage_keypair_source == keypair::Source::Generated {
|
||||
clap::Error::with_description(
|
||||
"The `storage-keypair` argument was not found",
|
||||
clap::ErrorKind::ArgumentNotFound,
|
||||
)
|
||||
.exit();
|
||||
}
|
||||
|
||||
let entrypoint_addr = matches
|
||||
.value_of("entrypoint")
|
||||
.map(|entrypoint| {
|
||||
solana_net_utils::parse_host_port(entrypoint)
|
||||
.expect("failed to parse entrypoint address")
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let gossip_addr = {
|
||||
let ip = solana_net_utils::get_public_ip_addr(&entrypoint_addr).unwrap();
|
||||
let mut addr = SocketAddr::new(ip, 0);
|
||||
addr.set_ip(solana_net_utils::get_public_ip_addr(&entrypoint_addr).unwrap());
|
||||
addr
|
||||
};
|
||||
let node = Node::new_archiver_with_external_ip(
|
||||
&identity_keypair.pubkey(),
|
||||
&gossip_addr,
|
||||
VALIDATOR_PORT_RANGE,
|
||||
);
|
||||
|
||||
println!(
|
||||
"{} version {} (branch={}, commit={})",
|
||||
style(crate_name!()).bold(),
|
||||
solana_clap_utils::version!(),
|
||||
option_env!("CI_BRANCH").unwrap_or("unknown"),
|
||||
option_env!("CI_COMMIT").unwrap_or("unknown")
|
||||
);
|
||||
solana_metrics::set_host_id(identity_keypair.pubkey().to_string());
|
||||
println!(
|
||||
"replicating the data with identity_keypair={:?} gossip_addr={:?}",
|
||||
identity_keypair.pubkey(),
|
||||
gossip_addr
|
||||
);
|
||||
|
||||
let entrypoint_info = ContactInfo::new_gossip_entry_point(&entrypoint_addr);
|
||||
let archiver = Archiver::new(
|
||||
&ledger_path,
|
||||
node,
|
||||
entrypoint_info,
|
||||
Arc::new(identity_keypair),
|
||||
Arc::new(storage_keypair),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
archiver.join();
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.6"
|
||||
rayon = "1.2.0"
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-measure = { path = "../measure", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
rand = "0.6.5"
|
||||
crossbeam-channel = "0.3"
|
19
banking_bench/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "0.19.0-pre0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.6"
|
||||
rayon = "1.2.0"
|
||||
solana-core = { path = "../core", version = "0.19.0-pre0" }
|
||||
solana-logger = { path = "../logger", version = "0.19.0-pre0" }
|
||||
solana-runtime = { path = "../runtime", version = "0.19.0-pre0" }
|
||||
solana-measure = { path = "../measure", version = "0.19.0-pre0" }
|
||||
solana-sdk = { path = "../sdk", version = "0.19.0-pre0" }
|
||||
rand = "0.6.5"
|
||||
crossbeam-channel = "0.3"
|
@@ -1,16 +1,21 @@
|
||||
#[macro_use]
|
||||
extern crate solana_core;
|
||||
extern crate crossbeam_channel;
|
||||
|
||||
use crossbeam_channel::unbounded;
|
||||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_core::bank_forks::BankForks;
|
||||
use solana_core::banking_stage::{create_test_recorder, BankingStage};
|
||||
use solana_core::blocktree::{get_tmp_ledger_path, Blocktree};
|
||||
use solana_core::cluster_info::ClusterInfo;
|
||||
use solana_core::cluster_info::Node;
|
||||
use solana_core::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||
use solana_core::genesis_utils::{create_genesis_block, GenesisBlockInfo};
|
||||
use solana_core::packet::to_packets_chunked;
|
||||
use solana_core::poh_recorder::PohRecorder;
|
||||
use solana_core::poh_recorder::WorkingBankEntry;
|
||||
use solana_ledger::bank_forks::BankForks;
|
||||
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
|
||||
use solana_core::service::Service;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::hash::Hash;
|
||||
@@ -20,6 +25,7 @@ use solana_sdk::signature::Signature;
|
||||
use solana_sdk::system_transaction;
|
||||
use solana_sdk::timing::{duration_as_us, timestamp};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use std::iter;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
@@ -35,7 +41,7 @@ fn check_txs(
|
||||
let now = Instant::now();
|
||||
let mut no_bank = false;
|
||||
loop {
|
||||
if let Ok((_bank, (entry, _tick_height))) = receiver.recv_timeout(Duration::from_millis(10))
|
||||
if let Ok((_bank, (entry, _tick_count))) = receiver.recv_timeout(Duration::from_millis(10))
|
||||
{
|
||||
total += entry.transactions.len();
|
||||
}
|
||||
@@ -97,21 +103,21 @@ fn main() {
|
||||
const PACKETS_PER_BATCH: usize = 192;
|
||||
let txes = PACKETS_PER_BATCH * num_threads * CHUNKS;
|
||||
let mint_total = 1_000_000_000_000;
|
||||
let GenesisConfigInfo {
|
||||
genesis_config,
|
||||
let GenesisBlockInfo {
|
||||
genesis_block,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(mint_total);
|
||||
} = create_genesis_block(mint_total);
|
||||
|
||||
let (verified_sender, verified_receiver) = unbounded();
|
||||
let (vote_sender, vote_receiver) = unbounded();
|
||||
let bank0 = Bank::new(&genesis_config);
|
||||
let bank0 = Bank::new(&genesis_block);
|
||||
let mut bank_forks = BankForks::new(0, bank0);
|
||||
let mut bank = bank_forks.working_bank();
|
||||
|
||||
info!("threads: {} txs: {}", num_threads, txes);
|
||||
|
||||
let mut transactions = make_accounts_txs(txes, &mint_keypair, genesis_config.hash());
|
||||
let mut transactions = make_accounts_txs(txes, &mint_keypair, genesis_block.hash());
|
||||
|
||||
// fund all the accounts
|
||||
transactions.iter().for_each(|tx| {
|
||||
@@ -119,7 +125,7 @@ fn main() {
|
||||
&mint_keypair,
|
||||
&tx.message.account_keys[0],
|
||||
mint_total / txes as u64,
|
||||
genesis_config.hash(),
|
||||
genesis_block.hash(),
|
||||
);
|
||||
let x = bank.process_transaction(&fund);
|
||||
x.unwrap();
|
||||
@@ -136,22 +142,27 @@ fn main() {
|
||||
assert!(r.is_ok(), "sanity parallel execution");
|
||||
}
|
||||
bank.clear_signatures();
|
||||
let mut verified: Vec<_> = to_packets_chunked(&transactions.clone(), PACKETS_PER_BATCH);
|
||||
let mut verified: Vec<_> = to_packets_chunked(&transactions.clone(), PACKETS_PER_BATCH)
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let len = x.packets.len();
|
||||
(x, iter::repeat(1).take(len).collect())
|
||||
})
|
||||
.collect();
|
||||
let ledger_path = get_tmp_ledger_path!();
|
||||
{
|
||||
let blockstore = Arc::new(
|
||||
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
||||
let blocktree = Arc::new(
|
||||
Blocktree::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
||||
);
|
||||
let (exit, poh_recorder, poh_service, signal_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, None);
|
||||
create_test_recorder(&bank, &blocktree);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(RwLock::new(cluster_info));
|
||||
let banking_stage = BankingStage::new(
|
||||
let _banking_stage = BankingStage::new(
|
||||
&cluster_info,
|
||||
&poh_recorder,
|
||||
verified_receiver,
|
||||
vote_receiver,
|
||||
None,
|
||||
);
|
||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||
|
||||
@@ -162,8 +173,9 @@ fn main() {
|
||||
// If it is dropped before poh_service, then poh_service will error when
|
||||
// calling send() on the channel.
|
||||
let signal_receiver = Arc::new(signal_receiver);
|
||||
let mut total_us = 0;
|
||||
let mut tx_total_us = 0;
|
||||
let signal_receiver2 = signal_receiver.clone();
|
||||
let mut total = 0;
|
||||
let mut tx_total = 0;
|
||||
let mut txs_processed = 0;
|
||||
let mut root = 1;
|
||||
let collector = Pubkey::new_rand();
|
||||
@@ -173,7 +185,6 @@ fn main() {
|
||||
chunk_len,
|
||||
num_threads,
|
||||
};
|
||||
let mut total_sent = 0;
|
||||
for _ in 0..ITERS {
|
||||
let now = Instant::now();
|
||||
let mut sent = 0;
|
||||
@@ -198,7 +209,7 @@ fn main() {
|
||||
index,
|
||||
);
|
||||
for xv in v {
|
||||
sent += xv.packets.len();
|
||||
sent += xv.0.packets.len();
|
||||
}
|
||||
verified_sender.send(v.to_vec()).unwrap();
|
||||
}
|
||||
@@ -215,7 +226,7 @@ fn main() {
|
||||
sleep(Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
if check_txs(&signal_receiver, txes / CHUNKS, &poh_recorder) {
|
||||
if check_txs(&signal_receiver2, txes / CHUNKS, &poh_recorder) {
|
||||
debug!(
|
||||
"resetting bank {} tx count: {} txs_proc: {}",
|
||||
bank.slot(),
|
||||
@@ -224,7 +235,7 @@ fn main() {
|
||||
);
|
||||
assert!(txs_processed < bank.transaction_count());
|
||||
txs_processed = bank.transaction_count();
|
||||
tx_total_us += duration_as_us(&now.elapsed());
|
||||
tx_total += duration_as_us(&now.elapsed());
|
||||
|
||||
let mut poh_time = Measure::start("poh_time");
|
||||
poh_recorder.lock().unwrap().reset(
|
||||
@@ -256,21 +267,20 @@ fn main() {
|
||||
poh_time.as_us(),
|
||||
);
|
||||
} else {
|
||||
tx_total_us += duration_as_us(&now.elapsed());
|
||||
tx_total += duration_as_us(&now.elapsed());
|
||||
}
|
||||
|
||||
// This signature clear may not actually clear the signatures
|
||||
// in this chunk, but since we rotate between CHUNKS then
|
||||
// we should clear them by the time we come around again to re-use that chunk.
|
||||
bank.clear_signatures();
|
||||
total_us += duration_as_us(&now.elapsed());
|
||||
total += duration_as_us(&now.elapsed());
|
||||
debug!(
|
||||
"time: {} us checked: {} sent: {}",
|
||||
duration_as_us(&now.elapsed()),
|
||||
txes / CHUNKS,
|
||||
sent,
|
||||
);
|
||||
total_sent += sent;
|
||||
|
||||
if bank.slot() > 0 && bank.slot() % 16 == 0 {
|
||||
for tx in transactions.iter_mut() {
|
||||
@@ -278,7 +288,13 @@ fn main() {
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
tx.signatures[0] = Signature::new(&sig[0..64]);
|
||||
}
|
||||
verified = to_packets_chunked(&transactions.clone(), PACKETS_PER_BATCH);
|
||||
verified = to_packets_chunked(&transactions.clone(), PACKETS_PER_BATCH)
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let len = x.packets.len();
|
||||
(x, iter::repeat(1).take(len).collect())
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
start += chunk_len;
|
||||
@@ -286,21 +302,18 @@ fn main() {
|
||||
}
|
||||
eprintln!(
|
||||
"{{'name': 'banking_bench_total', 'median': '{}'}}",
|
||||
(1000.0 * 1000.0 * total_sent as f64) / (total_us as f64),
|
||||
total / ITERS as u64,
|
||||
);
|
||||
eprintln!(
|
||||
"{{'name': 'banking_bench_tx_total', 'median': '{}'}}",
|
||||
(1000.0 * 1000.0 * total_sent as f64) / (tx_total_us as f64),
|
||||
tx_total / ITERS as u64,
|
||||
);
|
||||
|
||||
drop(verified_sender);
|
||||
drop(vote_sender);
|
||||
exit.store(true, Ordering::Relaxed);
|
||||
banking_stage.join().unwrap();
|
||||
debug!("waited for banking_stage");
|
||||
poh_service.join().unwrap();
|
||||
sleep(Duration::from_secs(1));
|
||||
debug!("waited for poh_service");
|
||||
}
|
||||
let _unused = Blockstore::destroy(&ledger_path);
|
||||
let _unused = Blocktree::destroy(&ledger_path);
|
||||
}
|
@@ -2,40 +2,38 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "0.23.6"
|
||||
version = "0.19.0-pre0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.2.1"
|
||||
bincode = "1.1.4"
|
||||
bs58 = "0.3.0"
|
||||
clap = "2.32.0"
|
||||
env_logger = "0.7.1"
|
||||
itertools = "0.8.2"
|
||||
env_logger = "0.6.2"
|
||||
itertools = "0.8.0"
|
||||
log = "0.4.8"
|
||||
num-derive = "0.3"
|
||||
num-derive = "0.2"
|
||||
num-traits = "0.2"
|
||||
rand = "0.6.5"
|
||||
rayon = "1.2.0"
|
||||
serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.44"
|
||||
serde_yaml = "0.8.11"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-genesis = { path = "../genesis", version = "0.23.6" }
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.6" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
serde = "1.0.101"
|
||||
serde_derive = "1.0.101"
|
||||
serde_json = "1.0.40"
|
||||
serde_yaml = "0.8.9"
|
||||
# solana-runtime = { path = "../solana/runtime"}
|
||||
solana-core = { path = "../core", version = "0.19.0-pre0" }
|
||||
solana-genesis = { path = "../genesis", version = "0.19.0-pre0" }
|
||||
solana-client = { path = "../client", version = "0.19.0-pre0" }
|
||||
solana-drone = { path = "../drone", version = "0.19.0-pre0" }
|
||||
solana-exchange-api = { path = "../programs/exchange_api", version = "0.19.0-pre0" }
|
||||
solana-exchange-program = { path = "../programs/exchange_program", version = "0.19.0-pre0" }
|
||||
solana-logger = { path = "../logger", version = "0.19.0-pre0" }
|
||||
solana-metrics = { path = "../metrics", version = "0.19.0-pre0" }
|
||||
solana-netutil = { path = "../netutil", version = "0.19.0-pre0" }
|
||||
solana-runtime = { path = "../runtime", version = "0.19.0-pre0" }
|
||||
solana-sdk = { path = "../sdk", version = "0.19.0-pre0" }
|
||||
untrusted = "0.7.0"
|
||||
ws = "0.9.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "0.23.6" }
|
||||
ws = "0.9.0"
|
||||
|
@@ -360,7 +360,7 @@ The Matcher would initiate the following last swap:
|
||||
|
||||
- Row 1, To: Investor 1 trades 2 A token to 12 B tokens
|
||||
- Row 1, From: Investor 2 trades 2 A token from 12 B tokens
|
||||
- Matcher takes 2 B tokens as profit
|
||||
- Matcher takes 4 B tokens as profit
|
||||
|
||||
Table becomes:
|
||||
|
||||
|
@@ -7,36 +7,32 @@ use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_client::perf_utils::{sample_txs, SampleStats};
|
||||
use solana_core::gen_keys::GenKeys;
|
||||
use solana_exchange_program::{exchange_instruction, exchange_state::*, id};
|
||||
use solana_faucet::faucet::request_airdrop_transaction;
|
||||
use solana_genesis::Base64Account;
|
||||
use solana_drone::drone::request_airdrop_transaction;
|
||||
use solana_exchange_api::exchange_instruction;
|
||||
use solana_exchange_api::exchange_state::*;
|
||||
use solana_exchange_api::id;
|
||||
use solana_genesis::PrimordialAccountDetails;
|
||||
use solana_metrics::datapoint_info;
|
||||
use solana_sdk::{
|
||||
client::{Client, SyncClient},
|
||||
commitment_config::CommitmentConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
timing::{duration_as_ms, duration_as_s},
|
||||
transaction::Transaction,
|
||||
{system_instruction, system_program},
|
||||
};
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, VecDeque},
|
||||
fs::File,
|
||||
io::prelude::*,
|
||||
mem,
|
||||
net::SocketAddr,
|
||||
path::Path,
|
||||
process::exit,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::{sleep, Builder},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use solana_sdk::client::Client;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::timing::{duration_as_ms, duration_as_s};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use solana_sdk::{system_instruction, system_program};
|
||||
use std::cmp;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::mem;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread::{sleep, Builder};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
// TODO Chunk length as specified results in a bunch of failures, divide by 10 helps...
|
||||
// Assume 4MB network buffers, and 512 byte packets
|
||||
@@ -93,7 +89,7 @@ pub fn create_client_accounts_file(
|
||||
keypairs.iter().for_each(|keypair| {
|
||||
accounts.insert(
|
||||
serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap(),
|
||||
Base64Account {
|
||||
PrimordialAccountDetails {
|
||||
balance: fund_amount,
|
||||
executable: false,
|
||||
owner: system_program::id().to_string(),
|
||||
@@ -144,7 +140,8 @@ where
|
||||
let path = Path::new(&client_ids_and_stake_file);
|
||||
let file = File::open(path).unwrap();
|
||||
|
||||
let accounts: HashMap<String, Base64Account> = serde_yaml::from_reader(file).unwrap();
|
||||
let accounts: HashMap<String, PrimordialAccountDetails> =
|
||||
serde_yaml::from_reader(file).unwrap();
|
||||
accounts
|
||||
.into_iter()
|
||||
.map(|(keypair, _)| {
|
||||
@@ -178,28 +175,19 @@ where
|
||||
|
||||
info!("Generating {:?} account keys", total_keys);
|
||||
let mut account_keypairs = generate_keypairs(total_keys);
|
||||
let src_keypairs: Vec<_> = account_keypairs
|
||||
let src_pubkeys: Vec<_> = account_keypairs
|
||||
.drain(0..accounts_in_groups)
|
||||
.map(|keypair| keypair)
|
||||
.collect();
|
||||
let src_pubkeys: Vec<Pubkey> = src_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.pubkey())
|
||||
.collect();
|
||||
|
||||
let profit_keypairs: Vec<_> = account_keypairs
|
||||
let profit_pubkeys: Vec<_> = account_keypairs
|
||||
.drain(0..accounts_in_groups)
|
||||
.map(|keypair| keypair)
|
||||
.collect();
|
||||
let profit_pubkeys: Vec<Pubkey> = profit_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.pubkey())
|
||||
.collect();
|
||||
|
||||
info!("Create {:?} source token accounts", src_pubkeys.len());
|
||||
create_token_accounts(client, &trader_signers, &src_keypairs);
|
||||
create_token_accounts(client, &trader_signers, &src_pubkeys);
|
||||
info!("Create {:?} profit token accounts", profit_pubkeys.len());
|
||||
create_token_accounts(client, &swapper_signers, &profit_keypairs);
|
||||
create_token_accounts(client, &swapper_signers, &profit_pubkeys);
|
||||
|
||||
// Collect the max transaction rate and total tx count seen (single node only)
|
||||
let sample_stats = Arc::new(RwLock::new(Vec::new()));
|
||||
@@ -256,7 +244,7 @@ where
|
||||
trace!("Start trader thread");
|
||||
let trader_thread = {
|
||||
let exit_signal = exit_signal.clone();
|
||||
|
||||
let shared_txs = shared_txs.clone();
|
||||
let client = clients[0].clone();
|
||||
Builder::new()
|
||||
.name("solana-exchange-trader".to_string())
|
||||
@@ -393,10 +381,7 @@ fn swapper<T>(
|
||||
let mut tries = 0;
|
||||
let mut trade_index = 0;
|
||||
while client
|
||||
.get_balance_with_commitment(
|
||||
&trade_infos[trade_index].trade_account,
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.get_balance(&trade_infos[trade_index].trade_account)
|
||||
.unwrap_or(0)
|
||||
== 0
|
||||
{
|
||||
@@ -450,7 +435,7 @@ fn swapper<T>(
|
||||
account_group = (account_group + 1) % account_groups as usize;
|
||||
|
||||
let (blockhash, _fee_calculator) = client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.get_recent_blockhash()
|
||||
.expect("Failed to get blockhash");
|
||||
let to_swap_txs: Vec<_> = to_swap
|
||||
.par_iter()
|
||||
@@ -573,39 +558,27 @@ fn trader<T>(
|
||||
trade_account: trade.pubkey(),
|
||||
order_info,
|
||||
});
|
||||
trades.push((signer, trade, side, src));
|
||||
trades.push((signer, trade.pubkey(), side, src));
|
||||
}
|
||||
account_group = (account_group + 1) % account_groups as usize;
|
||||
|
||||
let (blockhash, _fee_calculator) = client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.get_recent_blockhash()
|
||||
.expect("Failed to get blockhash");
|
||||
|
||||
trades.chunks(chunk_size).for_each(|chunk| {
|
||||
let trades_txs: Vec<_> = chunk
|
||||
.par_iter()
|
||||
.map(|(owner, trade, side, src)| {
|
||||
let owner_pubkey = &owner.pubkey();
|
||||
let trade_pubkey = &trade.pubkey();
|
||||
.map(|(signer, trade, side, src)| {
|
||||
let s: &Keypair = &signer;
|
||||
let owner = &signer.pubkey();
|
||||
let space = mem::size_of::<ExchangeState>() as u64;
|
||||
Transaction::new_signed_instructions(
|
||||
&[owner.as_ref(), trade],
|
||||
&[s],
|
||||
vec![
|
||||
system_instruction::create_account(
|
||||
owner_pubkey,
|
||||
trade_pubkey,
|
||||
1,
|
||||
space,
|
||||
&id(),
|
||||
),
|
||||
system_instruction::create_account(owner, trade, 1, space, &id()),
|
||||
exchange_instruction::trade_request(
|
||||
owner_pubkey,
|
||||
trade_pubkey,
|
||||
*side,
|
||||
pair,
|
||||
tokens,
|
||||
price,
|
||||
src,
|
||||
owner, trade, *side, pair, tokens, price, src,
|
||||
),
|
||||
],
|
||||
blockhash,
|
||||
@@ -661,14 +634,12 @@ fn trader<T>(
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_transaction<T>(sync_client: &T, tx: &Transaction) -> bool
|
||||
fn verify_transfer<T>(sync_client: &T, tx: &Transaction) -> bool
|
||||
where
|
||||
T: SyncClient + ?Sized,
|
||||
{
|
||||
for s in &tx.signatures {
|
||||
if let Ok(Some(r)) =
|
||||
sync_client.get_signature_status_with_commitment(s, CommitmentConfig::recent())
|
||||
{
|
||||
if let Ok(Some(r)) = sync_client.get_signature_status(s) {
|
||||
match r {
|
||||
Ok(_) => {
|
||||
return true;
|
||||
@@ -687,21 +658,16 @@ fn verify_funding_transfer<T: SyncClient + ?Sized>(
|
||||
tx: &Transaction,
|
||||
amount: u64,
|
||||
) -> bool {
|
||||
if verify_transaction(client, tx) {
|
||||
for a in &tx.message().account_keys[1..] {
|
||||
if client
|
||||
.get_balance_with_commitment(a, CommitmentConfig::recent())
|
||||
.unwrap_or(0)
|
||||
>= amount
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for a in &tx.message().account_keys[1..] {
|
||||
if client.get_balance(a).unwrap_or(0) >= amount {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
|
||||
pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
|
||||
let total = lamports * (dests.len() as u64 + 1);
|
||||
let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
|
||||
let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
|
||||
@@ -775,9 +741,8 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
|
||||
to_fund_txs.len(),
|
||||
);
|
||||
|
||||
let (blockhash, _fee_calculator) = client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.expect("blockhash");
|
||||
let (blockhash, _fee_calculator) =
|
||||
client.get_recent_blockhash().expect("blockhash");
|
||||
to_fund_txs.par_iter_mut().for_each(|(k, tx)| {
|
||||
tx.sign(&[*k], blockhash);
|
||||
});
|
||||
@@ -814,41 +779,27 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
|
||||
});
|
||||
funded.append(&mut new_funded);
|
||||
funded.retain(|(k, b)| {
|
||||
client
|
||||
.get_balance_with_commitment(&k.pubkey(), CommitmentConfig::recent())
|
||||
.unwrap_or(0)
|
||||
> lamports
|
||||
&& *b > lamports
|
||||
client.get_balance(&k.pubkey()).unwrap_or(0) > lamports && *b > lamports
|
||||
});
|
||||
debug!(" Funded: {} left: {}", funded.len(), notfunded.len());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_token_accounts<T: Client>(
|
||||
client: &T,
|
||||
signers: &[Arc<Keypair>],
|
||||
accounts: &[Keypair],
|
||||
) {
|
||||
let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect();
|
||||
pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], accounts: &[Pubkey]) {
|
||||
let mut notfunded: Vec<(&Arc<Keypair>, &Pubkey)> = signers.iter().zip(accounts).collect();
|
||||
|
||||
while !notfunded.is_empty() {
|
||||
notfunded.chunks(FUND_CHUNK_LEN).for_each(|chunk| {
|
||||
let mut to_create_txs: Vec<_> = chunk
|
||||
.par_iter()
|
||||
.map(|(from_keypair, new_keypair)| {
|
||||
let owner_pubkey = &from_keypair.pubkey();
|
||||
.map(|(signer, new)| {
|
||||
let owner_pubkey = &signer.pubkey();
|
||||
let space = mem::size_of::<ExchangeState>() as u64;
|
||||
let create_ix = system_instruction::create_account(
|
||||
owner_pubkey,
|
||||
&new_keypair.pubkey(),
|
||||
1,
|
||||
space,
|
||||
&id(),
|
||||
);
|
||||
let request_ix =
|
||||
exchange_instruction::account_request(owner_pubkey, &new_keypair.pubkey());
|
||||
let create_ix =
|
||||
system_instruction::create_account(owner_pubkey, new, 1, space, &id());
|
||||
let request_ix = exchange_instruction::account_request(owner_pubkey, new);
|
||||
(
|
||||
(from_keypair, new_keypair),
|
||||
signer,
|
||||
Transaction::new_unsigned_instructions(vec![create_ix, request_ix]),
|
||||
)
|
||||
})
|
||||
@@ -867,13 +818,12 @@ pub fn create_token_accounts<T: Client>(
|
||||
let mut retries = 0;
|
||||
while !to_create_txs.is_empty() {
|
||||
let (blockhash, _fee_calculator) = client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.get_recent_blockhash()
|
||||
.expect("Failed to get blockhash");
|
||||
to_create_txs
|
||||
.par_iter_mut()
|
||||
.for_each(|((from_keypair, to_keypair), tx)| {
|
||||
tx.sign(&[from_keypair.as_ref(), to_keypair], blockhash);
|
||||
});
|
||||
to_create_txs.par_iter_mut().for_each(|(k, tx)| {
|
||||
let kp: &Keypair = k;
|
||||
tx.sign(&[kp], blockhash);
|
||||
});
|
||||
to_create_txs.iter().for_each(|(_, tx)| {
|
||||
client.async_send_transaction(tx.clone()).expect("transfer");
|
||||
});
|
||||
@@ -881,11 +831,11 @@ pub fn create_token_accounts<T: Client>(
|
||||
let mut waits = 0;
|
||||
while !to_create_txs.is_empty() {
|
||||
sleep(Duration::from_millis(200));
|
||||
to_create_txs.retain(|(_, tx)| !verify_transaction(client, &tx));
|
||||
to_create_txs.retain(|(_, tx)| !verify_transfer(client, &tx));
|
||||
if to_create_txs.is_empty() {
|
||||
break;
|
||||
}
|
||||
info!(
|
||||
debug!(
|
||||
" {} transactions outstanding, waits {:?}",
|
||||
to_create_txs.len(),
|
||||
waits
|
||||
@@ -898,7 +848,7 @@ pub fn create_token_accounts<T: Client>(
|
||||
|
||||
if !to_create_txs.is_empty() {
|
||||
retries += 1;
|
||||
info!(" Retry {:?} {} txes left", retries, to_create_txs.len());
|
||||
debug!(" Retry {:?}", retries);
|
||||
if retries >= 20 {
|
||||
error!(
|
||||
"create_token_accounts: Too many retries ({}), give up",
|
||||
@@ -910,13 +860,9 @@ pub fn create_token_accounts<T: Client>(
|
||||
}
|
||||
});
|
||||
|
||||
let mut new_notfunded: Vec<(&Arc<Keypair>, &Keypair)> = vec![];
|
||||
let mut new_notfunded: Vec<(&Arc<Keypair>, &Pubkey)> = vec![];
|
||||
for f in ¬funded {
|
||||
if client
|
||||
.get_balance_with_commitment(&f.1.pubkey(), CommitmentConfig::recent())
|
||||
.unwrap_or(0)
|
||||
== 0
|
||||
{
|
||||
if client.get_balance(&f.1).unwrap_or(0) == 0 {
|
||||
new_notfunded.push(*f)
|
||||
}
|
||||
}
|
||||
@@ -972,13 +918,8 @@ fn generate_keypairs(num: u64) -> Vec<Keypair> {
|
||||
rnd.gen_n_keypairs(num)
|
||||
}
|
||||
|
||||
pub fn airdrop_lamports<T: Client>(
|
||||
client: &T,
|
||||
faucet_addr: &SocketAddr,
|
||||
id: &Keypair,
|
||||
amount: u64,
|
||||
) {
|
||||
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent());
|
||||
pub fn airdrop_lamports(client: &dyn Client, drone_addr: &SocketAddr, id: &Keypair, amount: u64) {
|
||||
let balance = client.get_balance(&id.pubkey());
|
||||
let balance = balance.unwrap_or(0);
|
||||
if balance >= amount {
|
||||
return;
|
||||
@@ -989,40 +930,33 @@ pub fn airdrop_lamports<T: Client>(
|
||||
info!(
|
||||
"Airdropping {:?} lamports from {} for {}",
|
||||
amount_to_drop,
|
||||
faucet_addr,
|
||||
drone_addr,
|
||||
id.pubkey(),
|
||||
);
|
||||
|
||||
let mut tries = 0;
|
||||
loop {
|
||||
let (blockhash, _fee_calculator) = client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.get_recent_blockhash()
|
||||
.expect("Failed to get blockhash");
|
||||
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), amount_to_drop, blockhash) {
|
||||
match request_airdrop_transaction(&drone_addr, &id.pubkey(), amount_to_drop, blockhash) {
|
||||
Ok(transaction) => {
|
||||
let signature = client.async_send_transaction(transaction).unwrap();
|
||||
|
||||
for _ in 0..30 {
|
||||
if let Ok(Some(_)) = client.get_signature_status_with_commitment(
|
||||
&signature,
|
||||
CommitmentConfig::recent(),
|
||||
) {
|
||||
if let Ok(Some(_)) = client.get_signature_status(&signature) {
|
||||
break;
|
||||
}
|
||||
sleep(Duration::from_millis(100));
|
||||
}
|
||||
if client
|
||||
.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent())
|
||||
.unwrap_or(0)
|
||||
>= amount
|
||||
{
|
||||
if client.get_balance(&id.pubkey()).unwrap_or(0) >= amount {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Error requesting airdrop: {:?} to addr: {:?} amount: {}",
|
||||
err, faucet_addr, amount
|
||||
err, drone_addr, amount
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@@ -1,14 +1,14 @@
|
||||
use clap::{crate_description, crate_name, value_t, App, Arg, ArgMatches};
|
||||
use clap::{crate_description, crate_name, crate_version, value_t, App, Arg, ArgMatches};
|
||||
use solana_core::gen_keys::GenKeys;
|
||||
use solana_faucet::faucet::FAUCET_PORT;
|
||||
use solana_sdk::signature::{read_keypair_file, Keypair};
|
||||
use solana_drone::drone::DRONE_PORT;
|
||||
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
|
||||
use std::net::SocketAddr;
|
||||
use std::process::exit;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct Config {
|
||||
pub entrypoint_addr: SocketAddr,
|
||||
pub faucet_addr: SocketAddr,
|
||||
pub drone_addr: SocketAddr,
|
||||
pub identity: Keypair,
|
||||
pub threads: usize,
|
||||
pub num_nodes: usize,
|
||||
@@ -27,7 +27,7 @@ impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
entrypoint_addr: SocketAddr::from(([127, 0, 0, 1], 8001)),
|
||||
faucet_addr: SocketAddr::from(([127, 0, 0, 1], FAUCET_PORT)),
|
||||
drone_addr: SocketAddr::from(([127, 0, 0, 1], DRONE_PORT)),
|
||||
identity: Keypair::new(),
|
||||
num_nodes: 1,
|
||||
threads: 4,
|
||||
@@ -44,10 +44,10 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
pub fn build_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
.version(version)
|
||||
.version(crate_version!())
|
||||
.arg(
|
||||
Arg::with_name("entrypoint")
|
||||
.short("n")
|
||||
@@ -59,14 +59,14 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
.help("Cluster entry point; defaults to 127.0.0.1:8001"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("faucet")
|
||||
Arg::with_name("drone")
|
||||
.short("d")
|
||||
.long("faucet")
|
||||
.long("drone")
|
||||
.value_name("HOST:PORT")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value("127.0.0.1:9900")
|
||||
.help("Location of the faucet; defaults to 127.0.0.1:9900"),
|
||||
.help("Location of the drone; defaults to 127.0.0.1:9900"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("identity")
|
||||
@@ -166,22 +166,20 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
|
||||
let mut args = Config::default();
|
||||
|
||||
args.entrypoint_addr = solana_net_utils::parse_host_port(
|
||||
matches.value_of("entrypoint").unwrap(),
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse entrypoint address: {}", e);
|
||||
exit(1)
|
||||
});
|
||||
|
||||
args.faucet_addr = solana_net_utils::parse_host_port(matches.value_of("faucet").unwrap())
|
||||
args.entrypoint_addr = solana_netutil::parse_host_port(matches.value_of("entrypoint").unwrap())
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse faucet address: {}", e);
|
||||
eprintln!("failed to parse entrypoint address: {}", e);
|
||||
exit(1)
|
||||
});
|
||||
|
||||
args.drone_addr = solana_netutil::parse_host_port(matches.value_of("drone").unwrap())
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse drone address: {}", e);
|
||||
exit(1)
|
||||
});
|
||||
|
||||
if matches.is_present("identity") {
|
||||
args.identity = read_keypair_file(matches.value_of("identity").unwrap())
|
||||
args.identity = read_keypair(matches.value_of("identity").unwrap())
|
||||
.expect("can't read client identity");
|
||||
} else {
|
||||
args.identity = {
|
||||
|
@@ -5,18 +5,18 @@ pub mod order_book;
|
||||
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
|
||||
use log::*;
|
||||
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_sdk::signature::Signer;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
solana_metrics::set_panic_hook("bench-exchange");
|
||||
|
||||
let matches = cli::build_args(solana_clap_utils::version!()).get_matches();
|
||||
let matches = cli::build_args().get_matches();
|
||||
let cli_config = cli::extract_args(&matches);
|
||||
|
||||
let cli::Config {
|
||||
entrypoint_addr,
|
||||
faucet_addr,
|
||||
drone_addr,
|
||||
identity,
|
||||
threads,
|
||||
num_nodes,
|
||||
@@ -54,7 +54,7 @@ fn main() {
|
||||
);
|
||||
} else {
|
||||
info!("Connecting to the cluster");
|
||||
let (nodes, _archivers) =
|
||||
let (nodes, _replicators) =
|
||||
discover_cluster(&entrypoint_addr, num_nodes).unwrap_or_else(|_| {
|
||||
panic!("Failed to discover nodes");
|
||||
});
|
||||
@@ -73,7 +73,7 @@ fn main() {
|
||||
const NUM_SIGNERS: u64 = 2;
|
||||
airdrop_lamports(
|
||||
&client,
|
||||
&faucet_addr,
|
||||
&drone_addr,
|
||||
&config.identity,
|
||||
fund_amount * (accounts_in_groups + 1) as u64 * NUM_SIGNERS,
|
||||
);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use itertools::EitherOrBoth::{Both, Left, Right};
|
||||
use itertools::Itertools;
|
||||
use log::*;
|
||||
use solana_exchange_program::exchange_state::*;
|
||||
use solana_exchange_api::exchange_state::*;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BinaryHeap;
|
||||
|
@@ -2,14 +2,13 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "0.23.6"
|
||||
version = "0.19.0-pre0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.19.0-pre0" }
|
||||
solana-logger = { path = "../logger", version = "0.19.0-pre0" }
|
||||
solana-netutil = { path = "../netutil", version = "0.19.0-pre0" }
|
||||
|
@@ -1,5 +1,7 @@
|
||||
use clap::{crate_description, crate_name, App, Arg};
|
||||
use solana_core::packet::{Packet, Packets, PacketsRecycler, PACKET_DATA_SIZE};
|
||||
use clap::{crate_description, crate_name, crate_version, App, Arg};
|
||||
use solana_core::packet::PacketsRecycler;
|
||||
use solana_core::packet::{Packet, Packets, BLOB_SIZE, PACKET_DATA_SIZE};
|
||||
use solana_core::result::Result;
|
||||
use solana_core::streamer::{receiver, PacketReceiver};
|
||||
use std::cmp::max;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||
@@ -7,7 +9,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::Arc;
|
||||
use std::thread::sleep;
|
||||
use std::thread::{spawn, JoinHandle, Result};
|
||||
use std::thread::{spawn, JoinHandle};
|
||||
use std::time::Duration;
|
||||
use std::time::SystemTime;
|
||||
|
||||
@@ -27,7 +29,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
|
||||
let mut num = 0;
|
||||
for p in &msgs.packets {
|
||||
let a = p.meta.addr();
|
||||
assert!(p.meta.size < PACKET_DATA_SIZE);
|
||||
assert!(p.meta.size < BLOB_SIZE);
|
||||
send.send_to(&p.data[..p.meta.size], &a).unwrap();
|
||||
num += 1;
|
||||
}
|
||||
@@ -52,7 +54,7 @@ fn main() -> Result<()> {
|
||||
|
||||
let matches = App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
.version(solana_clap_utils::version!())
|
||||
.version(crate_version!())
|
||||
.arg(
|
||||
Arg::with_name("num-recv-sockets")
|
||||
.long("num-recv-sockets")
|
||||
@@ -75,7 +77,7 @@ fn main() -> Result<()> {
|
||||
let mut read_threads = Vec::new();
|
||||
let recycler = PacketsRecycler::default();
|
||||
for _ in 0..num_sockets {
|
||||
let read = solana_net_utils::bind_to(port, false).unwrap();
|
||||
let read = solana_netutil::bind_to(port, false).unwrap();
|
||||
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
|
||||
|
||||
addr = read.local_addr().unwrap();
|
||||
|
@@ -2,38 +2,34 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "0.23.6"
|
||||
version = "0.19.0-pre0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.2.1"
|
||||
bincode = "1.1.4"
|
||||
clap = "2.33.0"
|
||||
log = "0.4.8"
|
||||
rayon = "1.2.0"
|
||||
serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.44"
|
||||
serde_yaml = "0.8.11"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-genesis = { path = "../genesis", version = "0.23.6" }
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.6" }
|
||||
solana-librapay = { path = "../programs/librapay", version = "0.23.6", optional = true }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-measure = { path = "../measure", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.6", optional = true }
|
||||
serde = "1.0.101"
|
||||
serde_derive = "1.0.101"
|
||||
serde_json = "1.0.40"
|
||||
serde_yaml = "0.8.9"
|
||||
solana-core = { path = "../core", version = "0.19.0-pre0" }
|
||||
solana-genesis = { path = "../genesis", version = "0.19.0-pre0" }
|
||||
solana-client = { path = "../client", version = "0.19.0-pre0" }
|
||||
solana-drone = { path = "../drone", version = "0.19.0-pre0" }
|
||||
solana-librapay-api = { path = "../programs/librapay_api", version = "0.19.0-pre0" }
|
||||
solana-logger = { path = "../logger", version = "0.19.0-pre0" }
|
||||
solana-metrics = { path = "../metrics", version = "0.19.0-pre0" }
|
||||
solana-measure = { path = "../measure", version = "0.19.0-pre0" }
|
||||
solana-netutil = { path = "../netutil", version = "0.19.0-pre0" }
|
||||
solana-runtime = { path = "../runtime", version = "0.19.0-pre0" }
|
||||
solana-sdk = { path = "../sdk", version = "0.19.0-pre0" }
|
||||
solana-move-loader-program = { path = "../programs/move_loader_program", version = "0.19.0-pre0" }
|
||||
solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.19.0-pre0" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.3.2"
|
||||
serial_test_derive = "0.3.1"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "0.23.6" }
|
||||
|
||||
[features]
|
||||
move = ["solana-librapay", "solana-move-loader-program"]
|
||||
serial_test = "0.2.0"
|
||||
serial_test_derive = "0.2.0"
|
||||
|
@@ -1,28 +1,29 @@
|
||||
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
||||
use solana_faucet::faucet::FAUCET_PORT;
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::signature::{read_keypair_file, Keypair};
|
||||
use std::{net::SocketAddr, process::exit, time::Duration};
|
||||
use std::net::SocketAddr;
|
||||
use std::process::exit;
|
||||
use std::time::Duration;
|
||||
|
||||
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;
|
||||
use clap::{crate_description, crate_name, crate_version, App, Arg, ArgMatches};
|
||||
use solana_drone::drone::DRONE_PORT;
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
|
||||
|
||||
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = 64 * 1024;
|
||||
|
||||
/// Holds the configuration for a single run of the benchmark
|
||||
pub struct Config {
|
||||
pub entrypoint_addr: SocketAddr,
|
||||
pub faucet_addr: SocketAddr,
|
||||
pub drone_addr: SocketAddr,
|
||||
pub id: Keypair,
|
||||
pub threads: usize,
|
||||
pub num_nodes: usize,
|
||||
pub duration: Duration,
|
||||
pub tx_count: usize,
|
||||
pub keypair_multiplier: usize,
|
||||
pub thread_batch_sleep_ms: usize,
|
||||
pub sustained: bool,
|
||||
pub client_ids_and_stake_file: String,
|
||||
pub write_to_client_file: bool,
|
||||
pub read_from_client_file: bool,
|
||||
pub target_lamports_per_signature: u64,
|
||||
pub multi_client: bool,
|
||||
pub use_move: bool,
|
||||
pub num_lamports_per_account: u64,
|
||||
}
|
||||
@@ -31,20 +32,18 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
entrypoint_addr: SocketAddr::from(([127, 0, 0, 1], 8001)),
|
||||
faucet_addr: SocketAddr::from(([127, 0, 0, 1], FAUCET_PORT)),
|
||||
drone_addr: SocketAddr::from(([127, 0, 0, 1], DRONE_PORT)),
|
||||
id: Keypair::new(),
|
||||
threads: 4,
|
||||
num_nodes: 1,
|
||||
duration: Duration::new(std::u64::MAX, 0),
|
||||
tx_count: 50_000,
|
||||
keypair_multiplier: 8,
|
||||
thread_batch_sleep_ms: 1000,
|
||||
tx_count: 500_000,
|
||||
thread_batch_sleep_ms: 0,
|
||||
sustained: false,
|
||||
client_ids_and_stake_file: String::new(),
|
||||
write_to_client_file: false,
|
||||
read_from_client_file: false,
|
||||
target_lamports_per_signature: FeeCalculator::default().target_lamports_per_signature,
|
||||
multi_client: true,
|
||||
use_move: false,
|
||||
num_lamports_per_account: NUM_LAMPORTS_PER_ACCOUNT_DEFAULT,
|
||||
}
|
||||
@@ -52,9 +51,9 @@ impl Default for Config {
|
||||
}
|
||||
|
||||
/// Defines and builds the CLI args for a run of the benchmark
|
||||
pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
pub fn build_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new(crate_name!()).about(crate_description!())
|
||||
.version(version)
|
||||
.version(crate_version!())
|
||||
.arg(
|
||||
Arg::with_name("entrypoint")
|
||||
.short("n")
|
||||
@@ -64,12 +63,12 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
.help("Rendezvous with the cluster at this entry point; defaults to 127.0.0.1:8001"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("faucet")
|
||||
Arg::with_name("drone")
|
||||
.short("d")
|
||||
.long("faucet")
|
||||
.long("drone")
|
||||
.value_name("HOST:PORT")
|
||||
.takes_value(true)
|
||||
.help("Location of the faucet; defaults to entrypoint:FAUCET_PORT"),
|
||||
.help("Location of the drone; defaults to entrypoint:DRONE_PORT"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("identity")
|
||||
@@ -112,11 +111,6 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
.long("use-move")
|
||||
.help("Use Move language transactions to perform transfers."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no-multi-client")
|
||||
.long("no-multi-client")
|
||||
.help("Disable multi-client support, only transact with the entrypoint."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tx_count")
|
||||
.long("tx_count")
|
||||
@@ -124,13 +118,6 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
.takes_value(true)
|
||||
.help("Number of transactions to send per batch")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("keypair_multiplier")
|
||||
.long("keypair-multiplier")
|
||||
.value_name("NUM")
|
||||
.takes_value(true)
|
||||
.help("Multiply by transaction count to determine number of keypairs to create")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("thread-batch-sleep-ms")
|
||||
.short("z")
|
||||
@@ -183,21 +170,21 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
|
||||
let mut args = Config::default();
|
||||
|
||||
if let Some(addr) = matches.value_of("entrypoint") {
|
||||
args.entrypoint_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| {
|
||||
args.entrypoint_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse entrypoint address: {}", e);
|
||||
exit(1)
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(addr) = matches.value_of("faucet") {
|
||||
args.faucet_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse faucet address: {}", e);
|
||||
if let Some(addr) = matches.value_of("drone") {
|
||||
args.drone_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse drone address: {}", e);
|
||||
exit(1)
|
||||
});
|
||||
}
|
||||
|
||||
if matches.is_present("identity") {
|
||||
args.id = read_keypair_file(matches.value_of("identity").unwrap())
|
||||
args.id = read_keypair(matches.value_of("identity").unwrap())
|
||||
.expect("can't read client identity");
|
||||
}
|
||||
|
||||
@@ -217,15 +204,7 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
|
||||
}
|
||||
|
||||
if let Some(s) = matches.value_of("tx_count") {
|
||||
args.tx_count = s.to_string().parse().expect("can't parse tx_count");
|
||||
}
|
||||
|
||||
if let Some(s) = matches.value_of("keypair_multiplier") {
|
||||
args.keypair_multiplier = s
|
||||
.to_string()
|
||||
.parse()
|
||||
.expect("can't parse keypair-multiplier");
|
||||
assert!(args.keypair_multiplier >= 2);
|
||||
args.tx_count = s.to_string().parse().expect("can't parse tx_account");
|
||||
}
|
||||
|
||||
if let Some(t) = matches.value_of("thread-batch-sleep-ms") {
|
||||
@@ -253,7 +232,6 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
|
||||
}
|
||||
|
||||
args.use_move = matches.is_present("use-move");
|
||||
args.multi_client = !matches.is_present("no-multi-client");
|
||||
|
||||
if let Some(v) = matches.value_of("num_lamports_per_account") {
|
||||
args.num_lamports_per_account = v.to_string().parse().expect("can't parse lamports");
|
||||
|
@@ -1,47 +1,45 @@
|
||||
use log::*;
|
||||
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs, generate_keypairs};
|
||||
use solana_bench_tps::cli;
|
||||
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||
use solana_genesis::Base64Account;
|
||||
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_genesis::PrimordialAccountDetails;
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::system_program;
|
||||
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
/// Number of signatures for all transactions in ~1 week at ~100K TPS
|
||||
pub const NUM_SIGNATURES_FOR_TXS: u64 = 100_000 * 60 * 60 * 24 * 7;
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup_with_default("solana=info");
|
||||
solana_logger::setup_with_filter("solana=info");
|
||||
solana_metrics::set_panic_hook("bench-tps");
|
||||
|
||||
let matches = cli::build_args(solana_clap_utils::version!()).get_matches();
|
||||
let matches = cli::build_args().get_matches();
|
||||
let cli_config = cli::extract_args(&matches);
|
||||
|
||||
let cli::Config {
|
||||
entrypoint_addr,
|
||||
faucet_addr,
|
||||
drone_addr,
|
||||
id,
|
||||
num_nodes,
|
||||
tx_count,
|
||||
keypair_multiplier,
|
||||
client_ids_and_stake_file,
|
||||
write_to_client_file,
|
||||
read_from_client_file,
|
||||
target_lamports_per_signature,
|
||||
use_move,
|
||||
multi_client,
|
||||
num_lamports_per_account,
|
||||
..
|
||||
} = &cli_config;
|
||||
|
||||
let keypair_count = *tx_count * keypair_multiplier;
|
||||
if *write_to_client_file {
|
||||
info!("Generating {} keypairs", keypair_count);
|
||||
let (keypairs, _) = generate_keypairs(&id, keypair_count as u64);
|
||||
let (keypairs, _) = generate_keypairs(&id, *tx_count as u64 * 2);
|
||||
let num_accounts = keypairs.len() as u64;
|
||||
let max_fee =
|
||||
FeeCalculator::new(*target_lamports_per_signature, 0).max_lamports_per_signature;
|
||||
let max_fee = FeeCalculator::new(*target_lamports_per_signature).max_lamports_per_signature;
|
||||
let num_lamports_per_account = (num_accounts - 1 + NUM_SIGNATURES_FOR_TXS * max_fee)
|
||||
/ num_accounts
|
||||
+ num_lamports_per_account;
|
||||
@@ -49,7 +47,7 @@ fn main() {
|
||||
keypairs.iter().for_each(|keypair| {
|
||||
accounts.insert(
|
||||
serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap(),
|
||||
Base64Account {
|
||||
PrimordialAccountDetails {
|
||||
balance: num_lamports_per_account,
|
||||
executable: false,
|
||||
owner: system_program::id().to_string(),
|
||||
@@ -58,7 +56,6 @@ fn main() {
|
||||
);
|
||||
});
|
||||
|
||||
info!("Writing {}", client_ids_and_stake_file);
|
||||
let serialized = serde_yaml::to_string(&accounts).unwrap();
|
||||
let path = Path::new(&client_ids_and_stake_file);
|
||||
let mut file = File::create(path).unwrap();
|
||||
@@ -66,33 +63,29 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Connecting to the cluster");
|
||||
let (nodes, _archivers) =
|
||||
println!("Connecting to the cluster");
|
||||
let (nodes, _replicators) =
|
||||
discover_cluster(&entrypoint_addr, *num_nodes).unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} nodes: {:?}", num_nodes, err);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
let client = if *multi_client {
|
||||
let (client, num_clients) = get_multi_client(&nodes);
|
||||
if nodes.len() < num_clients {
|
||||
eprintln!(
|
||||
"Error: Insufficient nodes discovered. Expecting {} or more",
|
||||
num_nodes
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
Arc::new(client)
|
||||
} else {
|
||||
Arc::new(get_client(&nodes))
|
||||
};
|
||||
let (client, num_clients) = get_multi_client(&nodes);
|
||||
|
||||
let (keypairs, move_keypairs) = if *read_from_client_file && !use_move {
|
||||
if nodes.len() < num_clients {
|
||||
eprintln!(
|
||||
"Error: Insufficient nodes discovered. Expecting {} or more",
|
||||
num_nodes
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let (keypairs, move_keypairs, keypair_balance) = if *read_from_client_file && !use_move {
|
||||
let path = Path::new(&client_ids_and_stake_file);
|
||||
let file = File::open(path).unwrap();
|
||||
|
||||
info!("Reading {}", client_ids_and_stake_file);
|
||||
let accounts: HashMap<String, Base64Account> = serde_yaml::from_reader(file).unwrap();
|
||||
let accounts: HashMap<String, PrimordialAccountDetails> =
|
||||
serde_yaml::from_reader(file).unwrap();
|
||||
let mut keypairs = vec![];
|
||||
let mut last_balance = 0;
|
||||
|
||||
@@ -103,27 +96,17 @@ fn main() {
|
||||
keypairs.push(Keypair::from_bytes(&bytes).unwrap());
|
||||
last_balance = primordial_account.balance;
|
||||
});
|
||||
|
||||
if keypairs.len() < keypair_count {
|
||||
eprintln!(
|
||||
"Expected {} accounts in {}, only received {} (--tx_count mismatch?)",
|
||||
keypair_count,
|
||||
client_ids_and_stake_file,
|
||||
keypairs.len(),
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
// Sort keypairs so that do_bench_tps() uses the same subset of accounts for each run.
|
||||
// This prevents the amount of storage needed for bench-tps accounts from creeping up
|
||||
// across multiple runs.
|
||||
keypairs.sort_by(|x, y| x.pubkey().to_string().cmp(&y.pubkey().to_string()));
|
||||
(keypairs, None)
|
||||
(keypairs, None, last_balance)
|
||||
} else {
|
||||
generate_and_fund_keypairs(
|
||||
client.clone(),
|
||||
Some(*faucet_addr),
|
||||
&client,
|
||||
Some(*drone_addr),
|
||||
&id,
|
||||
keypair_count,
|
||||
*tx_count,
|
||||
*num_lamports_per_account,
|
||||
*use_move,
|
||||
)
|
||||
@@ -133,5 +116,11 @@ fn main() {
|
||||
})
|
||||
};
|
||||
|
||||
do_bench_tps(client, cli_config, keypairs, move_keypairs);
|
||||
do_bench_tps(
|
||||
vec![client],
|
||||
cli_config,
|
||||
keypairs,
|
||||
keypair_balance,
|
||||
move_keypairs,
|
||||
);
|
||||
}
|
||||
|
15
book/art/consensus.msc
Normal file
@@ -0,0 +1,15 @@
|
||||
msc {
|
||||
client,leader,verifier_a,verifier_b,verifier_c;
|
||||
|
||||
client=>leader [ label = "SUBMIT" ] ;
|
||||
leader=>client [ label = "CONFIRMED" ] ;
|
||||
leader=>verifier_a [ label = "CONFIRMED" ] ;
|
||||
leader=>verifier_b [ label = "CONFIRMED" ] ;
|
||||
leader=>verifier_c [ label = "CONFIRMED" ] ;
|
||||
verifier_a=>leader [ label = "VERIFIED" ] ;
|
||||
verifier_b=>leader [ label = "VERIFIED" ] ;
|
||||
leader=>client [ label = "FINALIZED" ] ;
|
||||
leader=>verifier_a [ label = "FINALIZED" ] ;
|
||||
leader=>verifier_b [ label = "FINALIZED" ] ;
|
||||
leader=>verifier_c [ label = "FINALIZED" ] ;
|
||||
}
|
@@ -24,7 +24,7 @@ msc {
|
||||
... ;
|
||||
Validator abox Validator [label="\nmax\nlockout\n"];
|
||||
|||;
|
||||
Cluster box Cluster [label="credits redeemed (at epoch)"];
|
||||
|
||||
StakerX => Cluster [label="StakeState::RedeemCredits()"];
|
||||
StakerY => Cluster [label="StakeState::RedeemCredits()"] ;
|
||||
|
||||
}
|
||||
|
@@ -1,19 +0,0 @@
|
||||
+----------+
|
||||
| Bank-Hash|
|
||||
+----------+
|
||||
^
|
||||
|
|
||||
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||
: :
|
||||
: +--------------+ +-------------+ :
|
||||
: Hash( | Accounts-Hash| + | Block-Merkle| ) :
|
||||
: +--------------+ +-------------+ :
|
||||
: ^ :
|
||||
+~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||
|
|
||||
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||
: +---------------+ +---------------+ +---------------+ :
|
||||
: Hash( | Hash(Account1)| + | Hash(Account2)| + ... + | Hash(AccountN)| ) :
|
||||
: +---------------+ +---------------+ +---------------+ :
|
||||
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||
|
18
book/art/spv-bank-merkle.bob
Normal file
@@ -0,0 +1,18 @@
|
||||
+------------+
|
||||
| Bank-Merkle|
|
||||
+------------+
|
||||
^ ^
|
||||
/ \
|
||||
+-----------------+ +-------------+
|
||||
| Bank-Diff-Merkle| | Block-Merkle|
|
||||
+-----------------+ +-------------+
|
||||
^ ^
|
||||
/ \
|
||||
+------+ +--------------------------+
|
||||
| Hash | | Previous Bank-Diff-Merkle|
|
||||
+------+ +--------------------------+
|
||||
^ ^
|
||||
/ \
|
||||
+---------------+ +---------------+
|
||||
| Hash(Account1)| | Hash(Account2)|
|
||||
+---------------+ +---------------+
|
@@ -7,7 +7,7 @@
|
||||
| TVU | |
|
||||
| | |
|
||||
| .-------. .------------. .----+---. .---------. |
|
||||
.------------. | | Shred | | Retransmit | | Replay | | Storage | |
|
||||
.------------. | | Blob | | Retransmit | | Replay | | Storage | |
|
||||
| Upstream +----->| Fetch +-->| Stage +-->| Stage +-->| Stage | |
|
||||
| Validators | | | Stage | | | | | | | |
|
||||
`------------` | `-------` `----+-------` `----+---` `---------` |
|
||||
|
@@ -1,30 +1,30 @@
|
||||
.---------------------------------------.
|
||||
| Validator |
|
||||
| |
|
||||
.--------. | .-------------------. |
|
||||
| |---->| | |
|
||||
| Client | | | JSON RPC Service | |
|
||||
| |<----| | |
|
||||
`----+---` | `-------------------` |
|
||||
| | ^ |
|
||||
| | | .----------------. | .------------------.
|
||||
| | | | Gossip Service |<-----------| Validators |
|
||||
| | | `----------------` | | |
|
||||
| | | ^ | | |
|
||||
| | | | | | .------------. |
|
||||
| | .---+---. .----+---. .------------. | | | | |
|
||||
| | | Bank |<-+ Replay | | ShredFetch |<------+ Upstream | |
|
||||
| | | Forks | | Stage | | Stage | | | | Validators | |
|
||||
| | `-------` `--------` `--+---------` | | | | |
|
||||
| | ^ ^ | | | `------------` |
|
||||
| | | | v | | |
|
||||
| | | .--+---------. | | |
|
||||
| | | | Blockstore | | | |
|
||||
| | | `------------` | | .------------. |
|
||||
| | | ^ | | | | |
|
||||
| | | | | | | Downstream | |
|
||||
| | .--+--. .-------+---. | | | Validators | |
|
||||
`-------->| TPU +---->| Broadcast +---------------->| | |
|
||||
| `-----` | Stage | | | `------------` |
|
||||
| `-----------` | `------------------`
|
||||
`---------------------------------------`
|
||||
.--------------------------------------.
|
||||
| Validator |
|
||||
| |
|
||||
.--------. | .-------------------. |
|
||||
| |---->| | |
|
||||
| Client | | | JSON RPC Service | |
|
||||
| |<----| | |
|
||||
`----+---` | `-------------------` |
|
||||
| | ^ |
|
||||
| | | .----------------. | .------------------.
|
||||
| | | | Gossip Service |<----------| Validators |
|
||||
| | | `----------------` | | |
|
||||
| | | ^ | | |
|
||||
| | | | | | .------------. |
|
||||
| | .---+---. .----+---. .-----------. | | | | |
|
||||
| | | Bank |<-+ Replay | | BlobFetch |<------+ Upstream | |
|
||||
| | | Forks | | Stage | | Stage | | | | Validators | |
|
||||
| | `-------` `--------` `--+--------` | | | | |
|
||||
| | ^ ^ | | | `------------` |
|
||||
| | | | v | | |
|
||||
| | | .--+--------. | | |
|
||||
| | | | Blocktree | | | |
|
||||
| | | `-----------` | | .------------. |
|
||||
| | | ^ | | | | |
|
||||
| | | | | | | Downstream | |
|
||||
| | .--+--. .-------+---. | | | Validators | |
|
||||
`-------->| TPU +---->| Broadcast +--------------->| | |
|
||||
| `-----` | Stage | | | `------------` |
|
||||
| `-----------` | `------------------`
|
||||
`--------------------------------------`
|
||||
|
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
usage=$(cargo -q run -p solana-cli -- -C ~/.foo --help | sed 's|'"$HOME"'|~|g')
|
||||
|
||||
out=${1:-src/cli/usage.md}
|
||||
|
||||
cat src/cli/.usage.md.header > "$out"
|
||||
|
||||
section() {
|
||||
declare mark=${2:-"###"}
|
||||
declare section=$1
|
||||
read -r name rest <<<"$section"
|
||||
|
||||
printf '%s %s
|
||||
' "$mark" "$name"
|
||||
printf '```text
|
||||
%s
|
||||
```
|
||||
|
||||
' "$section"
|
||||
}
|
||||
|
||||
section "$usage" >> "$out"
|
||||
|
||||
in_subcommands=0
|
||||
while read -r subcommand rest; do
|
||||
[[ $subcommand == "SUBCOMMANDS:" ]] && in_subcommands=1 && continue
|
||||
if ((in_subcommands)); then
|
||||
section "$(cargo -q run -p solana-cli -- help "$subcommand" | sed 's|'"$HOME"'|~|g')" "####" >> "$out"
|
||||
fi
|
||||
done <<<"$usage">>"$out"
|
11
book/build-svg.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
make -j"$(nproc)" -B svg
|
||||
|
||||
if [[ -n $CI ]]; then
|
||||
# In CI confirm that no svgs need to be built
|
||||
git diff --exit-code
|
||||
fi
|
@@ -3,14 +3,4 @@ set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# md check
|
||||
find src -name '*.md' -a \! -name SUMMARY.md |
|
||||
while read -r file; do
|
||||
if ! grep -q '('"${file#src/}"')' src/SUMMARY.md; then
|
||||
echo "Error: $file missing from SUMMARY.md"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
make -j"$(nproc)" test
|
||||
|
@@ -1,6 +1,6 @@
|
||||
BOB_SRCS=$(wildcard art/*.bob)
|
||||
MSC_SRCS=$(wildcard art/*.msc)
|
||||
MD_SRCS=$(wildcard src/*.md src/*/*.md)
|
||||
MD_SRCS=$(wildcard src/*.md)
|
||||
|
||||
SVG_IMGS=$(BOB_SRCS:art/%.bob=src/.gitbook/assets/%.svg) $(MSC_SRCS:art/%.msc=src/.gitbook/assets/%.svg)
|
||||
|
||||
|
183
book/src/.gitbook/assets/data-plane-fanout.svg
Normal file
@@ -0,0 +1,183 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="304" width="544" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="304" width="544" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="4" x2="4" y1="8" y2="184"/>
|
||||
<line x1="4" x2="540" y1="8" y2="8"/>
|
||||
<line x1="4" x2="540" y1="184" y2="184"/>
|
||||
<line x1="540" x2="540" y1="8" y2="184"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="28" x2="28" y1="232" y2="296"/>
|
||||
<line x1="28" x2="108" y1="232" y2="232"/>
|
||||
<line x1="28" x2="196" y1="296" y2="296"/>
|
||||
<line x1="108" x2="164" y1="232" y2="232"/>
|
||||
<line x1="164" x2="196" y1="232" y2="232"/>
|
||||
<line x1="196" x2="196" y1="232" y2="296"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="36" x2="36" y1="40" y2="104"/>
|
||||
<line x1="36" x2="180" y1="40" y2="40"/>
|
||||
<line x1="36" x2="108" y1="104" y2="104"/>
|
||||
<line x1="108" x2="108" y1="104" y2="176"/>
|
||||
<line x1="108" x2="124" y1="104" y2="104"/>
|
||||
<line x1="124" x2="124" y1="104" y2="136"/>
|
||||
<line x1="124" x2="180" y1="104" y2="104"/>
|
||||
<line x1="124" x2="364" y1="136" y2="136"/>
|
||||
<line x1="180" x2="180" y1="40" y2="56"/>
|
||||
<line x1="180" x2="180" y1="56" y2="88"/>
|
||||
<line marker-end="url(#triangle)" x1="180" x2="356" y1="56" y2="56"/>
|
||||
<line x1="180" x2="180" y1="88" y2="104"/>
|
||||
<line x1="180" x2="184" y1="88" y2="88"/>
|
||||
<line x1="364" x2="364" y1="136" y2="152"/>
|
||||
<line x1="364" x2="364" y1="152" y2="176"/>
|
||||
<line x1="364" x2="420" y1="152" y2="152"/>
|
||||
<line x1="420" x2="420" y1="104" y2="152"/>
|
||||
<line x1="420" x2="436" y1="104" y2="104"/>
|
||||
<line x1="436" x2="436" y1="104" y2="176"/>
|
||||
<line x1="436" x2="508" y1="104" y2="104"/>
|
||||
<line x1="508" x2="508" y1="40" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="108" x2="108" y1="192" y2="220"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="164" x2="164" y1="152" y2="176"/>
|
||||
<line x1="164" x2="364" y1="152" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="164" x2="164" y1="192" y2="220"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="192" x2="188" y1="88" y2="88"/>
|
||||
<line x1="192" x2="364" y1="88" y2="88"/>
|
||||
<line x1="364" x2="364" y1="56" y2="88"/>
|
||||
<line x1="364" x2="364" y1="88" y2="104"/>
|
||||
<line x1="364" x2="420" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="348" x2="348" y1="232" y2="296"/>
|
||||
<line x1="348" x2="364" y1="232" y2="232"/>
|
||||
<line x1="348" x2="516" y1="296" y2="296"/>
|
||||
<line x1="364" x2="436" y1="232" y2="232"/>
|
||||
<line x1="436" x2="516" y1="232" y2="232"/>
|
||||
<line x1="516" x2="516" y1="232" y2="296"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="364" x2="364" y1="40" y2="56"/>
|
||||
<line x1="364" x2="508" y1="40" y2="40"/>
|
||||
<line x1="364" x2="360" y1="56" y2="56"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="364" x2="364" y1="192" y2="220"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="436" x2="436" y1="192" y2="220"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="57" y="268">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="65" y="76">
|
||||
Validator
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="76">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="161" y="268">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="217" y="44">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="321" y="44">
|
||||
0
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="377" y="268">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="393" y="76">
|
||||
Validator
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="76">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="481" y="268">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
322
book/src/.gitbook/assets/data-plane-neighborhood.svg
Normal file
@@ -0,0 +1,322 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="400" width="856" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="400" width="856" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="4" x2="4" y1="8" y2="152"/>
|
||||
<line x1="4" x2="852" y1="8" y2="8"/>
|
||||
<line x1="4" x2="852" y1="152" y2="152"/>
|
||||
<line x1="852" x2="852" y1="8" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="4" x2="4" y1="248" y2="392"/>
|
||||
<line x1="4" x2="852" y1="248" y2="248"/>
|
||||
<line x1="4" x2="852" y1="392" y2="392"/>
|
||||
<line x1="852" x2="852" y1="248" y2="392"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="60" x2="60" y1="56" y2="120"/>
|
||||
<line x1="60" x2="196" y1="56" y2="56"/>
|
||||
<line x1="60" x2="84" y1="120" y2="120"/>
|
||||
<line x1="84" x2="84" y1="120" y2="144"/>
|
||||
<line x1="84" x2="196" y1="120" y2="120"/>
|
||||
<line x1="196" x2="196" y1="56" y2="72"/>
|
||||
<line x1="196" x2="196" y1="72" y2="104"/>
|
||||
<line marker-end="url(#triangle)" x1="196" x2="252" y1="72" y2="72"/>
|
||||
<line x1="196" x2="196" y1="104" y2="120"/>
|
||||
<line x1="196" x2="200" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="60" x2="60" y1="296" y2="360"/>
|
||||
<line x1="60" x2="84" y1="296" y2="296"/>
|
||||
<line x1="60" x2="196" y1="360" y2="360"/>
|
||||
<line x1="84" x2="196" y1="296" y2="296"/>
|
||||
<line x1="196" x2="196" y1="296" y2="312"/>
|
||||
<line x1="196" x2="196" y1="312" y2="344"/>
|
||||
<line marker-end="url(#triangle)" x1="196" x2="252" y1="312" y2="312"/>
|
||||
<line x1="196" x2="196" y1="344" y2="360"/>
|
||||
<line x1="196" x2="200" y1="344" y2="344"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="84" x2="84" y1="160" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="84" x2="84" y1="256" y2="284"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="208" x2="204" y1="104" y2="104"/>
|
||||
<line x1="208" x2="260" y1="104" y2="104"/>
|
||||
<line x1="260" x2="260" y1="72" y2="104"/>
|
||||
<line x1="260" x2="260" y1="104" y2="120"/>
|
||||
<line x1="260" x2="284" y1="120" y2="120"/>
|
||||
<line x1="284" x2="284" y1="120" y2="144"/>
|
||||
<line x1="284" x2="396" y1="120" y2="120"/>
|
||||
<line x1="396" x2="396" y1="104" y2="120"/>
|
||||
<line x1="396" x2="400" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="208" x2="204" y1="344" y2="344"/>
|
||||
<line x1="208" x2="260" y1="344" y2="344"/>
|
||||
<line x1="260" x2="260" y1="312" y2="344"/>
|
||||
<line x1="260" x2="260" y1="344" y2="360"/>
|
||||
<line x1="260" x2="396" y1="360" y2="360"/>
|
||||
<line x1="396" x2="396" y1="344" y2="360"/>
|
||||
<line x1="396" x2="400" y1="344" y2="344"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="260" x2="260" y1="56" y2="72"/>
|
||||
<line x1="260" x2="396" y1="56" y2="56"/>
|
||||
<line x1="260" x2="256" y1="72" y2="72"/>
|
||||
<line x1="396" x2="396" y1="56" y2="72"/>
|
||||
<line x1="396" x2="396" y1="72" y2="104"/>
|
||||
<line marker-end="url(#triangle)" x1="396" x2="452" y1="72" y2="72"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="260" x2="260" y1="296" y2="312"/>
|
||||
<line x1="260" x2="284" y1="296" y2="296"/>
|
||||
<line x1="260" x2="256" y1="312" y2="312"/>
|
||||
<line x1="284" x2="396" y1="296" y2="296"/>
|
||||
<line x1="396" x2="396" y1="296" y2="312"/>
|
||||
<line x1="396" x2="396" y1="312" y2="344"/>
|
||||
<line marker-end="url(#triangle)" x1="396" x2="452" y1="312" y2="312"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="284" x2="284" y1="160" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="284" x2="284" y1="256" y2="284"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="408" x2="404" y1="104" y2="104"/>
|
||||
<line x1="408" x2="460" y1="104" y2="104"/>
|
||||
<line x1="460" x2="460" y1="72" y2="104"/>
|
||||
<line x1="460" x2="460" y1="104" y2="120"/>
|
||||
<line x1="460" x2="508" y1="120" y2="120"/>
|
||||
<line x1="508" x2="508" y1="120" y2="144"/>
|
||||
<line x1="508" x2="596" y1="120" y2="120"/>
|
||||
<line x1="596" x2="596" y1="104" y2="120"/>
|
||||
<line x1="596" x2="600" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="408" x2="404" y1="344" y2="344"/>
|
||||
<line x1="408" x2="460" y1="344" y2="344"/>
|
||||
<line x1="460" x2="460" y1="312" y2="344"/>
|
||||
<line x1="460" x2="460" y1="344" y2="360"/>
|
||||
<line x1="460" x2="596" y1="360" y2="360"/>
|
||||
<line x1="596" x2="596" y1="344" y2="360"/>
|
||||
<line x1="596" x2="600" y1="344" y2="344"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="460" x2="460" y1="56" y2="72"/>
|
||||
<line x1="460" x2="596" y1="56" y2="56"/>
|
||||
<line x1="460" x2="456" y1="72" y2="72"/>
|
||||
<line x1="596" x2="596" y1="56" y2="72"/>
|
||||
<line x1="596" x2="596" y1="72" y2="104"/>
|
||||
<line marker-end="url(#triangle)" x1="596" x2="652" y1="72" y2="72"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="460" x2="460" y1="296" y2="312"/>
|
||||
<line x1="460" x2="508" y1="296" y2="296"/>
|
||||
<line x1="460" x2="456" y1="312" y2="312"/>
|
||||
<line x1="508" x2="596" y1="296" y2="296"/>
|
||||
<line x1="596" x2="596" y1="296" y2="312"/>
|
||||
<line x1="596" x2="596" y1="312" y2="344"/>
|
||||
<line marker-end="url(#triangle)" x1="596" x2="652" y1="312" y2="312"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="508" x2="508" y1="160" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="508" x2="508" y1="256" y2="284"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="608" x2="604" y1="104" y2="104"/>
|
||||
<line x1="608" x2="660" y1="104" y2="104"/>
|
||||
<line x1="660" x2="660" y1="72" y2="104"/>
|
||||
<line x1="660" x2="660" y1="104" y2="120"/>
|
||||
<line x1="660" x2="684" y1="120" y2="120"/>
|
||||
<line x1="684" x2="684" y1="120" y2="144"/>
|
||||
<line x1="684" x2="796" y1="120" y2="120"/>
|
||||
<line x1="796" x2="796" y1="56" y2="120"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="608" x2="604" y1="344" y2="344"/>
|
||||
<line x1="608" x2="660" y1="344" y2="344"/>
|
||||
<line x1="660" x2="660" y1="312" y2="344"/>
|
||||
<line x1="660" x2="660" y1="344" y2="360"/>
|
||||
<line x1="660" x2="796" y1="360" y2="360"/>
|
||||
<line x1="796" x2="796" y1="296" y2="360"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="660" x2="660" y1="56" y2="72"/>
|
||||
<line x1="660" x2="796" y1="56" y2="56"/>
|
||||
<line x1="660" x2="656" y1="72" y2="72"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="660" x2="660" y1="296" y2="312"/>
|
||||
<line x1="660" x2="684" y1="296" y2="296"/>
|
||||
<line x1="660" x2="656" y1="312" y2="312"/>
|
||||
<line x1="684" x2="796" y1="296" y2="296"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="684" x2="684" y1="160" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="684" x2="684" y1="256" y2="284"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="89" y="92">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="89" y="332">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="161" y="92">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="161" y="332">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="289" y="92">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="289" y="332">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="353" y="28">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="353" y="268">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="361" y="92">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="361" y="332">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="457" y="28">
|
||||
Above
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="457" y="268">
|
||||
Below
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="489" y="92">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="489" y="332">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="561" y="92">
|
||||
3
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="561" y="332">
|
||||
3
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="689" y="92">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="689" y="332">
|
||||
Neighbor
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="761" y="92">
|
||||
4
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="761" y="332">
|
||||
4
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.3 KiB |
138
book/src/.gitbook/assets/data-plane-seeding.svg
Normal file
@@ -0,0 +1,138 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="240" width="544" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="240" width="544" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="4" x2="4" y1="104" y2="232"/>
|
||||
<line x1="4" x2="108" y1="104" y2="104"/>
|
||||
<line x1="4" x2="540" y1="232" y2="232"/>
|
||||
<line x1="108" x2="436" y1="104" y2="104"/>
|
||||
<line x1="436" x2="540" y1="104" y2="104"/>
|
||||
<line x1="540" x2="540" y1="104" y2="232"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="36" x2="36" y1="136" y2="200"/>
|
||||
<line x1="36" x2="180" y1="136" y2="136"/>
|
||||
<line x1="36" x2="180" y1="200" y2="200"/>
|
||||
<line x1="180" x2="180" y1="136" y2="152"/>
|
||||
<line x1="180" x2="180" y1="152" y2="184"/>
|
||||
<line marker-end="url(#triangle)" x1="180" x2="356" y1="152" y2="152"/>
|
||||
<line x1="180" x2="180" y1="184" y2="200"/>
|
||||
<line x1="180" x2="184" y1="184" y2="184"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="108" x2="108" y1="40" y2="92"/>
|
||||
<line x1="108" x2="212" y1="40" y2="40"/>
|
||||
<line x1="212" x2="212" y1="8" y2="40"/>
|
||||
<line x1="212" x2="332" y1="8" y2="8"/>
|
||||
<line x1="212" x2="212" y1="40" y2="72"/>
|
||||
<line x1="212" x2="332" y1="72" y2="72"/>
|
||||
<line x1="332" x2="332" y1="8" y2="40"/>
|
||||
<line x1="332" x2="332" y1="40" y2="72"/>
|
||||
<line x1="332" x2="436" y1="40" y2="40"/>
|
||||
<line marker-end="url(#triangle)" x1="436" x2="436" y1="40" y2="92"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="192" x2="188" y1="184" y2="184"/>
|
||||
<line x1="192" x2="364" y1="184" y2="184"/>
|
||||
<line x1="364" x2="364" y1="152" y2="184"/>
|
||||
<line x1="364" x2="364" y1="184" y2="200"/>
|
||||
<line x1="364" x2="508" y1="200" y2="200"/>
|
||||
<line x1="508" x2="508" y1="136" y2="200"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="364" x2="364" y1="136" y2="152"/>
|
||||
<line x1="364" x2="508" y1="136" y2="136"/>
|
||||
<line x1="364" x2="360" y1="152" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="65" y="172">
|
||||
Validator
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="172">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="217" y="140">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="249" y="44">
|
||||
Leader
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="321" y="140">
|
||||
0
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="393" y="172">
|
||||
Validator
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="172">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
192
book/src/.gitbook/assets/data-plane.svg
Normal file
@@ -0,0 +1,192 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="288" width="736" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="288" width="736" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="4" x2="4" y1="216" y2="280"/>
|
||||
<line x1="4" x2="156" y1="216" y2="216"/>
|
||||
<line x1="4" x2="172" y1="280" y2="280"/>
|
||||
<line x1="156" x2="172" y1="216" y2="216"/>
|
||||
<line x1="172" x2="172" y1="216" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="124" x2="124" y1="104" y2="168"/>
|
||||
<line x1="124" x2="204" y1="104" y2="104"/>
|
||||
<line x1="124" x2="156" y1="168" y2="168"/>
|
||||
<line marker-end="url(#triangle)" x1="156" x2="156" y1="168" y2="204"/>
|
||||
<line x1="156" x2="204" y1="168" y2="168"/>
|
||||
<line x1="204" x2="292" y1="104" y2="104"/>
|
||||
<line marker-end="url(#triangle)" x1="204" x2="204" y1="168" y2="204"/>
|
||||
<line x1="204" x2="292" y1="168" y2="168"/>
|
||||
<line x1="292" x2="292" y1="104" y2="168"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="188" x2="188" y1="216" y2="280"/>
|
||||
<line x1="188" x2="204" y1="216" y2="216"/>
|
||||
<line x1="188" x2="356" y1="280" y2="280"/>
|
||||
<line x1="204" x2="356" y1="216" y2="216"/>
|
||||
<line x1="356" x2="356" y1="216" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="204" x2="204" y1="40" y2="92"/>
|
||||
<line x1="204" x2="276" y1="40" y2="40"/>
|
||||
<line x1="276" x2="276" y1="8" y2="40"/>
|
||||
<line x1="276" x2="444" y1="8" y2="8"/>
|
||||
<line x1="276" x2="276" y1="40" y2="72"/>
|
||||
<line x1="276" x2="444" y1="72" y2="72"/>
|
||||
<line x1="444" x2="444" y1="8" y2="40"/>
|
||||
<line x1="444" x2="444" y1="40" y2="72"/>
|
||||
<line x1="444" x2="532" y1="40" y2="40"/>
|
||||
<line marker-end="url(#triangle)" x1="532" x2="532" y1="40" y2="92"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="380" x2="380" y1="216" y2="280"/>
|
||||
<line x1="380" x2="532" y1="216" y2="216"/>
|
||||
<line x1="380" x2="548" y1="280" y2="280"/>
|
||||
<line x1="532" x2="548" y1="216" y2="216"/>
|
||||
<line x1="548" x2="548" y1="216" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="444" x2="444" y1="104" y2="168"/>
|
||||
<line x1="444" x2="532" y1="104" y2="104"/>
|
||||
<line x1="444" x2="532" y1="168" y2="168"/>
|
||||
<line x1="532" x2="612" y1="104" y2="104"/>
|
||||
<line marker-end="url(#triangle)" x1="532" x2="532" y1="168" y2="204"/>
|
||||
<line x1="532" x2="580" y1="168" y2="168"/>
|
||||
<line marker-end="url(#triangle)" x1="580" x2="580" y1="168" y2="204"/>
|
||||
<line x1="580" x2="612" y1="168" y2="168"/>
|
||||
<line x1="612" x2="612" y1="104" y2="168"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="564" x2="564" y1="216" y2="280"/>
|
||||
<line x1="564" x2="580" y1="216" y2="216"/>
|
||||
<line x1="564" x2="732" y1="280" y2="280"/>
|
||||
<line x1="580" x2="732" y1="216" y2="216"/>
|
||||
<line x1="732" x2="732" y1="216" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="33" y="252">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="252">
|
||||
3
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="153" y="140">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="217" y="252">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="257" y="140">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="305" y="44">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="321" y="252">
|
||||
4
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="409" y="44">
|
||||
0
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="409" y="252">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="140">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="513" y="252">
|
||||
5
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="577" y="140">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="593" y="252">
|
||||
Neighborhood
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="697" y="252">
|
||||
6
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
330
book/src/.gitbook/assets/fork-generation.svg
Normal file
@@ -0,0 +1,330 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="208" width="768" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="208" width="768" x="0" y="0"/>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="76" x2="76" y1="32" y2="172"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="124" x2="124" y1="24" y2="56"/>
|
||||
<line x1="124" x2="164" y1="24" y2="24"/>
|
||||
<line x1="124" x2="124" y1="56" y2="88"/>
|
||||
<line x1="124" x2="164" y1="56" y2="56"/>
|
||||
<line x1="124" x2="124" y1="88" y2="120"/>
|
||||
<line x1="124" x2="164" y1="88" y2="88"/>
|
||||
<line x1="124" x2="124" y1="120" y2="152"/>
|
||||
<line x1="124" x2="164" y1="120" y2="120"/>
|
||||
<line x1="124" x2="124" y1="152" y2="184"/>
|
||||
<line x1="124" x2="164" y1="152" y2="152"/>
|
||||
<line x1="124" x2="164" y1="184" y2="184"/>
|
||||
<line x1="164" x2="164" y1="24" y2="56"/>
|
||||
<line x1="164" x2="164" y1="56" y2="88"/>
|
||||
<line x1="164" x2="164" y1="88" y2="120"/>
|
||||
<line x1="164" x2="164" y1="120" y2="152"/>
|
||||
<line x1="164" x2="164" y1="152" y2="184"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="188" x2="188" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="200" x2="208" y1="128" y2="112"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="224" x2="236" y1="112" y2="136"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="232" x2="240" y1="96" y2="80"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="236" x2="236" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="260" x2="260" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="264" x2="272" y1="64" y2="48"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="280" y1="80" y2="96"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="280" y1="128" y2="112"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="304" x2="316" y1="112" y2="136"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="316" x2="316" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="348" x2="360" y1="136" y2="112"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="348" x2="348" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="368" x2="376" y1="96" y2="80"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="376" x2="384" y1="48" y2="64"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="376" x2="388" y1="112" y2="136"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="388" x2="388" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="416" x2="424" y1="80" y2="96"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="436" x2="420" y1="104" y2="136"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="448" x2="460" y1="112" y2="136"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="460" x2="460" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="512" x2="640" y1="24" y2="24"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="17" y="108">
|
||||
time
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="44">
|
||||
L1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="76">
|
||||
L2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="108">
|
||||
L3
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="140">
|
||||
L4
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="172">
|
||||
L5
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="185" y="140">
|
||||
x
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="185" y="172">
|
||||
xx
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="209" y="108">
|
||||
E3
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="225" y="172">
|
||||
xx
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="249" y="76">
|
||||
E2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="257" y="140">
|
||||
E4
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="257" y="172">
|
||||
xx
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="289" y="108">
|
||||
x
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="305" y="172">
|
||||
E5
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="313" y="44">
|
||||
E1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="337" y="172">
|
||||
xx
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="361" y="108">
|
||||
E3'
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="377" y="172">
|
||||
xx
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="393" y="76">
|
||||
x
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="409" y="172">
|
||||
xx
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="449" y="172">
|
||||
xx
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="513" y="12">
|
||||
validator
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="513" y="60">
|
||||
vote(E1)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="513" y="92">
|
||||
vote(E2)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="513" y="124">
|
||||
slash(E3)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="513" y="156">
|
||||
vote(E4)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="513" y="188">
|
||||
hang
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="553" y="188">
|
||||
on
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="577" y="188">
|
||||
to
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="593" y="12">
|
||||
action
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="601" y="188">
|
||||
E4
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="625" y="188">
|
||||
and
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="657" y="188">
|
||||
E5
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="681" y="188">
|
||||
for
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="713" y="188">
|
||||
more...
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
92
book/src/.gitbook/assets/forks-pruned.svg
Normal file
@@ -0,0 +1,92 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="144" width="48" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="144" width="48" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="20" x2="24" y1="88" y2="80"/>
|
||||
<line x1="20" x2="20" y1="96" y2="88"/>
|
||||
<line x1="20" x2="20" y1="96" y2="128"/>
|
||||
<line x1="24" x2="40" y1="80" y2="48"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="44" x2="44" y1="16" y2="32"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="44" x2="44" y1="48" y2="96"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="17" y="140">
|
||||
5
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="41" y="12">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="41" y="44">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="41" y="108">
|
||||
4
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
92
book/src/.gitbook/assets/forks-pruned2.svg
Normal file
@@ -0,0 +1,92 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="176" width="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="176" width="40" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="16" y2="32"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="48" y2="128"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="32" y1="48" y2="80"/>
|
||||
<line x1="36" x2="32" y1="88" y2="80"/>
|
||||
<line x1="36" x2="36" y1="96" y2="88"/>
|
||||
<line x1="36" x2="36" y1="96" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="9" y="12">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="9" y="44">
|
||||
3
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="9" y="140">
|
||||
6
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="33" y="172">
|
||||
7
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
122
book/src/.gitbook/assets/forks.svg
Normal file
@@ -0,0 +1,122 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="208" width="96" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="208" width="96" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="20" x2="24" y1="88" y2="80"/>
|
||||
<line x1="20" x2="20" y1="96" y2="88"/>
|
||||
<line x1="20" x2="20" y1="96" y2="128"/>
|
||||
<line x1="24" x2="40" y1="80" y2="48"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="44" x2="44" y1="16" y2="32"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="44" x2="44" y1="48" y2="96"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="48" x2="64" y1="16" y2="48"/>
|
||||
<line x1="68" x2="64" y1="56" y2="48"/>
|
||||
<line x1="68" x2="68" y1="64" y2="56"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="68" x2="68" y1="80" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="72" x2="80" y1="80" y2="96"/>
|
||||
<line x1="80" x2="88" y1="96" y2="112"/>
|
||||
<line x1="92" x2="88" y1="120" y2="112"/>
|
||||
<line x1="92" x2="92" y1="128" y2="120"/>
|
||||
<line x1="92" x2="92" y1="128" y2="192"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="17" y="140">
|
||||
5
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="41" y="12">
|
||||
1
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="41" y="44">
|
||||
2
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="41" y="108">
|
||||
4
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="65" y="76">
|
||||
3
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="65" y="172">
|
||||
6
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="89" y="204">
|
||||
7
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 542 KiB |
BIN
book/src/.gitbook/assets/p_ex_schedule (1).png
Normal file
After Width: | Height: | Size: 256 KiB |
BIN
book/src/.gitbook/assets/p_ex_schedule (2).png
Normal file
After Width: | Height: | Size: 256 KiB |
BIN
book/src/.gitbook/assets/p_ex_schedule (3).png
Normal file
After Width: | Height: | Size: 256 KiB |
BIN
book/src/.gitbook/assets/p_ex_schedule (4).png
Normal file
After Width: | Height: | Size: 256 KiB |
BIN
book/src/.gitbook/assets/p_ex_supply (1).png
Normal file
After Width: | Height: | Size: 269 KiB |
BIN
book/src/.gitbook/assets/p_ex_supply (2).png
Normal file
After Width: | Height: | Size: 269 KiB |
BIN
book/src/.gitbook/assets/p_ex_supply (3).png
Normal file
After Width: | Height: | Size: 269 KiB |
BIN
book/src/.gitbook/assets/p_ex_supply (4).png
Normal file
After Width: | Height: | Size: 269 KiB |
238
book/src/.gitbook/assets/passive-staking-callflow.svg
Normal file
@@ -0,0 +1,238 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1"
|
||||
width="1320px" height="487px"
|
||||
viewBox="0 0 1320 487"
|
||||
xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges"
|
||||
stroke-width="1" text-rendering="geometricPrecision">
|
||||
<polygon fill="white" points="101,7 161,7 161,16 101,16"/>
|
||||
<text x="132" y="16" textLength="59" font-family="Helvetica" font-size="12" fill="black" text-anchor="middle">
|
||||
|
||||
VoteSigner
|
||||
</text>
|
||||
<polygon fill="white" points="371,7 419,7 419,16 371,16"/>
|
||||
<text x="396" y="16" textLength="47" font-family="Helvetica" font-size="12" fill="black" text-anchor="middle">
|
||||
|
||||
Validator
|
||||
</text>
|
||||
<polygon fill="white" points="639,7 679,7 679,16 639,16"/>
|
||||
<text x="660" y="16" textLength="38" font-family="Helvetica" font-size="12" fill="black" text-anchor="middle">
|
||||
|
||||
Cluster
|
||||
</text>
|
||||
<polygon fill="white" points="901,7 945,7 945,16 901,16"/>
|
||||
<text x="924" y="16" textLength="43" font-family="Helvetica" font-size="12" fill="black" text-anchor="middle">
|
||||
|
||||
StakerX
|
||||
</text>
|
||||
<polygon fill="white" points="1165,7 1209,7 1209,16 1165,16"/>
|
||||
<text x="1188" y="16" textLength="43" font-family="Helvetica" font-size="12" fill="black" text-anchor="middle">
|
||||
|
||||
StakerY
|
||||
</text>
|
||||
<line x1="132" y1="22" x2="132" y2="39" stroke="black"/>
|
||||
<line x1="396" y1="22" x2="396" y2="39" stroke="black"/>
|
||||
<line x1="660" y1="22" x2="660" y2="39" stroke="black"/>
|
||||
<line x1="924" y1="22" x2="924" y2="39" stroke="black"/>
|
||||
<line x1="1188" y1="22" x2="1188" y2="39" stroke="black"/>
|
||||
<line x1="132" y1="39" x2="132" y2="67" stroke="black"/>
|
||||
<line x1="396" y1="39" x2="396" y2="67" stroke="black"/>
|
||||
<line x1="660" y1="39" x2="660" y2="67" stroke="black"/>
|
||||
<line x1="924" y1="39" x2="924" y2="67" stroke="black"/>
|
||||
<line x1="1188" y1="39" x2="1188" y2="67" stroke="black"/>
|
||||
<polygon fill="white" points="272,39 520,39 520,61 272,61"/>
|
||||
<line x1="272" y1="39" x2="520" y2="39" stroke="black"/>
|
||||
<line x1="272" y1="61" x2="520" y2="61" stroke="black"/>
|
||||
<line x1="272" y1="39" x2="272" y2="61" stroke="black"/>
|
||||
<line x1="520" y1="39" x2="520" y2="61" stroke="black"/>
|
||||
<polygon fill="white" points="379,46 411,46 411,55 379,55"/>
|
||||
<text x="380" y="55" textLength="30" font-family="Helvetica" font-size="12" fill="black">
|
||||
boot..
|
||||
</text>
|
||||
<line x1="132" y1="67" x2="132" y2="106" stroke="black"/>
|
||||
<line x1="396" y1="67" x2="396" y2="106" stroke="black"/>
|
||||
<line x1="660" y1="67" x2="660" y2="106" stroke="black"/>
|
||||
<line x1="924" y1="67" x2="924" y2="106" stroke="black"/>
|
||||
<line x1="1188" y1="67" x2="1188" y2="106" stroke="black"/>
|
||||
<line x1="132" y1="82" x2="396" y2="82" stroke="black"/>
|
||||
<line x1="132" y1="84" x2="396" y2="84" stroke="black"/>
|
||||
<polygon fill="black" points="396,83 386,89 386,77"/>
|
||||
<polygon fill="black" points="132,83 142,89 142,77"/>
|
||||
<polygon fill="white" points="242,68 284,68 284,77 242,77"/>
|
||||
<text x="243" y="77" textLength="40" font-family="Helvetica" font-size="12" fill="black">
|
||||
register
|
||||
</text>
|
||||
<polygon fill="white" points="262,79 264,79 264,88 262,88"/>
|
||||
<text x="263" y="88" textLength="0" font-family="Helvetica" font-size="12" fill="black">
|
||||
|
||||
</text>
|
||||
<polygon fill="white" points="237,90 289,90 289,99 237,99"/>
|
||||
<text x="238" y="99" textLength="50" font-family="Helvetica" font-size="12" fill="black">
|
||||
(optional)
|
||||
</text>
|
||||
<line x1="132" y1="106" x2="132" y2="134" stroke="black"/>
|
||||
<line x1="396" y1="106" x2="396" y2="134" stroke="black"/>
|
||||
<line x1="660" y1="106" x2="660" y2="134" stroke="black"/>
|
||||
<line x1="924" y1="106" x2="924" y2="134" stroke="black"/>
|
||||
<line x1="1188" y1="106" x2="1188" y2="134" stroke="black"/>
|
||||
<line x1="396" y1="117" x2="660" y2="117" stroke="black"/>
|
||||
<polygon fill="black" points="660,117 650,123 650,111"/>
|
||||
<polygon fill="white" points="441,107 613,107 613,116 441,116"/>
|
||||
<text x="442" y="116" textLength="170" font-family="Helvetica" font-size="12" fill="black">
|
||||
VoteState::Initialize(VoteSigner)
|
||||
</text>
|
||||
<line x1="132" y1="134" x2="132" y2="162" stroke="black"/>
|
||||
<line x1="396" y1="134" x2="396" y2="162" stroke="black"/>
|
||||
<line x1="660" y1="134" x2="660" y2="162" stroke="black"/>
|
||||
<line x1="924" y1="134" x2="924" y2="162" stroke="black"/>
|
||||
<line x1="1188" y1="134" x2="1188" y2="162" stroke="black"/>
|
||||
<line x1="924" y1="145" x2="660" y2="145" stroke="black"/>
|
||||
<polygon fill="black" points="660,145 670,151 670,139"/>
|
||||
<polygon fill="white" points="706,135 877,135 877,144 706,144"/>
|
||||
<text x="707" y="144" textLength="169" font-family="Helvetica" font-size="12" fill="black">
|
||||
StakeState::Delegate(Validator)
|
||||
</text>
|
||||
<line x1="132" y1="162" x2="132" y2="190" stroke="black"/>
|
||||
<line x1="396" y1="162" x2="396" y2="190" stroke="black"/>
|
||||
<line x1="660" y1="162" x2="660" y2="190" stroke="black"/>
|
||||
<line x1="924" y1="162" x2="924" y2="190" stroke="black"/>
|
||||
<line x1="1188" y1="162" x2="1188" y2="190" stroke="black"/>
|
||||
<line x1="1188" y1="173" x2="660" y2="173" stroke="black"/>
|
||||
<polygon fill="black" points="660,173 670,179 670,167"/>
|
||||
<polygon fill="white" points="838,163 1009,163 1009,172 838,172"/>
|
||||
<text x="839" y="172" textLength="169" font-family="Helvetica" font-size="12" fill="black">
|
||||
StakeState::Delegate(Validator)
|
||||
</text>
|
||||
<line x1="132" y1="190" x2="132" y2="207" stroke="black"/>
|
||||
<line x1="396" y1="190" x2="396" y2="207" stroke="black"/>
|
||||
<line x1="660" y1="190" x2="660" y2="207" stroke="black"/>
|
||||
<line x1="924" y1="190" x2="924" y2="207" stroke="black"/>
|
||||
<line x1="1188" y1="190" x2="1188" y2="207" stroke="black"/>
|
||||
<line x1="132" y1="207" x2="132" y2="246" stroke="black"/>
|
||||
<line x1="396" y1="207" x2="396" y2="246" stroke="black"/>
|
||||
<line x1="660" y1="207" x2="660" y2="246" stroke="black"/>
|
||||
<line x1="924" y1="207" x2="924" y2="246" stroke="black"/>
|
||||
<line x1="1188" y1="207" x2="1188" y2="246" stroke="black"/>
|
||||
<polygon fill="white" points="272,207 784,207 784,240 272,240"/>
|
||||
<line x1="272" y1="207" x2="784" y2="207" stroke="black"/>
|
||||
<line x1="272" y1="240" x2="784" y2="240" stroke="black"/>
|
||||
<line x1="272" y1="207" x2="272" y2="240" stroke="black"/>
|
||||
<line x1="784" y1="207" x2="784" y2="240" stroke="black"/>
|
||||
<polygon fill="white" points="526,208 528,208 528,217 526,217"/>
|
||||
<text x="527" y="217" textLength="0" font-family="Helvetica" font-size="12" fill="black">
|
||||
|
||||
</text>
|
||||
<polygon fill="white" points="506,219 549,219 549,228 506,228"/>
|
||||
<text x="507" y="228" textLength="41" font-family="Helvetica" font-size="12" fill="black">
|
||||
validate
|
||||
</text>
|
||||
<polygon fill="white" points="526,230 528,230 528,239 526,239"/>
|
||||
<text x="527" y="239" textLength="0" font-family="Helvetica" font-size="12" fill="black">
|
||||
|
||||
</text>
|
||||
<line x1="132" y1="246" x2="132" y2="274" stroke="black"/>
|
||||
<line x1="396" y1="246" x2="396" y2="274" stroke="black"/>
|
||||
<line x1="660" y1="246" x2="660" y2="274" stroke="black"/>
|
||||
<line x1="924" y1="246" x2="924" y2="274" stroke="black"/>
|
||||
<line x1="1188" y1="246" x2="1188" y2="274" stroke="black"/>
|
||||
<line x1="396" y1="257" x2="132" y2="257" stroke="black"/>
|
||||
<polygon fill="black" points="132,257 142,263 142,251"/>
|
||||
<polygon fill="white" points="236,247 291,247 291,256 236,256"/>
|
||||
<text x="237" y="256" textLength="53" font-family="Helvetica" font-size="12" fill="black">
|
||||
sign(vote)
|
||||
</text>
|
||||
<line x1="132" y1="274" x2="132" y2="302" stroke="black"/>
|
||||
<line x1="396" y1="274" x2="396" y2="302" stroke="black"/>
|
||||
<line x1="660" y1="274" x2="660" y2="302" stroke="black"/>
|
||||
<line x1="924" y1="274" x2="924" y2="302" stroke="black"/>
|
||||
<line x1="1188" y1="274" x2="1188" y2="302" stroke="black"/>
|
||||
<line x1="132" y1="285" x2="396" y2="285" stroke="black" stroke-dasharray="2,2"/>
|
||||
<polygon fill="black" points="396,285 386,291 386,279"/>
|
||||
<polygon fill="white" points="232,275 295,275 295,284 232,284"/>
|
||||
<text x="233" y="284" textLength="61" font-family="Helvetica" font-size="12" fill="black">
|
||||
signed vote
|
||||
</text>
|
||||
<line x1="132" y1="302" x2="132" y2="330" stroke="black"/>
|
||||
<line x1="396" y1="302" x2="396" y2="330" stroke="black"/>
|
||||
<line x1="660" y1="302" x2="660" y2="330" stroke="black"/>
|
||||
<line x1="924" y1="302" x2="924" y2="330" stroke="black"/>
|
||||
<line x1="1188" y1="302" x2="1188" y2="330" stroke="black"/>
|
||||
<line x1="396" y1="313" x2="660" y2="313" stroke="black"/>
|
||||
<polygon fill="black" points="660,313 650,319 650,307"/>
|
||||
<polygon fill="white" points="494,303 561,303 561,312 494,312"/>
|
||||
<text x="495" y="312" textLength="65" font-family="Helvetica" font-size="12" fill="black">
|
||||
gossip(vote)
|
||||
</text>
|
||||
<line x1="132" y1="330" x2="132" y2="347" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="396" y1="330" x2="396" y2="347" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="660" y1="330" x2="660" y2="347" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="924" y1="330" x2="924" y2="347" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="1188" y1="330" x2="1188" y2="347" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="132" y1="347" x2="132" y2="364" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="396" y1="347" x2="396" y2="364" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="660" y1="347" x2="660" y2="364" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="924" y1="347" x2="924" y2="364" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="1188" y1="347" x2="1188" y2="364" stroke="black" stroke-dasharray="2,2"/>
|
||||
<line x1="132" y1="364" x2="132" y2="414" stroke="black"/>
|
||||
<line x1="396" y1="364" x2="396" y2="414" stroke="black"/>
|
||||
<line x1="660" y1="364" x2="660" y2="414" stroke="black"/>
|
||||
<line x1="924" y1="364" x2="924" y2="414" stroke="black"/>
|
||||
<line x1="1188" y1="364" x2="1188" y2="414" stroke="black"/>
|
||||
<polygon fill="white" points="278,364 514,364 514,408 278,408"/>
|
||||
<polygon fill="white" points="278,364 278,408 272,386"/>
|
||||
<polygon fill="white" points="514,364 514,408 520,386"/>
|
||||
<line x1="278" y1="364" x2="514" y2="364" stroke="black"/>
|
||||
<line x1="278" y1="408" x2="514" y2="408" stroke="black"/>
|
||||
<line x1="278" y1="364" x2="272" y2="386" stroke="black"/>
|
||||
<line x1="272" y1="386" x2="278" y2="408" stroke="black"/>
|
||||
<line x1="514" y1="364" x2="520" y2="386" stroke="black"/>
|
||||
<line x1="520" y1="386" x2="514" y2="408" stroke="black"/>
|
||||
<polygon fill="white" points="394,365 396,365 396,374 394,374"/>
|
||||
<text x="395" y="374" textLength="0" font-family="Helvetica" font-size="12" fill="black">
|
||||
|
||||
</text>
|
||||
<polygon fill="white" points="383,376 408,376 408,385 383,385"/>
|
||||
<text x="384" y="385" textLength="23" font-family="Helvetica" font-size="12" fill="black">
|
||||
max
|
||||
</text>
|
||||
<polygon fill="white" points="375,387 415,387 415,396 375,396"/>
|
||||
<text x="376" y="396" textLength="38" font-family="Helvetica" font-size="12" fill="black">
|
||||
lockout
|
||||
</text>
|
||||
<polygon fill="white" points="394,398 396,398 396,407 394,407"/>
|
||||
<text x="395" y="407" textLength="0" font-family="Helvetica" font-size="12" fill="black">
|
||||
|
||||
</text>
|
||||
<line x1="132" y1="414" x2="132" y2="431" stroke="black"/>
|
||||
<line x1="396" y1="414" x2="396" y2="431" stroke="black"/>
|
||||
<line x1="660" y1="414" x2="660" y2="431" stroke="black"/>
|
||||
<line x1="924" y1="414" x2="924" y2="431" stroke="black"/>
|
||||
<line x1="1188" y1="414" x2="1188" y2="431" stroke="black"/>
|
||||
<line x1="132" y1="431" x2="132" y2="459" stroke="black"/>
|
||||
<line x1="396" y1="431" x2="396" y2="459" stroke="black"/>
|
||||
<line x1="660" y1="431" x2="660" y2="459" stroke="black"/>
|
||||
<line x1="924" y1="431" x2="924" y2="459" stroke="black"/>
|
||||
<line x1="1188" y1="431" x2="1188" y2="459" stroke="black"/>
|
||||
<line x1="924" y1="442" x2="660" y2="442" stroke="black"/>
|
||||
<polygon fill="black" points="660,442 670,448 670,436"/>
|
||||
<polygon fill="white" points="712,432 871,432 871,441 712,441"/>
|
||||
<text x="713" y="441" textLength="157" font-family="Helvetica" font-size="12" fill="black">
|
||||
StakeState::RedeemCredits()
|
||||
</text>
|
||||
<line x1="132" y1="459" x2="132" y2="487" stroke="black"/>
|
||||
<line x1="396" y1="459" x2="396" y2="487" stroke="black"/>
|
||||
<line x1="660" y1="459" x2="660" y2="487" stroke="black"/>
|
||||
<line x1="924" y1="459" x2="924" y2="487" stroke="black"/>
|
||||
<line x1="1188" y1="459" x2="1188" y2="487" stroke="black"/>
|
||||
<line x1="1188" y1="470" x2="660" y2="470" stroke="black"/>
|
||||
<polygon fill="black" points="660,470 670,476 670,464"/>
|
||||
<polygon fill="white" points="844,460 1003,460 1003,469 844,469"/>
|
||||
<text x="845" y="469" textLength="157" font-family="Helvetica" font-size="12" fill="black">
|
||||
StakeState::RedeemCredits()
|
||||
</text>
|
||||
<line x1="132" y1="481" x2="132" y2="487" stroke="black"/>
|
||||
<line x1="396" y1="481" x2="396" y2="487" stroke="black"/>
|
||||
<line x1="660" y1="481" x2="660" y2="487" stroke="black"/>
|
||||
<line x1="924" y1="481" x2="924" y2="487" stroke="black"/>
|
||||
<line x1="1188" y1="481" x2="1188" y2="487" stroke="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
346
book/src/.gitbook/assets/runtime.svg
Normal file
@@ -0,0 +1,346 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="160" width="848" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="160" width="848" x="0" y="0"/>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="0" x2="28" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="4" x2="4" y1="12" y2="52"/>
|
||||
<path d="M 4 52 A 4 4 0 0 0 8 56" fill="none"/>
|
||||
<path d="M 8 8 A 4 4 0 0 0 4 12" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="8" x2="104" y1="8" y2="8"/>
|
||||
<path d="M 108 12 A 4 4 0 0 0 104 8" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="8" x2="104" y1="56" y2="56"/>
|
||||
<path d="M 104 56 A 4 4 0 0 0 108 52" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="36" x2="36" y1="92" y2="116"/>
|
||||
<path d="M 36 116 A 4 4 0 0 0 40 120" fill="none"/>
|
||||
<path d="M 40 88 A 4 4 0 0 0 36 92" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="40" x2="160" y1="88" y2="88"/>
|
||||
<path d="M 164 92 A 4 4 0 0 0 160 88" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="40" x2="160" y1="120" y2="120"/>
|
||||
<path d="M 160 120 A 4 4 0 0 0 164 116" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="12" y2="24"/>
|
||||
<line x1="108" x2="108" y1="24" y2="52"/>
|
||||
<line marker-end="url(#triangle)" x1="108" x2="140" y1="24" y2="24"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="156" x2="156" y1="12" y2="36"/>
|
||||
<path d="M 156 36 A 4 4 0 0 0 160 40" fill="none"/>
|
||||
<path d="M 160 8 A 4 4 0 0 0 156 12" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="160" x2="248" y1="8" y2="8"/>
|
||||
<path d="M 252 12 A 4 4 0 0 0 248 8" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="160" x2="248" y1="40" y2="40"/>
|
||||
<path d="M 248 40 A 4 4 0 0 0 252 36" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="164" x2="164" y1="92" y2="104"/>
|
||||
<line x1="164" x2="164" y1="104" y2="116"/>
|
||||
<line marker-end="url(#triangle)" x1="164" x2="196" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="204" x2="204" y1="92" y2="116"/>
|
||||
<path d="M 204 116 A 4 4 0 0 0 208 120" fill="none"/>
|
||||
<path d="M 208 88 A 4 4 0 0 0 204 92" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="208" x2="280" y1="88" y2="88"/>
|
||||
<path d="M 284 92 A 4 4 0 0 0 280 88" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="208" x2="280" y1="120" y2="120"/>
|
||||
<path d="M 280 120 A 4 4 0 0 0 284 116" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="252" x2="252" y1="12" y2="24"/>
|
||||
<line x1="252" x2="252" y1="24" y2="36"/>
|
||||
<line marker-end="url(#triangle)" x1="252" x2="284" y1="24" y2="24"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="284" x2="284" y1="92" y2="104"/>
|
||||
<line x1="284" x2="284" y1="104" y2="116"/>
|
||||
<line marker-end="url(#triangle)" x1="284" x2="316" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="292" x2="292" y1="12" y2="36"/>
|
||||
<path d="M 292 36 A 4 4 0 0 0 296 40" fill="none"/>
|
||||
<path d="M 296 8 A 4 4 0 0 0 292 12" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="296" x2="416" y1="8" y2="8"/>
|
||||
<path d="M 420 12 A 4 4 0 0 0 416 8" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="296" x2="416" y1="40" y2="40"/>
|
||||
<path d="M 416 40 A 4 4 0 0 0 420 36" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="324" x2="324" y1="92" y2="132"/>
|
||||
<path d="M 324 132 A 4 4 0 0 0 328 136" fill="none"/>
|
||||
<path d="M 328 88 A 4 4 0 0 0 324 92" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="328" x2="424" y1="88" y2="88"/>
|
||||
<path d="M 428 92 A 4 4 0 0 0 424 88" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="328" x2="424" y1="136" y2="136"/>
|
||||
<path d="M 424 136 A 4 4 0 0 0 428 132" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="12" y2="24"/>
|
||||
<line x1="420" x2="420" y1="24" y2="36"/>
|
||||
<line marker-end="url(#triangle)" x1="420" x2="452" y1="24" y2="24"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="428" x2="428" y1="92" y2="104"/>
|
||||
<line x1="428" x2="428" y1="104" y2="132"/>
|
||||
<line marker-end="url(#triangle)" x1="428" x2="460" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="460" x2="460" y1="12" y2="36"/>
|
||||
<path d="M 460 36 A 4 4 0 0 0 464 40" fill="none"/>
|
||||
<path d="M 464 8 A 4 4 0 0 0 460 12" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="464" x2="576" y1="8" y2="8"/>
|
||||
<path d="M 580 12 A 4 4 0 0 0 576 8" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="464" x2="576" y1="40" y2="40"/>
|
||||
<path d="M 576 40 A 4 4 0 0 0 580 36" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="468" x2="468" y1="92" y2="116"/>
|
||||
<path d="M 468 116 A 4 4 0 0 0 472 120" fill="none"/>
|
||||
<path d="M 472 88 A 4 4 0 0 0 468 92" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="472" x2="608" y1="88" y2="88"/>
|
||||
<path d="M 612 92 A 4 4 0 0 0 608 88" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="472" x2="608" y1="120" y2="120"/>
|
||||
<path d="M 608 120 A 4 4 0 0 0 612 116" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="580" x2="580" y1="12" y2="24"/>
|
||||
<line x1="580" x2="580" y1="24" y2="36"/>
|
||||
<line marker-end="url(#triangle)" x1="580" x2="612" y1="24" y2="24"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="612" x2="612" y1="92" y2="104"/>
|
||||
<line x1="612" x2="612" y1="104" y2="116"/>
|
||||
<line marker-end="url(#triangle)" x1="612" x2="636" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="620" x2="620" y1="12" y2="36"/>
|
||||
<path d="M 620 36 A 4 4 0 0 0 624 40" fill="none"/>
|
||||
<path d="M 624 8 A 4 4 0 0 0 620 12" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="624" x2="808" y1="8" y2="8"/>
|
||||
<path d="M 812 12 A 4 4 0 0 0 808 8" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="624" x2="808" y1="40" y2="40"/>
|
||||
<path d="M 808 40 A 4 4 0 0 0 812 36" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="644" x2="644" y1="92" y2="116"/>
|
||||
<path d="M 644 116 A 4 4 0 0 0 648 120" fill="none"/>
|
||||
<path d="M 648 88 A 4 4 0 0 0 644 92" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="648" x2="784" y1="88" y2="88"/>
|
||||
<path d="M 788 92 A 4 4 0 0 0 784 88" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="648" x2="784" y1="120" y2="120"/>
|
||||
<path d="M 784 120 A 4 4 0 0 0 788 116" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="788" x2="788" y1="92" y2="116"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="812" x2="812" y1="12" y2="24"/>
|
||||
<line x1="812" x2="812" y1="24" y2="36"/>
|
||||
<line marker-end="url(#triangle)" x1="812" x2="844" y1="24" y2="24"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="17" y="28">
|
||||
PoH
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="49" y="28">
|
||||
verify
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="49" y="44">
|
||||
TVU
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="49" y="108">
|
||||
load
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="89" y="108">
|
||||
accounts
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="169" y="28">
|
||||
sigverify
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="217" y="108">
|
||||
execute
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="305" y="28">
|
||||
lock
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="337" y="108">
|
||||
PoH
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="345" y="28">
|
||||
accounts
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="361" y="124">
|
||||
TPU
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="369" y="108">
|
||||
record
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="28">
|
||||
validate
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="481" y="108">
|
||||
commit
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="537" y="108">
|
||||
accounts
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="545" y="28">
|
||||
fee
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="633" y="28">
|
||||
allocate
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="657" y="108">
|
||||
unlock
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="705" y="28">
|
||||
new
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="713" y="108">
|
||||
accounts
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="737" y="28">
|
||||
accounts
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.0 KiB |
237
book/src/.gitbook/assets/sdk-tools.svg
Normal file
@@ -0,0 +1,237 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="320" width="560" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="320" width="560" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="20" x2="20" y1="140" y2="196"/>
|
||||
<path d="M 20 196 A 4 4 0 0 0 24 200" fill="none"/>
|
||||
<path d="M 24 136 A 4 4 0 0 0 20 140" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="24" x2="104" y1="136" y2="136"/>
|
||||
<path d="M 108 140 A 4 4 0 0 0 104 136" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="24" x2="104" y1="200" y2="200"/>
|
||||
<path d="M 104 200 A 4 4 0 0 0 108 196" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="140" y2="152"/>
|
||||
<line x1="108" x2="108" y1="152" y2="184"/>
|
||||
<line x1="108" x2="176" y1="152" y2="152"/>
|
||||
<line x1="108" x2="108" y1="184" y2="196"/>
|
||||
<line x1="108" x2="176" y1="184" y2="184"/>
|
||||
<path d="M 176 152 A 4 4 0 0 0 180 148" fill="none"/>
|
||||
<path d="M 180 188 A 4 4 0 0 0 176 184" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="180" x2="180" y1="108" y2="148"/>
|
||||
<path d="M 184 104 A 4 4 0 0 0 180 108" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="180" x2="180" y1="188" y2="244"/>
|
||||
<path d="M 180 244 A 4 4 0 0 0 184 248" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="184" x2="252" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="184" x2="252" y1="248" y2="248"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="228" x2="228" y1="28" y2="96"/>
|
||||
<path d="M 232 24 A 4 4 0 0 0 228 28" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="228" x2="228" y1="112" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="228" x2="228" y1="256" y2="308"/>
|
||||
<path d="M 228 308 A 4 4 0 0 0 232 312" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="232" x2="552" y1="24" y2="24"/>
|
||||
<path d="M 556 28 A 4 4 0 0 0 552 24" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="232" x2="552" y1="312" y2="312"/>
|
||||
<path d="M 552 312 A 4 4 0 0 0 556 308" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="260" x2="260" y1="76" y2="132"/>
|
||||
<path d="M 260 132 A 4 4 0 0 0 264 136" fill="none"/>
|
||||
<path d="M 264 72 A 4 4 0 0 0 260 76" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="260" x2="260" y1="220" y2="276"/>
|
||||
<path d="M 260 276 A 4 4 0 0 0 264 280" fill="none"/>
|
||||
<path d="M 264 216 A 4 4 0 0 0 260 220" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="264" x2="360" y1="72" y2="72"/>
|
||||
<path d="M 364 76 A 4 4 0 0 0 360 72" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="264" x2="360" y1="136" y2="136"/>
|
||||
<path d="M 360 136 A 4 4 0 0 0 364 132" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="264" x2="316" y1="216" y2="216"/>
|
||||
<line x1="316" x2="316" y1="188" y2="216"/>
|
||||
<line x1="316" x2="360" y1="216" y2="216"/>
|
||||
<path d="M 320 184 A 4 4 0 0 0 316 188" fill="none"/>
|
||||
<path d="M 364 220 A 4 4 0 0 0 360 216" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="264" x2="360" y1="280" y2="280"/>
|
||||
<path d="M 360 280 A 4 4 0 0 0 364 276" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="320" x2="448" y1="184" y2="184"/>
|
||||
<path d="M 448 184 A 4 4 0 0 0 452 180" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="364" x2="364" y1="76" y2="104"/>
|
||||
<line x1="364" x2="364" y1="104" y2="132"/>
|
||||
<line marker-end="url(#triangle)" x1="364" x2="388" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="364" x2="364" y1="220" y2="248"/>
|
||||
<line x1="364" x2="364" y1="248" y2="276"/>
|
||||
<line marker-end="url(#triangle)" x1="364" x2="388" y1="248" y2="248"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="396" x2="396" y1="76" y2="132"/>
|
||||
<path d="M 396 132 A 4 4 0 0 0 400 136" fill="none"/>
|
||||
<path d="M 400 72 A 4 4 0 0 0 396 76" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="396" x2="396" y1="220" y2="276"/>
|
||||
<path d="M 396 276 A 4 4 0 0 0 400 280" fill="none"/>
|
||||
<path d="M 400 216 A 4 4 0 0 0 396 220" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="400" x2="496" y1="72" y2="72"/>
|
||||
<path d="M 500 76 A 4 4 0 0 0 496 72" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="400" x2="496" y1="136" y2="136"/>
|
||||
<path d="M 496 136 A 4 4 0 0 0 500 132" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="400" x2="504" y1="216" y2="216"/>
|
||||
<path d="M 508 220 A 4 4 0 0 0 504 216" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="400" x2="504" y1="280" y2="280"/>
|
||||
<path d="M 504 280 A 4 4 0 0 0 508 276" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="452" x2="452" y1="160" y2="148"/>
|
||||
<line x1="452" x2="452" y1="160" y2="180"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="500" x2="500" y1="76" y2="132"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="508" x2="508" y1="220" y2="276"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="556" x2="556" y1="28" y2="308"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="41" y="172">
|
||||
Client
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="108">
|
||||
Verifier
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="252">
|
||||
Loader
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="329" y="44">
|
||||
Solana
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="337" y="172">
|
||||
LoadAccounts
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="385" y="44">
|
||||
Runtime
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="409" y="252">
|
||||
Interpreter
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="417" y="108">
|
||||
Accounts
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.2 KiB |
163
book/src/.gitbook/assets/spv-bank-merkle.svg
Normal file
@@ -0,0 +1,163 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="288" width="432" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="288" width="432" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="248" y2="280"/>
|
||||
<line x1="12" x2="140" y1="248" y2="248"/>
|
||||
<line x1="12" x2="140" y1="280" y2="280"/>
|
||||
<line x1="140" x2="140" y1="248" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="112" x2="126" y1="240" y2="212"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="124" x2="124" y1="88" y2="120"/>
|
||||
<line x1="124" x2="268" y1="88" y2="88"/>
|
||||
<line x1="124" x2="268" y1="120" y2="120"/>
|
||||
<line x1="268" x2="268" y1="88" y2="120"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="124" x2="124" y1="168" y2="200"/>
|
||||
<line x1="124" x2="180" y1="168" y2="168"/>
|
||||
<line x1="124" x2="180" y1="200" y2="200"/>
|
||||
<line x1="180" x2="180" y1="168" y2="200"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="160" x2="174" y1="160" y2="132"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="172" x2="172" y1="248" y2="280"/>
|
||||
<line x1="172" x2="300" y1="248" y2="248"/>
|
||||
<line x1="172" x2="300" y1="280" y2="280"/>
|
||||
<line x1="300" x2="300" y1="248" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="184" x2="178" y1="224" y2="212"/>
|
||||
<line x1="184" x2="192" y1="224" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="212" x2="212" y1="168" y2="200"/>
|
||||
<line x1="212" x2="428" y1="168" y2="168"/>
|
||||
<line x1="212" x2="428" y1="200" y2="200"/>
|
||||
<line x1="428" x2="428" y1="168" y2="200"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="224" x2="218" y1="144" y2="132"/>
|
||||
<line x1="224" x2="232" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="236" x2="236" y1="8" y2="40"/>
|
||||
<line x1="236" x2="340" y1="8" y2="8"/>
|
||||
<line x1="236" x2="340" y1="40" y2="40"/>
|
||||
<line x1="340" x2="340" y1="8" y2="40"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="248" x2="262" y1="80" y2="52"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="308" x2="308" y1="88" y2="120"/>
|
||||
<line x1="308" x2="420" y1="88" y2="88"/>
|
||||
<line x1="308" x2="420" y1="120" y2="120"/>
|
||||
<line x1="420" x2="420" y1="88" y2="120"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="320" x2="314" y1="64" y2="52"/>
|
||||
<line x1="320" x2="328" y1="64" y2="80"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="25" y="268">
|
||||
Hash(Account1)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="108">
|
||||
Bank-Diff-Merkle
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="188">
|
||||
Hash
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="185" y="268">
|
||||
Hash(Account2)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="225" y="188">
|
||||
Previous
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="249" y="28">
|
||||
Bank-Merkle
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="297" y="188">
|
||||
Bank-Diff-Merkle
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="321" y="108">
|
||||
Block-Merkle
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
203
book/src/.gitbook/assets/spv-block-merkle.svg
Normal file
@@ -0,0 +1,203 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="304" width="584" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="304" width="584" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="248" y2="280"/>
|
||||
<line x1="12" x2="156" y1="248" y2="248"/>
|
||||
<line x1="12" x2="156" y1="280" y2="280"/>
|
||||
<line x1="156" x2="156" y1="248" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="128" x2="142" y1="240" y2="212"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="148" x2="148" y1="168" y2="200"/>
|
||||
<line x1="148" x2="212" y1="168" y2="168"/>
|
||||
<line x1="148" x2="212" y1="200" y2="200"/>
|
||||
<line x1="212" x2="212" y1="168" y2="200"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="180" x2="180" y1="88" y2="120"/>
|
||||
<line x1="180" x2="292" y1="88" y2="88"/>
|
||||
<line x1="180" x2="292" y1="120" y2="120"/>
|
||||
<line x1="292" x2="292" y1="88" y2="120"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="188" x2="188" y1="248" y2="280"/>
|
||||
<line x1="188" x2="332" y1="248" y2="248"/>
|
||||
<line x1="188" x2="332" y1="280" y2="280"/>
|
||||
<line x1="332" x2="332" y1="248" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="192" x2="206" y1="160" y2="132"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="212" x2="212" y1="224" y2="212"/>
|
||||
<line x1="212" x2="212" y1="224" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="264" x2="278" y1="80" y2="52"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="284" x2="284" y1="8" y2="40"/>
|
||||
<line x1="284" x2="412" y1="8" y2="8"/>
|
||||
<line x1="284" x2="412" y1="40" y2="40"/>
|
||||
<line x1="412" x2="412" y1="8" y2="40"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="364" x2="364" y1="248" y2="280"/>
|
||||
<line x1="364" x2="508" y1="248" y2="248"/>
|
||||
<line x1="364" x2="508" y1="280" y2="280"/>
|
||||
<line x1="508" x2="508" y1="248" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="396" x2="396" y1="88" y2="120"/>
|
||||
<line x1="396" x2="508" y1="88" y2="88"/>
|
||||
<line x1="396" x2="508" y1="120" y2="120"/>
|
||||
<line x1="508" x2="508" y1="88" y2="120"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="416" x2="410" y1="64" y2="52"/>
|
||||
<line x1="416" x2="424" y1="64" y2="80"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="468" x2="468" y1="168" y2="200"/>
|
||||
<line x1="468" x2="532" y1="168" y2="168"/>
|
||||
<line x1="468" x2="532" y1="200" y2="200"/>
|
||||
<line x1="532" x2="532" y1="168" y2="200"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="476" x2="476" y1="224" y2="212"/>
|
||||
<line x1="476" x2="476" y1="224" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="480" x2="474" y1="144" y2="132"/>
|
||||
<line x1="480" x2="488" y1="144" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="536" x2="530" y1="224" y2="212"/>
|
||||
<line x1="536" x2="544" y1="224" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="540" x2="540" y1="248" y2="280"/>
|
||||
<line x1="540" x2="572" y1="248" y2="248"/>
|
||||
<line x1="540" x2="572" y1="280" y2="280"/>
|
||||
<line x1="572" x2="572" y1="248" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="25" y="268">
|
||||
Hash(T1,
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="97" y="268">
|
||||
status)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="169" y="188">
|
||||
Hash
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="193" y="108">
|
||||
Entry-Merkle
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="201" y="268">
|
||||
Hash(T2,
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="273" y="268">
|
||||
status)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="305" y="28">
|
||||
Block-Merkle
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="377" y="268">
|
||||
Hash(T3,
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="409" y="108">
|
||||
Entry-Merkle
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="449" y="268">
|
||||
status)
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="489" y="188">
|
||||
Hash
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="553" y="268">
|
||||
0
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.0 KiB |
312
book/src/.gitbook/assets/tpu.svg
Normal file
@@ -0,0 +1,312 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="304" width="696" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="304" width="696" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="140" y2="164"/>
|
||||
<path d="M 12 164 A 4 4 0 0 0 16 168" fill="none"/>
|
||||
<path d="M 16 136 A 4 4 0 0 0 12 140" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="88" y1="136" y2="136"/>
|
||||
<path d="M 92 140 A 4 4 0 0 0 88 136" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="88" y1="168" y2="168"/>
|
||||
<path d="M 88 168 A 4 4 0 0 0 92 164" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="92" x2="92" y1="140" y2="164"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="92" x2="124" y1="152" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="92" y2="144"/>
|
||||
<path d="M 112 88 A 4 4 0 0 0 108 92" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="160" y2="212"/>
|
||||
<path d="M 108 212 A 4 4 0 0 0 112 216" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="356" y1="88" y2="88"/>
|
||||
<line x1="356" x2="396" y1="88" y2="88"/>
|
||||
<line x1="396" x2="560" y1="88" y2="88"/>
|
||||
<path d="M 564 92 A 4 4 0 0 0 560 88" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="380" y1="216" y2="216"/>
|
||||
<line x1="380" x2="560" y1="216" y2="216"/>
|
||||
<path d="M 560 216 A 4 4 0 0 0 564 212" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="132" x2="132" y1="124" y2="180"/>
|
||||
<path d="M 132 180 A 4 4 0 0 0 136 184" fill="none"/>
|
||||
<path d="M 136 120 A 4 4 0 0 0 132 124" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="192" y1="120" y2="120"/>
|
||||
<path d="M 196 124 A 4 4 0 0 0 192 120" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="192" y1="184" y2="184"/>
|
||||
<path d="M 192 184 A 4 4 0 0 0 196 180" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="196" x2="196" y1="124" y2="180"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="196" x2="212" y1="152" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="220" x2="220" y1="124" y2="180"/>
|
||||
<path d="M 220 180 A 4 4 0 0 0 224 184" fill="none"/>
|
||||
<path d="M 224 120 A 4 4 0 0 0 220 124" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="224" x2="312" y1="120" y2="120"/>
|
||||
<path d="M 316 124 A 4 4 0 0 0 312 120" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="224" x2="312" y1="184" y2="184"/>
|
||||
<path d="M 312 184 A 4 4 0 0 0 316 180" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="316" x2="316" y1="124" y2="180"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="316" x2="332" y1="152" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="324" x2="324" y1="28" y2="52"/>
|
||||
<path d="M 324 52 A 4 4 0 0 0 328 56" fill="none"/>
|
||||
<path d="M 328 24 A 4 4 0 0 0 324 28" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="328" x2="432" y1="24" y2="24"/>
|
||||
<path d="M 436 28 A 4 4 0 0 0 432 24" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="328" x2="396" y1="56" y2="56"/>
|
||||
<line marker-end="url(#triangle)" x1="396" x2="396" y1="56" y2="108"/>
|
||||
<line x1="396" x2="432" y1="56" y2="56"/>
|
||||
<path d="M 432 56 A 4 4 0 0 0 436 52" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="340" x2="340" y1="124" y2="180"/>
|
||||
<path d="M 340 180 A 4 4 0 0 0 344 184" fill="none"/>
|
||||
<path d="M 344 120 A 4 4 0 0 0 340 124" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="344" x2="356" y1="120" y2="120"/>
|
||||
<line x1="356" x2="356" y1="80" y2="120"/>
|
||||
<line x1="356" x2="416" y1="120" y2="120"/>
|
||||
<path d="M 420 124 A 4 4 0 0 0 416 120" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="344" x2="380" y1="184" y2="184"/>
|
||||
<line marker-end="url(#triangle)" x1="380" x2="380" y1="184" y2="252"/>
|
||||
<line x1="380" x2="416" y1="184" y2="184"/>
|
||||
<path d="M 416 184 A 4 4 0 0 0 420 180" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="356" x2="356" y1="80" y2="68"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="356" x2="356" y1="268" y2="292"/>
|
||||
<path d="M 356 292 A 4 4 0 0 0 360 296" fill="none"/>
|
||||
<path d="M 360 264 A 4 4 0 0 0 356 268" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="360" x2="408" y1="264" y2="264"/>
|
||||
<path d="M 412 268 A 4 4 0 0 0 408 264" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="360" x2="408" y1="296" y2="296"/>
|
||||
<path d="M 408 296 A 4 4 0 0 0 412 292" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="412" x2="412" y1="268" y2="292"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="124" y2="180"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="420" x2="436" y1="152" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="436" x2="436" y1="28" y2="52"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="444" x2="444" y1="124" y2="180"/>
|
||||
<path d="M 444 180 A 4 4 0 0 0 448 184" fill="none"/>
|
||||
<path d="M 448 120 A 4 4 0 0 0 444 124" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="448" x2="536" y1="120" y2="120"/>
|
||||
<path d="M 540 124 A 4 4 0 0 0 536 120" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="448" x2="536" y1="184" y2="184"/>
|
||||
<path d="M 536 184 A 4 4 0 0 0 540 180" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="540" x2="540" y1="124" y2="180"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="540" x2="580" y1="152" y2="152"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="564" x2="564" y1="92" y2="144"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="564" x2="564" y1="160" y2="212"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="588" x2="588" y1="124" y2="180"/>
|
||||
<path d="M 588 180 A 4 4 0 0 0 592 184" fill="none"/>
|
||||
<path d="M 592 120 A 4 4 0 0 0 588 124" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="592" x2="688" y1="120" y2="120"/>
|
||||
<path d="M 692 124 A 4 4 0 0 0 688 120" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="592" x2="688" y1="184" y2="184"/>
|
||||
<path d="M 688 184 A 4 4 0 0 0 692 180" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="692" x2="692" y1="124" y2="180"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="25" y="156">
|
||||
Clients
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="121" y="108">
|
||||
TPU
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="140">
|
||||
Fetch
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="156">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="233" y="140">
|
||||
SigVerify
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="233" y="156">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="337" y="44">
|
||||
PoH
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="353" y="140">
|
||||
Banking
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="353" y="156">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="369" y="44">
|
||||
Service
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="369" y="284">
|
||||
Bank
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="457" y="140">
|
||||
Broadcast
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="457" y="156">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="601" y="140">
|
||||
Downstream
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="601" y="156">
|
||||
Validators
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.4 KiB |
311
book/src/.gitbook/assets/tvu.svg
Normal file
@@ -0,0 +1,311 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="352" width="616" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="352" width="616" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="156" y2="196"/>
|
||||
<path d="M 12 196 A 4 4 0 0 0 16 200" fill="none"/>
|
||||
<path d="M 16 152 A 4 4 0 0 0 12 156" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="112" y1="152" y2="152"/>
|
||||
<path d="M 116 156 A 4 4 0 0 0 112 152" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="112" y1="200" y2="200"/>
|
||||
<path d="M 112 200 A 4 4 0 0 0 116 196" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="116" x2="116" y1="156" y2="168"/>
|
||||
<line x1="116" x2="116" y1="168" y2="196"/>
|
||||
<line marker-end="url(#triangle)" x1="116" x2="164" y1="168" y2="168"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="148" x2="148" y1="92" y2="160"/>
|
||||
<path d="M 152 88 A 4 4 0 0 0 148 92" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="148" x2="148" y1="176" y2="244"/>
|
||||
<path d="M 148 244 A 4 4 0 0 0 152 248" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="152" x2="444" y1="88" y2="88"/>
|
||||
<line x1="444" x2="608" y1="88" y2="88"/>
|
||||
<path d="M 612 92 A 4 4 0 0 0 608 88" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="152" x2="220" y1="248" y2="248"/>
|
||||
<line x1="220" x2="308" y1="248" y2="248"/>
|
||||
<line x1="308" x2="444" y1="248" y2="248"/>
|
||||
<line x1="444" x2="608" y1="248" y2="248"/>
|
||||
<path d="M 608 248 A 4 4 0 0 0 612 244" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="172" x2="172" y1="140" y2="196"/>
|
||||
<path d="M 172 196 A 4 4 0 0 0 176 200" fill="none"/>
|
||||
<path d="M 176 136 A 4 4 0 0 0 172 140" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="176" x2="232" y1="136" y2="136"/>
|
||||
<path d="M 236 140 A 4 4 0 0 0 232 136" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="176" x2="232" y1="200" y2="200"/>
|
||||
<path d="M 232 200 A 4 4 0 0 0 236 196" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="212" x2="212" y1="300" y2="340"/>
|
||||
<path d="M 212 340 A 4 4 0 0 0 216 344" fill="none"/>
|
||||
<path d="M 216 296 A 4 4 0 0 0 212 300" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="216" x2="312" y1="344" y2="344"/>
|
||||
<path d="M 312 344 A 4 4 0 0 0 316 340" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="220" x2="220" y1="224" y2="212"/>
|
||||
<line x1="220" x2="220" y1="224" y2="296"/>
|
||||
<line x1="220" x2="216" y1="296" y2="296"/>
|
||||
<line x1="220" x2="312" y1="296" y2="296"/>
|
||||
<path d="M 316 300 A 4 4 0 0 0 312 296" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="236" x2="236" y1="140" y2="168"/>
|
||||
<line x1="236" x2="236" y1="168" y2="196"/>
|
||||
<line marker-end="url(#triangle)" x1="236" x2="260" y1="168" y2="168"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="140" y2="196"/>
|
||||
<path d="M 268 196 A 4 4 0 0 0 272 200" fill="none"/>
|
||||
<path d="M 272 136 A 4 4 0 0 0 268 140" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="136" y2="136"/>
|
||||
<path d="M 372 140 A 4 4 0 0 0 368 136" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="308" y1="200" y2="200"/>
|
||||
<line marker-end="url(#triangle)" x1="308" x2="308" y1="200" y2="284"/>
|
||||
<line x1="308" x2="368" y1="200" y2="200"/>
|
||||
<path d="M 368 200 A 4 4 0 0 0 372 196" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="316" x2="316" y1="300" y2="340"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="140" y2="168"/>
|
||||
<line x1="372" x2="372" y1="168" y2="196"/>
|
||||
<line marker-end="url(#triangle)" x1="372" x2="396" y1="168" y2="168"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="404" x2="404" y1="12" y2="36"/>
|
||||
<path d="M 404 36 A 4 4 0 0 0 408 40" fill="none"/>
|
||||
<path d="M 408 8 A 4 4 0 0 0 404 12" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="404" x2="404" y1="140" y2="196"/>
|
||||
<path d="M 404 196 A 4 4 0 0 0 408 200" fill="none"/>
|
||||
<path d="M 408 136 A 4 4 0 0 0 404 140" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="408" x2="472" y1="8" y2="8"/>
|
||||
<path d="M 476 12 A 4 4 0 0 0 472 8" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="408" x2="472" y1="40" y2="40"/>
|
||||
<path d="M 472 40 A 4 4 0 0 0 476 36" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="408" x2="444" y1="136" y2="136"/>
|
||||
<line x1="444" x2="444" y1="64" y2="136"/>
|
||||
<line x1="444" x2="472" y1="136" y2="136"/>
|
||||
<path d="M 476 140 A 4 4 0 0 0 472 136" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="408" x2="444" y1="200" y2="200"/>
|
||||
<line marker-end="url(#triangle)" x1="444" x2="444" y1="200" y2="284"/>
|
||||
<line x1="444" x2="472" y1="200" y2="200"/>
|
||||
<path d="M 472 200 A 4 4 0 0 0 476 196" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="300" y2="324"/>
|
||||
<path d="M 420 324 A 4 4 0 0 0 424 328" fill="none"/>
|
||||
<path d="M 424 296 A 4 4 0 0 0 420 300" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="424" x2="472" y1="296" y2="296"/>
|
||||
<path d="M 476 300 A 4 4 0 0 0 472 296" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="424" x2="472" y1="328" y2="328"/>
|
||||
<path d="M 472 328 A 4 4 0 0 0 476 324" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="444" x2="444" y1="64" y2="52"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="476" x2="476" y1="12" y2="36"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="476" x2="476" y1="140" y2="168"/>
|
||||
<line x1="476" x2="476" y1="168" y2="196"/>
|
||||
<line marker-end="url(#triangle)" x1="476" x2="500" y1="168" y2="168"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="476" x2="476" y1="300" y2="324"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="508" x2="508" y1="140" y2="196"/>
|
||||
<path d="M 508 196 A 4 4 0 0 0 512 200" fill="none"/>
|
||||
<path d="M 512 136 A 4 4 0 0 0 508 140" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="512" x2="584" y1="136" y2="136"/>
|
||||
<path d="M 588 140 A 4 4 0 0 0 584 136" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="512" x2="584" y1="200" y2="200"/>
|
||||
<path d="M 584 200 A 4 4 0 0 0 588 196" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="588" x2="588" y1="140" y2="196"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="612" x2="612" y1="92" y2="244"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="25" y="172">
|
||||
Upstream
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="25" y="188">
|
||||
Validators
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="169" y="108">
|
||||
TVU
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="185" y="156">
|
||||
Blob
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="185" y="172">
|
||||
Fetch
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="185" y="188">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="225" y="316">
|
||||
Gossip
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="225" y="332">
|
||||
Service
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="156">
|
||||
Retransmit
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="172">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="417" y="28">
|
||||
Leader
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="417" y="156">
|
||||
Replay
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="417" y="172">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="433" y="316">
|
||||
Bank
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="521" y="156">
|
||||
Storage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="521" y="172">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.7 KiB |
496
book/src/.gitbook/assets/validator-proposal.svg
Normal file
@@ -0,0 +1,496 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="960" width="528" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="960" width="528" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="188" y2="212"/>
|
||||
<path d="M 12 212 A 4 4 0 0 0 16 216" fill="none"/>
|
||||
<path d="M 16 184 A 4 4 0 0 0 12 188" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="80" y1="184" y2="184"/>
|
||||
<path d="M 84 188 A 4 4 0 0 0 80 184" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="80" y1="216" y2="216"/>
|
||||
<path d="M 80 216 A 4 4 0 0 0 84 212" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="20" x2="20" y1="748" y2="788"/>
|
||||
<path d="M 20 788 A 4 4 0 0 0 24 792" fill="none"/>
|
||||
<path d="M 24 744 A 4 4 0 0 0 20 748" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="24" x2="80" y1="744" y2="744"/>
|
||||
<path d="M 84 748 A 4 4 0 0 0 80 744" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="24" x2="80" y1="792" y2="792"/>
|
||||
<path d="M 80 792 A 4 4 0 0 0 84 788" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="84" x2="84" y1="188" y2="200"/>
|
||||
<line x1="84" x2="84" y1="200" y2="212"/>
|
||||
<line marker-end="url(#triangle)" x1="84" x2="124" y1="200" y2="200"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="84" x2="84" y1="748" y2="760"/>
|
||||
<line x1="84" x2="84" y1="760" y2="788"/>
|
||||
<line marker-end="url(#triangle)" x1="84" x2="124" y1="760" y2="760"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="124" y2="192"/>
|
||||
<path d="M 112 120 A 4 4 0 0 0 108 124" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="208" y2="436"/>
|
||||
<path d="M 108 436 A 4 4 0 0 0 112 440" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="700" y2="752"/>
|
||||
<path d="M 112 696 A 4 4 0 0 0 108 700" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="768" y2="836"/>
|
||||
<path d="M 108 836 A 4 4 0 0 0 112 840" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="392" y1="120" y2="120"/>
|
||||
<path d="M 396 124 A 4 4 0 0 0 392 120" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="392" y1="440" y2="440"/>
|
||||
<path d="M 392 440 A 4 4 0 0 0 396 436" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="392" y1="696" y2="696"/>
|
||||
<path d="M 396 700 A 4 4 0 0 0 392 696" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="392" y1="840" y2="840"/>
|
||||
<path d="M 392 840 A 4 4 0 0 0 396 836" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="132" x2="132" y1="172" y2="212"/>
|
||||
<path d="M 132 212 A 4 4 0 0 0 136 216" fill="none"/>
|
||||
<path d="M 136 168 A 4 4 0 0 0 132 172" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="132" x2="132" y1="268" y2="308"/>
|
||||
<path d="M 132 308 A 4 4 0 0 0 136 312" fill="none"/>
|
||||
<path d="M 136 264 A 4 4 0 0 0 132 268" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="132" x2="132" y1="748" y2="788"/>
|
||||
<path d="M 132 788 A 4 4 0 0 0 136 792" fill="none"/>
|
||||
<path d="M 136 744 A 4 4 0 0 0 132 748" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="224" y1="168" y2="168"/>
|
||||
<path d="M 228 172 A 4 4 0 0 0 224 168" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="164" y1="216" y2="216"/>
|
||||
<line marker-end="url(#triangle)" x1="164" x2="164" y1="216" y2="252"/>
|
||||
<line x1="164" x2="224" y1="216" y2="216"/>
|
||||
<path d="M 224 216 A 4 4 0 0 0 228 212" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="224" y1="264" y2="264"/>
|
||||
<path d="M 228 268 A 4 4 0 0 0 224 264" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="224" y1="312" y2="312"/>
|
||||
<path d="M 224 312 A 4 4 0 0 0 228 308" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="224" y1="744" y2="744"/>
|
||||
<path d="M 228 748 A 4 4 0 0 0 224 744" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="224" y1="792" y2="792"/>
|
||||
<path d="M 224 792 A 4 4 0 0 0 228 788" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="228" x2="228" y1="172" y2="212"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="228" x2="228" y1="268" y2="308"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="228" x2="228" y1="748" y2="760"/>
|
||||
<line x1="228" x2="228" y1="760" y2="788"/>
|
||||
<line marker-end="url(#triangle)" x1="228" x2="260" y1="760" y2="760"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="240" x2="236" y1="280" y2="280"/>
|
||||
<line marker-end="url(#triangle)" x1="240" x2="260" y1="280" y2="280"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="28" y2="68"/>
|
||||
<path d="M 268 68 A 4 4 0 0 0 272 72" fill="none"/>
|
||||
<path d="M 272 24 A 4 4 0 0 0 268 28" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="172" y2="212"/>
|
||||
<path d="M 268 212 A 4 4 0 0 0 272 216" fill="none"/>
|
||||
<path d="M 272 168 A 4 4 0 0 0 268 172" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="268" y2="308"/>
|
||||
<path d="M 268 308 A 4 4 0 0 0 272 312" fill="none"/>
|
||||
<path d="M 272 264 A 4 4 0 0 0 268 268" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="364" y2="404"/>
|
||||
<path d="M 268 404 A 4 4 0 0 0 272 408" fill="none"/>
|
||||
<path d="M 272 360 A 4 4 0 0 0 268 364" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="492" y2="532"/>
|
||||
<path d="M 268 532 A 4 4 0 0 0 272 536" fill="none"/>
|
||||
<path d="M 272 488 A 4 4 0 0 0 268 492" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="604" y2="644"/>
|
||||
<path d="M 268 644 A 4 4 0 0 0 272 648" fill="none"/>
|
||||
<path d="M 272 600 A 4 4 0 0 0 268 604" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="748" y2="788"/>
|
||||
<path d="M 268 788 A 4 4 0 0 0 272 792" fill="none"/>
|
||||
<path d="M 272 744 A 4 4 0 0 0 268 748" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="268" x2="268" y1="892" y2="932"/>
|
||||
<path d="M 268 932 A 4 4 0 0 0 272 936" fill="none"/>
|
||||
<path d="M 272 888 A 4 4 0 0 0 268 892" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="24" y2="24"/>
|
||||
<path d="M 372 28 A 4 4 0 0 0 368 24" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="308" y1="72" y2="72"/>
|
||||
<line x1="308" x2="308" y1="72" y2="112"/>
|
||||
<line x1="308" x2="368" y1="72" y2="72"/>
|
||||
<path d="M 368 72 A 4 4 0 0 0 372 68" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="168" y2="168"/>
|
||||
<path d="M 372 172 A 4 4 0 0 0 368 168" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="308" y1="216" y2="216"/>
|
||||
<line marker-end="url(#triangle)" x1="308" x2="308" y1="216" y2="252"/>
|
||||
<line x1="308" x2="368" y1="216" y2="216"/>
|
||||
<path d="M 368 216 A 4 4 0 0 0 372 212" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="264" y2="264"/>
|
||||
<path d="M 372 268 A 4 4 0 0 0 368 264" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="308" y1="312" y2="312"/>
|
||||
<line marker-end="url(#triangle)" x1="308" x2="308" y1="312" y2="348"/>
|
||||
<line x1="308" x2="368" y1="312" y2="312"/>
|
||||
<path d="M 368 312 A 4 4 0 0 0 372 308" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="360" y2="360"/>
|
||||
<path d="M 372 364 A 4 4 0 0 0 368 360" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="308" y1="408" y2="408"/>
|
||||
<line x1="308" x2="308" y1="408" y2="432"/>
|
||||
<line x1="308" x2="368" y1="408" y2="408"/>
|
||||
<path d="M 368 408 A 4 4 0 0 0 372 404" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="488" y2="488"/>
|
||||
<path d="M 372 492 A 4 4 0 0 0 368 488" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="536" y2="536"/>
|
||||
<path d="M 368 536 A 4 4 0 0 0 372 532" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="600" y2="600"/>
|
||||
<path d="M 372 604 A 4 4 0 0 0 368 600" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="332" y1="648" y2="648"/>
|
||||
<line x1="332" x2="332" y1="648" y2="688"/>
|
||||
<line x1="332" x2="368" y1="648" y2="648"/>
|
||||
<path d="M 368 648 A 4 4 0 0 0 372 644" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="300" y1="744" y2="744"/>
|
||||
<line x1="300" x2="300" y1="704" y2="744"/>
|
||||
<line x1="300" x2="368" y1="744" y2="744"/>
|
||||
<path d="M 372 748 A 4 4 0 0 0 368 744" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="316" y1="792" y2="792"/>
|
||||
<line x1="316" x2="316" y1="792" y2="832"/>
|
||||
<line x1="316" x2="368" y1="792" y2="792"/>
|
||||
<path d="M 368 792 A 4 4 0 0 0 372 788" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="888" y2="888"/>
|
||||
<path d="M 372 892 A 4 4 0 0 0 368 888" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="272" x2="368" y1="936" y2="936"/>
|
||||
<path d="M 368 936 A 4 4 0 0 0 372 932" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="300" x2="300" y1="672" y2="660"/>
|
||||
<line x1="300" x2="300" y1="672" y2="688"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="308" x2="308" y1="128" y2="156"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="308" x2="308" y1="448" y2="476"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="316" x2="316" y1="848" y2="876"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="332" x2="332" y1="704" y2="732"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="28" y2="68"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="172" y2="212"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="268" y2="308"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="364" y2="404"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="492" y2="532"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="604" y2="644"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="748" y2="788"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="372" x2="372" y1="892" y2="932"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="384" x2="380" y1="760" y2="760"/>
|
||||
<line marker-end="url(#triangle)" x1="384" x2="412" y1="760" y2="760"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="396" x2="396" y1="124" y2="436"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="396" x2="396" y1="700" y2="752"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="396" x2="396" y1="768" y2="836"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="748" y2="788"/>
|
||||
<path d="M 420 788 A 4 4 0 0 0 424 792" fill="none"/>
|
||||
<path d="M 424 744 A 4 4 0 0 0 420 748" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="424" x2="520" y1="744" y2="744"/>
|
||||
<path d="M 524 748 A 4 4 0 0 0 520 744" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="424" x2="520" y1="792" y2="792"/>
|
||||
<path d="M 520 792 A 4 4 0 0 0 524 788" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="524" x2="524" y1="748" y2="788"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="25" y="204">
|
||||
Client
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="33" y="764">
|
||||
Fetch
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="33" y="780">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="121" y="140">
|
||||
Validator
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="121" y="716">
|
||||
TPU
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="188">
|
||||
Fetch
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="204">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="284">
|
||||
TPU
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="764">
|
||||
SigVerify
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="780">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="44">
|
||||
Upstream
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="60">
|
||||
Validators
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="188">
|
||||
Repair
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="204">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="284">
|
||||
Blockstore
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="380">
|
||||
Multicast
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="396">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="508">
|
||||
Downstream
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="524">
|
||||
Validators
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="620">
|
||||
PoH
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="636">
|
||||
Service
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="764">
|
||||
Banking
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="780">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="281" y="908">
|
||||
Banktree
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="433" y="764">
|
||||
Blockstore
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
456
book/src/.gitbook/assets/validator.svg
Normal file
@@ -0,0 +1,456 @@
|
||||
<svg class="bob" font-family="arial" font-size="14" height="480" width="592" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="triangle" markerHeight="8" markerWidth="8" orient="auto" refX="4" refY="2" viewBox="0 0 8 4">
|
||||
<polygon fill="black" points="0,0 0,4 8,2 0,0"/>
|
||||
</marker>
|
||||
<marker id="clear_triangle" markerHeight="10" markerWidth="10" orient="auto" refX="1" refY="7" viewBox="0 0 20 14">
|
||||
<polygon fill="none" points="2,2 2,12 18,7 2,2" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="circle" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="black" r="8"/>
|
||||
</marker>
|
||||
<marker id="square" markerHeight="5" markerWidth="5" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<rect fill="black" height="20" width="20" x="0" y="0"/>
|
||||
</marker>
|
||||
<marker id="open_circle" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="10" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" fill="white" r="4" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
<marker id="big_open_circle" markerHeight="20" markerWidth="20" orient="auto" refX="20" refY="20" viewBox="0 0 40 40">
|
||||
<circle cx="20" cy="20" fill="white" r="6" stroke="black" stroke-width="2"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style type="text/css">
|
||||
|
||||
line,path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
line.dashed {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
circle.solid {
|
||||
fill:black;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
circle.open {
|
||||
fill:none;
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: miter;
|
||||
}
|
||||
tspan.head{
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<rect fill="white" height="480" width="592" x="0" y="0"/>
|
||||
<g>
|
||||
<line x1="12" x2="12" y1="60" y2="116"/>
|
||||
<path d="M 12 116 A 4 4 0 0 0 16 120" fill="none"/>
|
||||
<path d="M 16 56 A 4 4 0 0 0 12 60" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="80" y1="56" y2="56"/>
|
||||
<path d="M 84 60 A 4 4 0 0 0 80 56" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="16" x2="52" y1="120" y2="120"/>
|
||||
<line x1="52" x2="52" y1="120" y2="420"/>
|
||||
<line x1="52" x2="80" y1="120" y2="120"/>
|
||||
<path d="M 52 420 A 4 4 0 0 0 56 424" fill="none"/>
|
||||
<path d="M 80 120 A 4 4 0 0 0 84 116" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="56" x2="124" y1="424" y2="424"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="84" x2="84" y1="60" y2="116"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="84" x2="124" y1="72" y2="72"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="96" x2="92" y1="104" y2="104"/>
|
||||
<line x1="96" x2="132" y1="104" y2="104"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="12" y2="64"/>
|
||||
<path d="M 112 8 A 4 4 0 0 0 108 12" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="80" y2="96"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="112" y2="416"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="108" x2="108" y1="432" y2="468"/>
|
||||
<path d="M 108 468 A 4 4 0 0 0 112 472" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="416" y1="8" y2="8"/>
|
||||
<path d="M 420 12 A 4 4 0 0 0 416 8" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="112" x2="416" y1="472" y2="472"/>
|
||||
<path d="M 416 472 A 4 4 0 0 0 420 468" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="124" x2="124" y1="236" y2="276"/>
|
||||
<path d="M 124 276 A 4 4 0 0 0 128 280" fill="none"/>
|
||||
<path d="M 128 232 A 4 4 0 0 0 124 236" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="128" x2="156" y1="232" y2="232"/>
|
||||
<line x1="156" x2="156" y1="144" y2="232"/>
|
||||
<line x1="156" x2="184" y1="232" y2="232"/>
|
||||
<path d="M 188 236 A 4 4 0 0 0 184 232" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="128" x2="184" y1="280" y2="280"/>
|
||||
<path d="M 184 280 A 4 4 0 0 0 188 276" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="132" x2="132" y1="60" y2="116"/>
|
||||
<path d="M 132 116 A 4 4 0 0 0 136 120" fill="none"/>
|
||||
<path d="M 136 56 A 4 4 0 0 0 132 60" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="132" x2="132" y1="412" y2="436"/>
|
||||
<path d="M 132 436 A 4 4 0 0 0 136 440" fill="none"/>
|
||||
<path d="M 136 408 A 4 4 0 0 0 132 412" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="288" y1="56" y2="56"/>
|
||||
<path d="M 292 60 A 4 4 0 0 0 288 56" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="288" y1="120" y2="120"/>
|
||||
<path d="M 288 120 A 4 4 0 0 0 292 116" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="156" y1="408" y2="408"/>
|
||||
<line x1="156" x2="156" y1="304" y2="408"/>
|
||||
<line x1="156" x2="176" y1="408" y2="408"/>
|
||||
<path d="M 180 412 A 4 4 0 0 0 176 408" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="136" x2="176" y1="440" y2="440"/>
|
||||
<path d="M 176 440 A 4 4 0 0 0 180 436" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="156" x2="156" y1="144" y2="132"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="156" x2="156" y1="304" y2="292"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="180" x2="180" y1="412" y2="424"/>
|
||||
<line x1="180" x2="180" y1="424" y2="436"/>
|
||||
<line marker-end="url(#triangle)" x1="180" x2="220" y1="424" y2="424"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="188" x2="188" y1="236" y2="276"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="200" x2="196" y1="248" y2="248"/>
|
||||
<line x1="200" x2="212" y1="248" y2="248"/>
|
||||
<line x1="212" x2="212" y1="236" y2="248"/>
|
||||
<line x1="212" x2="212" y1="248" y2="276"/>
|
||||
<path d="M 212 276 A 4 4 0 0 0 216 280" fill="none"/>
|
||||
<path d="M 216 232 A 4 4 0 0 0 212 236" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="204" x2="204" y1="156" y2="180"/>
|
||||
<path d="M 204 180 A 4 4 0 0 0 208 184" fill="none"/>
|
||||
<path d="M 208 152 A 4 4 0 0 0 204 156" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="208" x2="336" y1="152" y2="152"/>
|
||||
<path d="M 340 156 A 4 4 0 0 0 336 152" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="208" x2="336" y1="184" y2="184"/>
|
||||
<path d="M 336 184 A 4 4 0 0 0 340 180" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="216" x2="252" y1="232" y2="232"/>
|
||||
<line x1="252" x2="252" y1="208" y2="232"/>
|
||||
<line x1="252" x2="280" y1="232" y2="232"/>
|
||||
<path d="M 284 236 A 4 4 0 0 0 280 232" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="216" x2="280" y1="280" y2="280"/>
|
||||
<path d="M 280 280 A 4 4 0 0 0 284 276" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="228" x2="228" y1="412" y2="452"/>
|
||||
<path d="M 228 452 A 4 4 0 0 0 232 456" fill="none"/>
|
||||
<path d="M 232 408 A 4 4 0 0 0 228 412" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="232" x2="292" y1="408" y2="408"/>
|
||||
<line x1="292" x2="292" y1="384" y2="408"/>
|
||||
<line x1="292" x2="320" y1="408" y2="408"/>
|
||||
<path d="M 324 412 A 4 4 0 0 0 320 408" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="232" x2="320" y1="456" y2="456"/>
|
||||
<path d="M 320 456 A 4 4 0 0 0 324 452" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="252" x2="252" y1="208" y2="196"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="252" x2="252" y1="332" y2="356"/>
|
||||
<path d="M 252 356 A 4 4 0 0 0 256 360" fill="none"/>
|
||||
<path d="M 256 328 A 4 4 0 0 0 252 332" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="256" x2="276" y1="328" y2="328"/>
|
||||
<line x1="276" x2="276" y1="304" y2="328"/>
|
||||
<line x1="276" x2="344" y1="328" y2="328"/>
|
||||
<path d="M 348 332 A 4 4 0 0 0 344 328" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="256" x2="344" y1="360" y2="360"/>
|
||||
<path d="M 344 360 A 4 4 0 0 0 348 356" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="276" x2="276" y1="304" y2="292"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="284" x2="284" y1="236" y2="276"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="292" x2="292" y1="60" y2="116"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="292" x2="292" y1="384" y2="372"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="300" x2="300" y1="236" y2="276"/>
|
||||
<path d="M 300 276 A 4 4 0 0 0 304 280" fill="none"/>
|
||||
<path d="M 304 232 A 4 4 0 0 0 300 236" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="304" x2="392" y1="232" y2="232"/>
|
||||
<path d="M 396 236 A 4 4 0 0 0 392 232" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="304" x2="324" y1="280" y2="280"/>
|
||||
<line marker-end="url(#triangle)" x1="324" x2="324" y1="280" y2="316"/>
|
||||
<line x1="324" x2="392" y1="280" y2="280"/>
|
||||
<path d="M 392 280 A 4 4 0 0 0 396 276" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="324" x2="324" y1="412" y2="424"/>
|
||||
<line x1="324" x2="324" y1="424" y2="452"/>
|
||||
<line marker-end="url(#triangle)" x1="324" x2="452" y1="424" y2="424"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="340" x2="340" y1="156" y2="180"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="348" x2="348" y1="332" y2="356"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="352" x2="348" y1="168" y2="168"/>
|
||||
<line x1="352" x2="436" y1="168" y2="168"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="396" x2="396" y1="236" y2="276"/>
|
||||
</g>
|
||||
<g>
|
||||
<line marker-end="url(#triangle)" x1="408" x2="404" y1="248" y2="248"/>
|
||||
<line x1="408" x2="460" y1="248" y2="248"/>
|
||||
<line x1="460" x2="460" y1="220" y2="248"/>
|
||||
<line x1="460" x2="460" y1="248" y2="292"/>
|
||||
<path d="M 460 292 A 4 4 0 0 0 464 296" fill="none"/>
|
||||
<path d="M 464 216 A 4 4 0 0 0 460 220" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="12" y2="160"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="176" y2="240"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="256" y2="416"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="420" x2="420" y1="432" y2="468"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="436" x2="436" y1="156" y2="240"/>
|
||||
<path d="M 440 152 A 4 4 0 0 0 436 156" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="436" x2="436" y1="256" y2="416"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="436" x2="436" y1="432" y2="452"/>
|
||||
<path d="M 436 452 A 4 4 0 0 0 440 456" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="440" x2="584" y1="152" y2="152"/>
|
||||
<path d="M 588 156 A 4 4 0 0 0 584 152" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="440" x2="584" y1="456" y2="456"/>
|
||||
<path d="M 584 456 A 4 4 0 0 0 588 452" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="460" x2="460" y1="364" y2="436"/>
|
||||
<path d="M 460 436 A 4 4 0 0 0 464 440" fill="none"/>
|
||||
<path d="M 464 360 A 4 4 0 0 0 460 364" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="464" x2="560" y1="216" y2="216"/>
|
||||
<path d="M 564 220 A 4 4 0 0 0 560 216" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="464" x2="560" y1="296" y2="296"/>
|
||||
<path d="M 560 296 A 4 4 0 0 0 564 292" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="464" x2="560" y1="360" y2="360"/>
|
||||
<path d="M 564 364 A 4 4 0 0 0 560 360" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="464" x2="560" y1="440" y2="440"/>
|
||||
<path d="M 560 440 A 4 4 0 0 0 564 436" fill="none"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="564" x2="564" y1="220" y2="292"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="564" x2="564" y1="364" y2="436"/>
|
||||
</g>
|
||||
<g>
|
||||
<line x1="588" x2="588" y1="156" y2="452"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="25" y="92">
|
||||
Client
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="129" y="28">
|
||||
Validator
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="252">
|
||||
Bank
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="137" y="268">
|
||||
Forks
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="92">
|
||||
JSON
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="145" y="428">
|
||||
TPU
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="185" y="92">
|
||||
RPC
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="217" y="92">
|
||||
Service
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="217" y="172">
|
||||
Gossip
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="225" y="252">
|
||||
Replay
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="225" y="268">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="241" y="428">
|
||||
Broadcast
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="241" y="444">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="265" y="348">
|
||||
Blocktree
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="273" y="172">
|
||||
Service
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="313" y="252">
|
||||
BlobFetch
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="321" y="268">
|
||||
Stage
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="449" y="172">
|
||||
Validators
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="252">
|
||||
Upstream
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="268">
|
||||
Validators
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="396">
|
||||
Downstream
|
||||
</text>
|
||||
</g>
|
||||
<g>
|
||||
<text x="473" y="412">
|
||||
Validators
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
@@ -1,31 +1,14 @@
|
||||
# Table of contents
|
||||
|
||||
* [Introduction](introduction.md)
|
||||
* [Using Solana from the Command-line](cli/README.md)
|
||||
* [Command-line Usage](cli/usage.md)
|
||||
* [Paper Wallet](paper-wallet/README.md)
|
||||
* [Installation](paper-wallet/installation.md)
|
||||
* [Paper Wallet Usage](paper-wallet/usage.md)
|
||||
* [Offline Signing](offline-signing/README.md)
|
||||
* [Durable Transaction Nonces](offline-signing/durable-nonce.md)
|
||||
* [Developing Applications](apps/README.md)
|
||||
* [Example: Web Wallet](apps/webwallet.md)
|
||||
* [Example: Tic-Tac-Toe](apps/tictactoe.md)
|
||||
* [Drones](apps/drones.md)
|
||||
* [Anatomy of a Transaction](transaction.md)
|
||||
* [JSON RPC API](apps/jsonrpc-api.md)
|
||||
* [JavaScript API](apps/javascript-api.md)
|
||||
* [Running a Validator](running-validator/README.md)
|
||||
* [Validator Requirements](running-validator/validator-reqs.md)
|
||||
* [Choosing a Testnet](running-validator/validator-testnet.md)
|
||||
* [Installing the Validator Software](running-validator/validator-software.md)
|
||||
* [Starting a Validator](running-validator/validator-start.md)
|
||||
* [Staking](running-validator/validator-stake.md)
|
||||
* [Monitoring a Validator](running-validator/validator-monitor.md)
|
||||
* [Publishing Validator Info](running-validator/validator-info.md)
|
||||
* [Troubleshooting](running-validator/validator-troubleshoot.md)
|
||||
* [Running an Archiver](running-archiver.md)
|
||||
* [Understanding Solana's Architecture](cluster/README.md)
|
||||
* [Terminology](terminology.md)
|
||||
* [Getting Started](getting-started/README.md)
|
||||
* [Testnet Participation](getting-started/testnet-participation.md)
|
||||
* [Example Client: Web Wallet](getting-started/webwallet.md)
|
||||
* [Programming Model](programs/README.md)
|
||||
* [Example: Tic-Tac-Toe](programs/tictactoe.md)
|
||||
* [Drones](programs/drones.md)
|
||||
* [A Solana Cluster](cluster/README.md)
|
||||
* [Synchronization](cluster/synchronization.md)
|
||||
* [Leader Rotation](cluster/leader-rotation.md)
|
||||
* [Fork Generation](cluster/fork-generation.md)
|
||||
@@ -37,55 +20,62 @@
|
||||
* [Performance Metrics](cluster/performance-metrics.md)
|
||||
* [Anatomy of a Validator](validator/README.md)
|
||||
* [TPU](validator/tpu.md)
|
||||
* [TVU](validator/tvu.md)
|
||||
* [Blockstore](validator/blockstore.md)
|
||||
* [TVU](validator/tvu/README.md)
|
||||
* [Blocktree](validator/tvu/blocktree.md)
|
||||
* [Gossip Service](validator/gossip.md)
|
||||
* [The Runtime](validator/runtime.md)
|
||||
* [Building from Source](building-from-source.md)
|
||||
* [Terminology](terminology.md)
|
||||
* [Implemented Design Proposals](implemented-proposals/README.md)
|
||||
* [Cluster Software Installation and Updates](implemented-proposals/installer.md)
|
||||
* [Cluster Economics](implemented-proposals/ed_overview/README.md)
|
||||
* [Validation-client Economics](implemented-proposals/ed_overview/ed_validation_client_economics/README.md)
|
||||
* [State-validation Protocol-based Rewards](implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md)
|
||||
* [State-validation Transaction Fees](implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_transaction_fees.md)
|
||||
* [Replication-validation Transaction Fees](implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_replication_validation_transaction_fees.md)
|
||||
* [Validation Stake Delegation](implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_validation_stake_delegation.md)
|
||||
* [Replication-client Economics](implemented-proposals/ed_overview/ed_replication_client_economics/README.md)
|
||||
* [Storage-replication Rewards](implemented-proposals/ed_overview/ed_replication_client_economics/ed_rce_storage_replication_rewards.md)
|
||||
* [Replication-client Reward Auto-delegation](implemented-proposals/ed_overview/ed_replication_client_economics/ed_rce_replication_client_reward_auto_delegation.md)
|
||||
* [Storage Rent Economics](implemented-proposals/ed_overview/ed_storage_rent_economics.md)
|
||||
* [Economic Sustainability](implemented-proposals/ed_overview/ed_economic_sustainability.md)
|
||||
* [Attack Vectors](implemented-proposals/ed_overview/ed_attack_vectors.md)
|
||||
* [Economic Design MVP](implemented-proposals/ed_overview/ed_mvp.md)
|
||||
* [References](implemented-proposals/ed_overview/ed_references.md)
|
||||
* [Deterministic Transaction Fees](implemented-proposals/transaction-fees.md)
|
||||
* [Tower BFT](implemented-proposals/tower-bft.md)
|
||||
* [Leader-to-Leader Transition](implemented-proposals/leader-leader-transition.md)
|
||||
* [Leader-to-Validator Transition](implemented-proposals/leader-validator-transition.md)
|
||||
* [Persistent Account Storage](implemented-proposals/persistent-account-storage.md)
|
||||
* [Reliable Vote Transmission](implemented-proposals/reliable-vote-transmission.md)
|
||||
* [Repair Service](implemented-proposals/repair-service.md)
|
||||
* [Testing Programs](implemented-proposals/testing-programs.md)
|
||||
* [Credit-only Accounts](implemented-proposals/readonly-accounts.md)
|
||||
* [Embedding the Move Langauge](implemented-proposals/embedding-move.md)
|
||||
* [Staking Rewards](implemented-proposals/staking-rewards.md)
|
||||
* [Rent](implemented-proposals/rent.md)
|
||||
* [Durable Transaction Nonces](implemented-proposals/durable-tx-nonces.md)
|
||||
* [Validator Timestamp Oracle](implemented-proposals/validator-timestamp-oracle.md)
|
||||
* [Commitment](implemented-proposals/commitment.md)
|
||||
* [Snapshot Verification](implemented-proposals/snapshot-verification.md)
|
||||
* [Anatomy of a Transaction](transaction.md)
|
||||
* [Running a Validator](running-validator/README.md)
|
||||
* [Hardware Requirements](running-validator/validator-hardware.md)
|
||||
* [Choosing a Testnet](running-validator/validator-testnet.md)
|
||||
* [Installing the Validator Software](running-validator/validator-software.md)
|
||||
* [Starting a Validator](running-validator/validator-start.md)
|
||||
* [Staking](running-validator/validator-stake.md)
|
||||
* [Monitoring a Validator](running-validator/validator-monitor.md)
|
||||
* [Publishing Validator Info](running-validator/validator-info.md)
|
||||
* [Troubleshooting](running-validator/validator-troubleshoot.md)
|
||||
* [FAQ](running-validator/validator-faq.md)
|
||||
* [Running a Replicator](running-replicator.md)
|
||||
* [API Reference](api-reference/README.md)
|
||||
* [Transaction](api-reference/transaction-api.md)
|
||||
* [Instruction](api-reference/instruction-api.md)
|
||||
* [Blockstreamer](api-reference/blockstreamer.md)
|
||||
* [JSON RPC API](api-reference/jsonrpc-api.md)
|
||||
* [JavaScript API](api-reference/javascript-api.md)
|
||||
* [solana CLI](api-reference/cli.md)
|
||||
* [Accepted Design Proposals](proposals/README.md)
|
||||
* [Ledger Replication](proposals/ledger-replication-to-implement.md)
|
||||
* [Secure Vote Signing](proposals/vote-signing-to-implement.md)
|
||||
* [Staking Rewards](proposals/staking-rewards.md)
|
||||
* [Cluster Economics](proposals/ed_overview/README.md)
|
||||
* [Validation-client Economics](proposals/ed_overview/ed_validation_client_economics/README.md)
|
||||
* [State-validation Protocol-based Rewards](proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md)
|
||||
* [State-validation Transaction Fees](proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_transaction_fees.md)
|
||||
* [Replication-validation Transaction Fees](proposals/ed_overview/ed_validation_client_economics/ed_vce_replication_validation_transaction_fees.md)
|
||||
* [Validation Stake Delegation](proposals/ed_overview/ed_validation_client_economics/ed_vce_validation_stake_delegation.md)
|
||||
* [Replication-client Economics](proposals/ed_overview/ed_replication_client_economics/README.md)
|
||||
* [Storage-replication Rewards](proposals/ed_overview/ed_replication_client_economics/ed_rce_storage_replication_rewards.md)
|
||||
* [Replication-client Reward Auto-delegation](proposals/ed_overview/ed_replication_client_economics/ed_rce_replication_client_reward_auto_delegation.md)
|
||||
* [Economic Sustainability](proposals/ed_overview/ed_economic_sustainability.md)
|
||||
* [Attack Vectors](proposals/ed_overview/ed_attack_vectors.md)
|
||||
* [Economic Design MVP](proposals/ed_overview/ed_mvp.md)
|
||||
* [References](proposals/ed_overview/ed_references.md)
|
||||
* [Cluster Test Framework](proposals/cluster-test-framework.md)
|
||||
* [Validator](proposals/validator-proposal.md)
|
||||
* [Simple Payment and State Verification](proposals/simple-payment-and-state-verification.md)
|
||||
* [Cross-Program Invocation](proposals/cross-program-invocation.md)
|
||||
* [Inter-chain Transaction Verification](proposals/interchain-transaction-verification.md)
|
||||
* [Snapshot Verification](proposals/snapshot-verification.md)
|
||||
* [Bankless Leader](proposals/bankless-leader.md)
|
||||
* [Slashing](proposals/slashing.md)
|
||||
* [Tick Verification](proposals/tick-verification.md)
|
||||
* [Block Confirmation](proposals/block-confirmation.md)
|
||||
* [ABI Management](proposals/abi-management.md)
|
||||
* [Implemented Design Proposals](implemented-proposals/README.md)
|
||||
* [Blocktree](implemented-proposals/blocktree.md)
|
||||
* [Cluster Software Installation and Updates](implemented-proposals/installer.md)
|
||||
* [Deterministic Transaction Fees](implemented-proposals/transaction-fees.md)
|
||||
* [Tower BFT](implemented-proposals/tower-bft.md)
|
||||
* [Leader-to-Leader Transition](implemented-proposals/leader-leader-transition.md)
|
||||
* [Leader-to-Validator Transition](implemented-proposals/leader-validator-transition.md)
|
||||
* [Passive Stake Delegation and Rewards](implemented-proposals/passive-stake-delegation-and-rewards.md)
|
||||
* [Persistent Account Storage](implemented-proposals/persistent-account-storage.md)
|
||||
* [Reliable Vote Transmission](implemented-proposals/reliable-vote-transmission.md)
|
||||
* [Repair Service](implemented-proposals/repair-service.md)
|
||||
* [Testing Programs](implemented-proposals/testing-programs.md)
|
||||
* [Credit-only Accounts](implemented-proposals/credit-only-credit-debit-accounts.md)
|
||||
* [Embedding the Move Langauge](implemented-proposals/embedding-move.md)
|
||||
|
||||
|
4
book/src/api-reference.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# API Reference
|
||||
|
||||
The following sections contain API references material you may find useful
|
||||
when developing applications utilizing a Solana cluster.
|
4
book/src/api-reference/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# API Reference
|
||||
|
||||
The following sections contain API references material you may find useful when developing applications utilizing a Solana cluster.
|
||||
|
28
book/src/api-reference/blockstreamer.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Blockstreamer
|
||||
|
||||
Solana supports a node type called an _blockstreamer_. This fullnode variation is intended for applications that need to observe the data plane without participating in transaction validation or ledger replication.
|
||||
|
||||
A blockstreamer runs without a vote signer, and can optionally stream ledger entries out to a Unix domain socket as they are processed. The JSON-RPC service still functions as on any other node.
|
||||
|
||||
To run a blockstreamer, include the argument `no-signer` and \(optional\) `blockstream` socket location:
|
||||
|
||||
```bash
|
||||
$ ./multinode-demo/validator-x.sh --no-signer --blockstream <SOCKET>
|
||||
```
|
||||
|
||||
The stream will output a series of JSON objects:
|
||||
|
||||
* An Entry event JSON object is sent when each ledger entry is processed, with the following fields:
|
||||
* `dt`, the system datetime, as RFC3339-formatted string
|
||||
* `t`, the event type, always "entry"
|
||||
* `s`, the slot height, as unsigned 64-bit integer
|
||||
* `h`, the tick height, as unsigned 64-bit integer
|
||||
* `entry`, the entry, as JSON object
|
||||
* A Block event JSON object is sent when a block is complete, with the following fields:
|
||||
* `dt`, the system datetime, as RFC3339-formatted string
|
||||
* `t`, the event type, always "block"
|
||||
* `s`, the slot height, as unsigned 64-bit integer
|
||||
* `h`, the tick height, as unsigned 64-bit integer
|
||||
* `l`, the slot leader id, as base-58 encoded string
|
||||
* `id`, the block id, as base-58 encoded string
|
||||
|
370
book/src/api-reference/cli.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# solana CLI
|
||||
|
||||
The [solana-cli crate](https://crates.io/crates/solana-cli) provides a command-line interface tool for Solana
|
||||
|
||||
## Examples
|
||||
|
||||
### Get Pubkey
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana address
|
||||
|
||||
// Return
|
||||
<PUBKEY>
|
||||
```
|
||||
|
||||
### Airdrop Lamports
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana airdrop 123
|
||||
|
||||
// Return
|
||||
"Your balance is: 123"
|
||||
```
|
||||
|
||||
### Get Balance
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana balance
|
||||
|
||||
// Return
|
||||
"Your balance is: 123"
|
||||
```
|
||||
|
||||
### Confirm Transaction
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana confirm <TX_SIGNATURE>
|
||||
|
||||
// Return
|
||||
"Confirmed" / "Not found" / "Transaction failed with error <ERR>"
|
||||
```
|
||||
|
||||
### Deploy program
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana deploy <PATH>
|
||||
|
||||
// Return
|
||||
<PROGRAM_ID>
|
||||
```
|
||||
|
||||
### Unconditional Immediate Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
### Post-Dated Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--after 2018-12-24T23:59:00 --require-timestamp-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
_`require-timestamp-from` is optional. If not provided, the transaction will expect a timestamp signed by this wallet's secret key_
|
||||
|
||||
### Authorized Transfer
|
||||
|
||||
A third party must send a signature to unlock the lamports.
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Post-Dated and Authorized Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--after 2018-12-24T23:59 --require-timestamp-from <PUBKEY> \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Multiple Witnesses
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY> \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Cancelable Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY> \
|
||||
--cancelable
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Cancel Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana cancel <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
### Send Signature
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana send-signature <PUBKEY> <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
### Indicate Elapsed Time
|
||||
|
||||
Use the current system time:
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana send-timestamp <PUBKEY> <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
Or specify some other arbitrary timestamp:
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
solana 0.12.0
|
||||
|
||||
USAGE:
|
||||
solana [FLAGS] [OPTIONS] [SUBCOMMAND]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--rpc-tls Enable TLS for the RPC endpoint
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--drone-host <IP ADDRESS> Drone host to use [default: same as --host]
|
||||
--drone-port <PORT> Drone port to use [default: 9900]
|
||||
-n, --host <IP ADDRESS> Host to use for both RPC and drone [default: 127.0.0.1]
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
--rpc-host <IP ADDRESS> RPC host to use [default: same as --host]
|
||||
--rpc-port <PORT> RPC port to use [default: 8899]
|
||||
|
||||
SUBCOMMANDS:
|
||||
address Get your public key
|
||||
airdrop Request a batch of lamports
|
||||
balance Get your balance
|
||||
cancel Cancel a transfer
|
||||
confirm Confirm transaction by signature
|
||||
deploy Deploy a program
|
||||
get-transaction-count Get current transaction count
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
pay Send a payment
|
||||
send-signature Send a signature to authorize a transfer
|
||||
send-timestamp Send a timestamp to unlock a transfer
|
||||
```
|
||||
|
||||
```text
|
||||
solana-address
|
||||
Get your public key
|
||||
|
||||
USAGE:
|
||||
solana address
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
```
|
||||
|
||||
```text
|
||||
solana-airdrop
|
||||
Request a batch of lamports
|
||||
|
||||
USAGE:
|
||||
solana airdrop <NUM>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
ARGS:
|
||||
<NUM> The number of lamports to request
|
||||
```
|
||||
|
||||
```text
|
||||
solana-balance
|
||||
Get your balance
|
||||
|
||||
USAGE:
|
||||
solana balance
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
```
|
||||
|
||||
```text
|
||||
solana-cancel
|
||||
Cancel a transfer
|
||||
|
||||
USAGE:
|
||||
solana cancel <PROCESS_ID>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
ARGS:
|
||||
<PROCESS_ID> The process id of the transfer to cancel
|
||||
```
|
||||
|
||||
```text
|
||||
solana-confirm
|
||||
Confirm transaction by signature
|
||||
|
||||
USAGE:
|
||||
solana confirm <SIGNATURE>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
ARGS:
|
||||
<SIGNATURE> The transaction signature to confirm
|
||||
```
|
||||
|
||||
```text
|
||||
solana-deploy
|
||||
Deploy a program
|
||||
|
||||
USAGE:
|
||||
solana deploy <PATH>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
ARGS:
|
||||
<PATH> /path/to/program.o
|
||||
```
|
||||
|
||||
```text
|
||||
solana-fees
|
||||
Display current cluster fees
|
||||
|
||||
USAGE:
|
||||
solana fees
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
```
|
||||
|
||||
```text
|
||||
solana-get-transaction-count
|
||||
Get current transaction count
|
||||
|
||||
USAGE:
|
||||
solana get-transaction-count
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
```
|
||||
|
||||
```text
|
||||
solana-pay
|
||||
Send a payment
|
||||
|
||||
USAGE:
|
||||
solana pay [FLAGS] [OPTIONS] <PUBKEY> <NUM>
|
||||
|
||||
FLAGS:
|
||||
--cancelable
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--after <DATETIME> A timestamp after which transaction will execute
|
||||
--require-timestamp-from <PUBKEY> Require timestamp from this third party
|
||||
--require-signature-from <PUBKEY>... Any third party signatures required to unlock the lamports
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The pubkey of recipient
|
||||
<NUM> The number of lamports to send
|
||||
```
|
||||
|
||||
```text
|
||||
solana-send-signature
|
||||
Send a signature to authorize a transfer
|
||||
|
||||
USAGE:
|
||||
solana send-signature <PUBKEY> <PROCESS_ID>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The pubkey of recipient
|
||||
<PROCESS_ID> The process id of the transfer to authorize
|
||||
```
|
||||
|
||||
```text
|
||||
solana-send-timestamp
|
||||
Send a timestamp to unlock a transfer
|
||||
|
||||
USAGE:
|
||||
solana send-timestamp [OPTIONS] <PUBKEY> <PROCESS_ID>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--date <DATETIME> Optional arbitrary timestamp to apply
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The pubkey of recipient
|
||||
<PROCESS_ID> The process id of the transfer to unlock
|
||||
```
|
||||
|
38
book/src/api-reference/instruction-api.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Instruction
|
||||
|
||||
For the purposes of building a [Transaction](../transaction.md), a more verbose instruction format is used:
|
||||
|
||||
* **Instruction:**
|
||||
* **program\_id:** The pubkey of the on-chain program that executes the
|
||||
|
||||
instruction
|
||||
|
||||
* **accounts:** An ordered list of accounts that should be passed to
|
||||
|
||||
the program processing the instruction, including metadata detailing
|
||||
|
||||
if an account is a signer of the transaction and if it is a credit
|
||||
|
||||
only account.
|
||||
|
||||
* **data:** A byte array that is passed to the program executing the
|
||||
|
||||
instruction
|
||||
|
||||
A more compact form is actually included in a `Transaction`:
|
||||
|
||||
* **CompiledInstruction:**
|
||||
* **program\_id\_index:** The index of the `program_id` in the
|
||||
|
||||
`account_keys` list
|
||||
|
||||
* **accounts:** An ordered list of indices into `account_keys`
|
||||
|
||||
specifying the accounds that should be passed to the program
|
||||
|
||||
processing the instruction.
|
||||
|
||||
* **data:** A byte array that is passed to the program executing the
|
||||
|
||||
instruction
|
||||
|
788
book/src/api-reference/jsonrpc-api.md
Normal file
@@ -0,0 +1,788 @@
|
||||
# JSON RPC API
|
||||
|
||||
Solana nodes accept HTTP requests using the [JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification.
|
||||
|
||||
To interact with a Solana node inside a JavaScript application, use the [solana-web3.js](https://github.com/solana-labs/solana-web3.js) library, which gives a convenient interface for the RPC methods.
|
||||
|
||||
## RPC HTTP Endpoint
|
||||
|
||||
**Default port:** 8899 eg. [http://localhost:8899](http://localhost:8899), [http://192.168.1.88:8899](http://192.168.1.88:8899)
|
||||
|
||||
## RPC PubSub WebSocket Endpoint
|
||||
|
||||
**Default port:** 8900 eg. ws://localhost:8900, [http://192.168.1.88:8900](http://192.168.1.88:8900)
|
||||
|
||||
## Methods
|
||||
|
||||
* [confirmTransaction](jsonrpc-api.md#confirmtransaction)
|
||||
* [getAccountInfo](jsonrpc-api.md#getaccountinfo)
|
||||
* [getBalance](jsonrpc-api.md#getbalance)
|
||||
* [getClusterNodes](jsonrpc-api.md#getclusternodes)
|
||||
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
|
||||
* [getGenesisBlockhash](jsonrpc-api.md#getgenesisblockhash)
|
||||
* [getLeaderSchedule](jsonrpc-api.md#getleaderschedule)
|
||||
* [getProgramAccounts](jsonrpc-api.md#getprogramaccounts)
|
||||
* [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash)
|
||||
* [getSignatureStatus](jsonrpc-api.md#getsignaturestatus)
|
||||
* [getSlot](jsonrpc-api.md#getslot)
|
||||
* [getSlotLeader](jsonrpc-api.md#getslotleader)
|
||||
* [getSlotsPerSegment](jsonrpc-api.md#getslotspersegment)
|
||||
* [getStorageTurn](jsonrpc-api.md#getstorageturn)
|
||||
* [getStorageTurnRate](jsonrpc-api.md#getstorageturnrate)
|
||||
* [getNumBlocksSinceSignatureConfirmation](jsonrpc-api.md#getnumblockssincesignatureconfirmation)
|
||||
* [getTransactionCount](jsonrpc-api.md#gettransactioncount)
|
||||
* [getTotalSupply](jsonrpc-api.md#gettotalsupply)
|
||||
* [getVersion](jsonrpc-api.md#getversion)
|
||||
* [getVoteAccounts](jsonrpc-api.md#getvoteaccounts)
|
||||
* [requestAirdrop](jsonrpc-api.md#requestairdrop)
|
||||
* [sendTransaction](jsonrpc-api.md#sendtransaction)
|
||||
* [startSubscriptionChannel](jsonrpc-api.md#startsubscriptionchannel)
|
||||
* [Subscription Websocket](jsonrpc-api.md#subscription-websocket)
|
||||
* [accountSubscribe](jsonrpc-api.md#accountsubscribe)
|
||||
* [accountUnsubscribe](jsonrpc-api.md#accountunsubscribe)
|
||||
* [programSubscribe](jsonrpc-api.md#programsubscribe)
|
||||
* [programUnsubscribe](jsonrpc-api.md#programunsubscribe)
|
||||
* [signatureSubscribe](jsonrpc-api.md#signaturesubscribe)
|
||||
* [signatureUnsubscribe](jsonrpc-api.md#signatureunsubscribe)
|
||||
|
||||
## Request Formatting
|
||||
|
||||
To make a JSON-RPC request, send an HTTP POST request with a `Content-Type: application/json` header. The JSON request data should contain 4 fields:
|
||||
|
||||
* `jsonrpc`, set to `"2.0"`
|
||||
* `id`, a unique client-generated identifying integer
|
||||
* `method`, a string containing the method to be invoked
|
||||
* `params`, a JSON array of ordered parameter values
|
||||
|
||||
Example using curl:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getBalance", "params":["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"]}' 192.168.1.88:8899
|
||||
```
|
||||
|
||||
The response output will be a JSON object with the following fields:
|
||||
|
||||
* `jsonrpc`, matching the request specification
|
||||
* `id`, matching the request identifier
|
||||
* `result`, requested data or success confirmation
|
||||
|
||||
Requests can be sent in batches by sending an array of JSON-RPC request objects as the data for a single POST.
|
||||
|
||||
## Definitions
|
||||
|
||||
* Hash: A SHA-256 hash of a chunk of data.
|
||||
* Pubkey: The public key of a Ed25519 key-pair.
|
||||
* Signature: An Ed25519 signature of a chunk of data.
|
||||
* Transaction: A Solana instruction signed by a client key-pair.
|
||||
|
||||
## JSON RPC API Reference
|
||||
|
||||
### confirmTransaction
|
||||
|
||||
Returns a transaction receipt
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Signature of Transaction to confirm, as base-58 encoded string
|
||||
|
||||
#### Results:
|
||||
|
||||
* `boolean` - Transaction status, true if Transaction is confirmed
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"confirmTransaction", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":true,"id":1}
|
||||
```
|
||||
|
||||
### getAccountInfo
|
||||
|
||||
Returns all information associated with the account of provided Pubkey
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Pubkey of account to query, as base-58 encoded string
|
||||
|
||||
#### Results:
|
||||
|
||||
The result field will be a JSON object with the following sub fields:
|
||||
|
||||
* `lamports`, number of lamports assigned to this account, as a signed 64-bit integer
|
||||
* `owner`, array of 32 bytes representing the program this account has been assigned to
|
||||
* `data`, array of bytes representing any data associated with the account
|
||||
* `executable`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getAccountInfo", "params":["2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":{"executable":false,"owner":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"lamports":1,"data":[3,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,50,48,53,48,45,48,49,45,48,49,84,48,48,58,48,48,58,48,48,90,252,10,7,28,246,140,88,177,98,82,10,227,89,81,18,30,194,101,199,16,11,73,133,20,246,62,114,39,20,113,189,32,50,0,0,0,0,0,0,0,247,15,36,102,167,83,225,42,133,127,82,34,36,224,207,130,109,230,224,188,163,33,213,13,5,117,211,251,65,159,197,51,0,0,0,0,0,0]},"id":1}
|
||||
```
|
||||
|
||||
### getBalance
|
||||
|
||||
Returns the balance of the account of provided Pubkey
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Pubkey of account to query, as base-58 encoded string
|
||||
|
||||
#### Results:
|
||||
|
||||
* `integer` - quantity, as a signed 64-bit integer
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getBalance", "params":["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":0,"id":1}
|
||||
```
|
||||
|
||||
### getClusterNodes
|
||||
|
||||
Returns information about all the nodes participating in the cluster
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
The result field will be an array of JSON objects, each with the following sub fields:
|
||||
|
||||
* `pubkey` - Node public key, as base-58 encoded string
|
||||
* `gossip` - Gossip network address for the node
|
||||
* `tpu` - TPU network address for the node
|
||||
* `rpc` - JSON RPC network address for the node, or `null` if the JSON RPC service is not enabled
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getClusterNodes"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":[{"gossip":"10.239.6.48:8001","pubkey":"9QzsJf7LPLj8GkXbYT3LFDKqsj2hHG7TA3xinJHu8epQ","rpc":"10.239.6.48:8899","tpu":"10.239.6.48:8856"}],"id":1}
|
||||
```
|
||||
|
||||
### getEpochInfo
|
||||
|
||||
Returns information about the current epoch
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
The result field will be an object with the following fields:
|
||||
|
||||
* `epoch`, the current epoch
|
||||
* `slotIndex`, the current slot relative to the start of the current epoch
|
||||
* `slotsInEpoch`, the number of slots in this epoch
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":{"epoch":3,"slotIndex":126,"slotsInEpoch":256},"id":1}
|
||||
```
|
||||
|
||||
### getGenesisBlockhash
|
||||
|
||||
Returns the genesis block hash
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
* `string` - a Hash as base-58 encoded string
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getGenesisBlockhash"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC","id":1}
|
||||
```
|
||||
|
||||
### getLeaderSchedule
|
||||
|
||||
Returns the leader schedule for the current epoch
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
The result field will be an array of leader public keys \(as base-58 encoded strings\) for each slot in the current epoch
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":[...],"id":1}
|
||||
```
|
||||
|
||||
### getProgramAccounts
|
||||
|
||||
Returns all accounts owned by the provided program Pubkey
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Pubkey of program, as base-58 encoded string
|
||||
|
||||
#### Results:
|
||||
|
||||
The result field will be an array of arrays. Each sub array will contain:
|
||||
|
||||
* `string` - the account Pubkey as base-58 encoded string and a JSON object, with the following sub fields:
|
||||
* `lamports`, number of lamports assigned to this account, as a signed 64-bit integer
|
||||
* `owner`, array of 32 bytes representing the program this account has been assigned to
|
||||
* `data`, array of bytes representing any data associated with the account
|
||||
* `executable`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getProgramAccounts", "params":["8nQwAgzN2yyUzrukXsCa3JELBYqDQrqJ3UyHiWazWxHR"]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":[["BqGKYtAKu69ZdWEBtZHh4xgJY1BYa2YBiBReQE3pe383", {"executable":false,"owner":[50,28,250,90,221,24,94,136,147,165,253,136,1,62,196,215,225,34,222,212,99,84,202,223,245,13,149,99,149,231,91,96],"lamports":1,"data":[]], ["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", {"executable":false,"owner":[50,28,250,90,221,24,94,136,147,165,253,136,1,62,196,215,225,34,222,212,99,84,202,223,245,13,149,99,149,231,91,96],"lamports":10,"data":[]]]},"id":1}
|
||||
```
|
||||
|
||||
### getRecentBlockhash
|
||||
|
||||
Returns a recent block hash from the ledger, and a fee schedule that can be used to compute the cost of submitting a transaction using it.
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
An array consisting of
|
||||
|
||||
* `string` - a Hash as base-58 encoded string
|
||||
* `FeeCalculator object` - the fee schedule for this block hash
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":["GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC",{"lamportsPerSignature": 0}],"id":1}
|
||||
```
|
||||
|
||||
### getSignatureStatus
|
||||
|
||||
Returns the status of a given signature. This method is similar to [confirmTransaction](jsonrpc-api.md#confirmtransaction) but provides more resolution for error events.
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Signature of Transaction to confirm, as base-58 encoded string
|
||||
|
||||
#### Results:
|
||||
|
||||
* `null` - Unknown transaction
|
||||
* `object` - Transaction status:
|
||||
* `"Ok": null` - Transaction was successful
|
||||
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"SignatureNotFound","id":1}
|
||||
```
|
||||
|
||||
### getSlot
|
||||
|
||||
Returns the current slot the node is processing
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
* `u64` - Current slot
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getSlot"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"1234","id":1}
|
||||
```
|
||||
|
||||
### getSlotLeader
|
||||
|
||||
Returns the current slot leader
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
* `string` - Node Id as base-58 encoded string
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getSlotLeader"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"ENvAW7JScgYq6o4zKZwewtkzzJgDzuJAFxYasvmEQdpS","id":1}
|
||||
```
|
||||
|
||||
### getSlotsPerSegment
|
||||
|
||||
Returns the current storage segment size in terms of slots
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
* `u64` - Number of slots in a storage segment
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getSlotsPerSegment"}' http://localhost:8899
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"1024","id":1}
|
||||
```
|
||||
|
||||
### getStorageTurn
|
||||
|
||||
Returns the current storage turn's blockhash and slot
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
An array consisting of
|
||||
|
||||
* `string` - a Hash as base-58 encoded string indicating the blockhash of the turn slot
|
||||
* `u64` - the current storage turn slot
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getStorageTurn"}' http://localhost:8899
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":["GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC", "2048"],"id":1}
|
||||
```
|
||||
|
||||
### getStorageTurnRate
|
||||
|
||||
Returns the current storage turn rate in terms of slots per turn
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
* `u64` - Number of slots in storage turn
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getStorageTurnRate"}' http://localhost:8899
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"1024","id":1}
|
||||
```
|
||||
|
||||
### getNumBlocksSinceSignatureConfirmation
|
||||
|
||||
Returns the current number of blocks since signature has been confirmed.
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Signature of Transaction to confirm, as base-58 encoded string
|
||||
|
||||
#### Results:
|
||||
|
||||
* `integer` - count, as unsigned 64-bit integer
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getNumBlocksSinceSignatureConfirmation", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":8,"id":1}
|
||||
```
|
||||
|
||||
### getTransactionCount
|
||||
|
||||
Returns the current Transaction count from the ledger
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
* `integer` - count, as unsigned 64-bit integer
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":268,"id":1}
|
||||
```
|
||||
|
||||
### getTotalSupply
|
||||
|
||||
Returns the current total supply in Lamports
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
* `integer` - Total supply, as unsigned 64-bit integer
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getTotalSupply"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":10126,"id":1}
|
||||
```
|
||||
|
||||
### getVersion
|
||||
|
||||
Returns the current solana versions running on the node
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
The result field will be a JSON object with the following sub fields:
|
||||
|
||||
* `solana-core`, software version of solana-core
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":{"solana-core": "0.17.2"},"id":1}
|
||||
```
|
||||
|
||||
### getVoteAccounts
|
||||
|
||||
Returns the account info and associated stake for all the voting accounts in the current bank.
|
||||
|
||||
#### Parameters:
|
||||
|
||||
None
|
||||
|
||||
#### Results:
|
||||
|
||||
The result field will be a JSON object of `current` and `delinquent` accounts, each containing an array of JSON objects with the following sub fields:
|
||||
|
||||
* `votePubkey` - Vote account public key, as base-58 encoded string
|
||||
* `nodePubkey` - Node public key, as base-58 encoded string
|
||||
* `activatedStake` - the stake, in lamports, delegated to this vote account and active in this epoch
|
||||
* `epochVoteAccount` - bool, whether the vote account is staked for this epoch
|
||||
* `commission`, an 8-bit integer used as a fraction \(commission/MAX\_U8\) for rewards payout
|
||||
* `lastVote` - Most recent slot voted on by this vote account
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":{"current":[{"commission":0,"epochVoteAccount":true,"nodePubkey":"B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD","lastVote":147,"activatedStake":42,"votePubkey":"3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"}],"delinquent":[{"commission":127,"epochVoteAccount":false,"nodePubkey":"6ZPxeQaDo4bkZLRsdNrCzchNQr5LN9QMc9sipXv9Kw8f","lastVote":0,"activatedStake":0,"votePubkey":"CmgCk4aMS7KW1SHX3s9K5tBJ6Yng2LBaC8MFov4wx9sm"}]},"id":1}
|
||||
```
|
||||
|
||||
### requestAirdrop
|
||||
|
||||
Requests an airdrop of lamports to a Pubkey
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Pubkey of account to receive lamports, as base-58 encoded string
|
||||
* `integer` - lamports, as a signed 64-bit integer
|
||||
|
||||
#### Results:
|
||||
|
||||
* `string` - Transaction Signature of airdrop, as base-58 encoded string
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"requestAirdrop", "params":["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri", 50]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW","id":1}
|
||||
```
|
||||
|
||||
### sendTransaction
|
||||
|
||||
Creates new transaction
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `array` - array of octets containing a fully-signed Transaction
|
||||
|
||||
#### Results:
|
||||
|
||||
* `string` - Transaction Signature, as base-58 encoded string
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"sendTransaction", "params":[[61, 98, 55, 49, 15, 187, 41, 215, 176, 49, 234, 229, 228, 77, 129, 221, 239, 88, 145, 227, 81, 158, 223, 123, 14, 229, 235, 247, 191, 115, 199, 71, 121, 17, 32, 67, 63, 209, 239, 160, 161, 2, 94, 105, 48, 159, 235, 235, 93, 98, 172, 97, 63, 197, 160, 164, 192, 20, 92, 111, 57, 145, 251, 6, 40, 240, 124, 194, 149, 155, 16, 138, 31, 113, 119, 101, 212, 128, 103, 78, 191, 80, 182, 234, 216, 21, 121, 243, 35, 100, 122, 68, 47, 57, 13, 39, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 40, 240, 124, 194, 149, 155, 16, 138, 31, 113, 119, 101, 212, 128, 103, 78, 191, 80, 182, 234, 216, 21, 121, 243, 35, 100, 122, 68, 47, 57, 11, 12, 106, 49, 74, 226, 201, 16, 161, 192, 28, 84, 124, 97, 190, 201, 171, 186, 6, 18, 70, 142, 89, 185, 176, 154, 115, 61, 26, 163, 77, 1, 88, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":"2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b","id":1}
|
||||
```
|
||||
|
||||
### Subscription Websocket
|
||||
|
||||
After connect to the RPC PubSub websocket at `ws://<ADDRESS>/`:
|
||||
|
||||
* Submit subscription requests to the websocket using the methods below
|
||||
* Multiple subscriptions may be active at once
|
||||
* All subscriptions take an optional `confirmations` parameter, which defines
|
||||
|
||||
how many confirmed blocks the node should wait before sending a notification.
|
||||
|
||||
The greater the number, the more likely the notification is to represent
|
||||
|
||||
consensus across the cluster, and the less likely it is to be affected by
|
||||
|
||||
forking or rollbacks. If unspecified, the default value is 0; the node will
|
||||
|
||||
send a notification as soon as it witnesses the event. The maximum
|
||||
|
||||
`confirmations` wait length is the cluster's `MAX_LOCKOUT_HISTORY`, which
|
||||
|
||||
represents the economic finality of the chain.
|
||||
|
||||
### accountSubscribe
|
||||
|
||||
Subscribe to an account to receive notifications when the lamports or data for a given account public key changes
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - account Pubkey, as base-58 encoded string
|
||||
* `integer` - optional, number of confirmed blocks to wait before notification.
|
||||
|
||||
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
||||
|
||||
#### Results:
|
||||
|
||||
* `integer` - Subscription id \(needed to unsubscribe\)
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
{"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12"]}
|
||||
|
||||
{"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", 15]}
|
||||
|
||||
// Result
|
||||
{"jsonrpc": "2.0","result": 0,"id": 1}
|
||||
```
|
||||
|
||||
#### Notification Format:
|
||||
|
||||
```bash
|
||||
{"jsonrpc": "2.0","method": "accountNotification", "params": {"result": {"executable":false,"owner":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"lamports":1,"data":[3,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,50,48,53,48,45,48,49,45,48,49,84,48,48,58,48,48,58,48,48,90,252,10,7,28,246,140,88,177,98,82,10,227,89,81,18,30,194,101,199,16,11,73,133,20,246,62,114,39,20,113,189,32,50,0,0,0,0,0,0,0,247,15,36,102,167,83,225,42,133,127,82,34,36,224,207,130,109,230,224,188,163,33,213,13,5,117,211,251,65,159,197,51,0,0,0,0,0,0]},"subscription":0}}
|
||||
```
|
||||
|
||||
### accountUnsubscribe
|
||||
|
||||
Unsubscribe from account change notifications
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `integer` - id of account Subscription to cancel
|
||||
|
||||
#### Results:
|
||||
|
||||
* `bool` - unsubscribe success message
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
{"jsonrpc":"2.0", "id":1, "method":"accountUnsubscribe", "params":[0]}
|
||||
|
||||
// Result
|
||||
{"jsonrpc": "2.0","result": true,"id": 1}
|
||||
```
|
||||
|
||||
### programSubscribe
|
||||
|
||||
Subscribe to a program to receive notifications when the lamports or data for a given account owned by the program changes
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - program\_id Pubkey, as base-58 encoded string
|
||||
* `integer` - optional, number of confirmed blocks to wait before notification.
|
||||
|
||||
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
||||
|
||||
#### Results:
|
||||
|
||||
* `integer` - Subscription id \(needed to unsubscribe\)
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
{"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["9gZbPtbtHrs6hEWgd6MbVY9VPFtS5Z8xKtnYwA2NynHV"]}
|
||||
|
||||
{"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["9gZbPtbtHrs6hEWgd6MbVY9VPFtS5Z8xKtnYwA2NynHV", 15]}
|
||||
|
||||
// Result
|
||||
{"jsonrpc": "2.0","result": 0,"id": 1}
|
||||
```
|
||||
|
||||
#### Notification Format:
|
||||
|
||||
* `string` - account Pubkey, as base-58 encoded string
|
||||
* `object` - account info JSON object \(see [getAccountInfo](jsonrpc-api.md#getaccountinfo) for field details\)
|
||||
|
||||
```bash
|
||||
{"jsonrpc":"2.0","method":"programNotification","params":{{"result":["8Rshv2oMkPu5E4opXTRyuyBeZBqQ4S477VG26wUTFxUM",{"executable":false,"lamports":1,"owner":[129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"data":[1,1,1,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,50,48,49,56,45,49,50,45,50,52,84,50,51,58,53,57,58,48,48,90,235,233,39,152,15,44,117,176,41,89,100,86,45,61,2,44,251,46,212,37,35,118,163,189,247,84,27,235,178,62,55,89,0,0,0,0,50,0,0,0,0,0,0,0,235,233,39,152,15,44,117,176,41,89,100,86,45,61,2,44,251,46,212,37,35,118,163,189,247,84,27,235,178,62,45,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],"subscription":0}}
|
||||
```
|
||||
|
||||
### programUnsubscribe
|
||||
|
||||
Unsubscribe from program-owned account change notifications
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `integer` - id of account Subscription to cancel
|
||||
|
||||
#### Results:
|
||||
|
||||
* `bool` - unsubscribe success message
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
{"jsonrpc":"2.0", "id":1, "method":"programUnsubscribe", "params":[0]}
|
||||
|
||||
// Result
|
||||
{"jsonrpc": "2.0","result": true,"id": 1}
|
||||
```
|
||||
|
||||
### signatureSubscribe
|
||||
|
||||
Subscribe to a transaction signature to receive notification when the transaction is confirmed On `signatureNotification`, the subscription is automatically cancelled
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `string` - Transaction Signature, as base-58 encoded string
|
||||
* `integer` - optional, number of confirmed blocks to wait before notification.
|
||||
|
||||
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
||||
|
||||
#### Results:
|
||||
|
||||
* `integer` - subscription id \(needed to unsubscribe\)
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
{"jsonrpc":"2.0", "id":1, "method":"signatureSubscribe", "params":["2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b"]}
|
||||
|
||||
{"jsonrpc":"2.0", "id":1, "method":"signatureSubscribe", "params":["2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b", 15]}
|
||||
|
||||
// Result
|
||||
{"jsonrpc": "2.0","result": 0,"id": 1}
|
||||
```
|
||||
|
||||
#### Notification Format:
|
||||
|
||||
```bash
|
||||
{"jsonrpc": "2.0","method": "signatureNotification", "params": {"result": "Confirmed","subscription":0}}
|
||||
```
|
||||
|
||||
### signatureUnsubscribe
|
||||
|
||||
Unsubscribe from signature confirmation notification
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `integer` - subscription id to cancel
|
||||
|
||||
#### Results:
|
||||
|
||||
* `bool` - unsubscribe success message
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
{"jsonrpc":"2.0", "id":1, "method":"signatureUnsubscribe", "params":[0]}
|
||||
|
||||
// Result
|
||||
{"jsonrpc": "2.0","result": true,"id": 1}
|
||||
```
|
||||
|
63
book/src/api-reference/transaction-api.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Transaction
|
||||
|
||||
## Components of a `Transaction`
|
||||
|
||||
* **Transaction:**
|
||||
* **message:** Defines the transaction
|
||||
* **header:** Details the account types of and signatures required by
|
||||
|
||||
the transaction
|
||||
|
||||
* **num\_required\_signatures:** The total number of signatures
|
||||
|
||||
required to make the transaction valid.
|
||||
|
||||
* **num\_credit\_only\_signed\_accounts:** The last
|
||||
|
||||
`num_credit_only_signed_accounts` signatures refer to signing
|
||||
|
||||
credit only accounts. Credit only accounts can be used concurrently
|
||||
|
||||
by multiple parallel transactions, but their balance may only be
|
||||
|
||||
increased, and their account data is read-only.
|
||||
|
||||
* **num\_credit\_only\_unsigned\_accounts:** The last
|
||||
|
||||
`num_credit_only_unsigned_accounts` pubkeys in `account_keys` refer
|
||||
|
||||
to non-signing credit only accounts
|
||||
|
||||
* **account\_keys:** List of pubkeys used by the transaction, including
|
||||
|
||||
by the instructions and for signatures. The first
|
||||
|
||||
`num_required_signatures` pubkeys must sign the transaction.
|
||||
|
||||
* **recent\_blockhash:** The ID of a recent ledger entry. Validators will
|
||||
|
||||
reject transactions with a `recent_blockhash` that is too old.
|
||||
|
||||
* **instructions:** A list of [instructions](https://github.com/solana-labs/solana/tree/6b18db969dd1616eff07de35e7b823c75339fea8/book/src/instruction.md) that are
|
||||
|
||||
run sequentially and committed in one atomic transaction if all
|
||||
|
||||
succeed.
|
||||
* **signatures:** A list of signatures applied to the transaction. The
|
||||
|
||||
list is always of length `num_required_signatures`, and the signature
|
||||
|
||||
at index `i` corresponds to the pubkey at index `i` in `account_keys`.
|
||||
|
||||
The list is initialized with empty signatures \(i.e. zeros\), and
|
||||
|
||||
populated as signatures are added.
|
||||
|
||||
## Transaction Signing
|
||||
|
||||
A `Transaction` is signed by using an ed25519 keypair to sign the serialization of the `message`. The resulting signature is placed at the index of `signatures` matching the index of the keypair's pubkey in `account_keys`.
|
||||
|
||||
## Transaction Serialization
|
||||
|
||||
`Transaction`s \(and their `message`s\) are serialized and deserialized using the [bincode](https://crates.io/crates/bincode) crate with a non-standard vector serialization that uses only one byte for the length if it can be encoded in 7 bits, 2 bytes if it fits in 14 bits, or 3 bytes if it requires 15 or 16 bits. The vector serialization is defined by Solana's [short-vec](https://github.com/solana-labs/solana/blob/master/sdk/src/short_vec.rs).
|
||||
|
@@ -1,44 +0,0 @@
|
||||
# Programming Model
|
||||
|
||||
An _app_ interacts with a Solana cluster by sending it _transactions_ with one or more _instructions_. The Solana _runtime_ passes those instructions to user-contributed _programs_. An instruction might, for example, tell a program to transfer _lamports_ from one _account_ to another or create an interactive contract that governs how lamports are transfered. Instructions are executed sequentially and atomically. If any instruction is invalid, any changes made within the transaction are discarded.
|
||||
|
||||
### Accounts and Signatures
|
||||
|
||||
Each transaction explicitly lists all account public keys referenced by the transaction's instructions. A subset of those public keys are each accompanied by a transaction signature. Those signatures signal on-chain programs that the account holder has authorized the transaction. Typically, the program uses the authorization to permit debiting the account or modifying its data.
|
||||
|
||||
The transaction also marks some accounts as _read-only accounts_. The runtime permits read-only accounts to be read concurrently. If a program attempts to modify a read-only account, the transaction is rejected by the runtime.
|
||||
|
||||
### Recent Blockhash
|
||||
|
||||
A Transaction includes a recent blockhash to prevent duplication and to give transactions lifetimes. Any transaction that is completely identical to a previous one is rejected, so adding a newer blockhash allows multiple transactions to repeat the exact same action. Transactions also have lifetimes that are defined by the blockhash, as any transaction whose blockhash is too old will be rejected.
|
||||
|
||||
### Instructions
|
||||
|
||||
Each instruction specifies a single program account \(which must be marked executable\), a subset of the transaction's accounts that should be passed to the program, and a data byte array instruction that is passed to the program. The program interprets the data array and operates on the accounts specified by the instructions. The program can return successfully, or with an error code. An error return causes the entire transaction to fail immediately.
|
||||
|
||||
## Deploying Programs to a Cluster
|
||||
|
||||

|
||||
|
||||
As shown in the diagram above a client creates a program and compiles it to an ELF shared object containing BPF bytecode and sends it to the Solana cluster. The cluster stores the program locally and makes it available to clients via a _program ID_. The program ID is a _public key_ generated by the client and is used to reference the program in subsequent transactions.
|
||||
|
||||
A program may be written in any programming language that can target the Berkley Packet Filter \(BPF\) safe execution environment. The Solana SDK offers the best support for C programs, which is compiled to BPF using the [LLVM compiler infrastructure](https://llvm.org).
|
||||
|
||||
## Storing State between Transactions
|
||||
|
||||
If the program needs to store state between transactions, it does so using _accounts_. Accounts are similar to files in operating systems such as Linux. Like a file, an account may hold arbitrary data and that data persists beyond the lifetime of a program. Also like a file, an account includes metadata that tells the runtime who is allowed to access the data and how. Unlike a file, the account includes metadata for the lifetime of the file. That lifetime is expressed in "tokens", which is a number of fractional native tokens, called _lamports_. Accounts are held in validator memory and pay "rent" to stay there. Each validator periodically scan all accounts and collects rent. Any account that drops to zero lamports is purged.
|
||||
|
||||
If an account is marked "executable", it will only be used by a _loader_ to run programs. For example, a BPF-compiled program is marked executable and loaded by the BPF loader. No program is allowed to modify the contents of an executable account.
|
||||
|
||||
An account also includes "owner" metadata. The owner is a program ID. The runtime grants the program write access to the account if its ID matches the owner. If an account is not owned by a program, the program is permitted to read its data and credit the account.
|
||||
|
||||
In the same way that a Linux user uses a path to look up a file, a Solana client uses public keys to look up accounts. To create an account, the client generates a _keypair_ and registers its public key using the `CreateAccount` instruction. The account created by `CreateAccount` is called a _system account_ and is owned by a built-in program called the System program. The System program allows clients to transfer lamports and assign account ownership.
|
||||
|
||||
The runtime only permits the owner to debit the account or modify its data. The program then defines additional rules for whether the client can modify accounts it owns. In the case of the System program, it allows users to transfer lamports by recognizing transaction signatures. If it sees the client signed the transaction using the keypair's _private key_, it knows the client authorized the token transfer.
|
||||
|
||||
After the runtime executes each of the transaction's instructions, it uses the account metadata to verify that none of the access rules were violated. If a program violates an access rule, the runtime discards all account changes made by all instructions and marks the transaction as failed.
|
||||
|
||||
## Smart Contracts
|
||||
|
||||
Programs don't always require transaction signatures, as the System program does. Instead, the program may manage _smart contracts_. A smart contract is a set of constraints that once satisfied, signal to a program that a token transfer or account update is permitted. For example, one could use the Budget program to create a smart contract that authorizes a token transfer only after some date. Once evidence that the date has past, the contract progresses, and token transfer completes.
|
||||
|
@@ -17,7 +17,7 @@ height of the block it is voting on. The account stores the 32 highest heights.
|
||||
* Only the validator knows how to find its own votes directly.
|
||||
|
||||
Other components, such as the one that calculates confirmation time, needs to
|
||||
be baked into the validator code. The validator code queries the bank for all
|
||||
be baked into the fullnode code. The fullnode code queries the bank for all
|
||||
accounts owned by the vote program.
|
||||
|
||||
* Voting ballots do not contain a PoH hash. The validator is only voting that
|
37
book/src/blockstreamer.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Blockstreamer
|
||||
|
||||
Solana supports a node type called an *blockstreamer*. This fullnode variation
|
||||
is intended for applications that need to observe the data plane without
|
||||
participating in transaction validation or ledger replication.
|
||||
|
||||
A blockstreamer runs without a vote signer, and can optionally stream ledger
|
||||
entries out to a Unix domain socket as they are processed. The JSON-RPC service
|
||||
still functions as on any other node.
|
||||
|
||||
To run a blockstreamer, include the argument `no-signer` and (optional)
|
||||
`blockstream` socket location:
|
||||
|
||||
```bash
|
||||
$ ./multinode-demo/validator-x.sh --no-signer --blockstream <SOCKET>
|
||||
```
|
||||
|
||||
The stream will output a series of JSON objects:
|
||||
- An Entry event JSON object is sent when each ledger entry is processed, with
|
||||
the following fields:
|
||||
|
||||
* `dt`, the system datetime, as RFC3339-formatted string
|
||||
* `t`, the event type, always "entry"
|
||||
* `s`, the slot height, as unsigned 64-bit integer
|
||||
* `h`, the tick height, as unsigned 64-bit integer
|
||||
* `entry`, the entry, as JSON object
|
||||
|
||||
|
||||
- A Block event JSON object is sent when a block is complete, with the
|
||||
following fields:
|
||||
|
||||
* `dt`, the system datetime, as RFC3339-formatted string
|
||||
* `t`, the event type, always "block"
|
||||
* `s`, the slot height, as unsigned 64-bit integer
|
||||
* `h`, the tick height, as unsigned 64-bit integer
|
||||
* `l`, the slot leader id, as base-58 encoded string
|
||||
* `id`, the block id, as base-58 encoded string
|
102
book/src/blocktree.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Blocktree
|
||||
|
||||
After a block reaches finality, all blocks from that one on down
|
||||
to the genesis block form a linear chain with the familiar name
|
||||
blockchain. Until that point, however, the validator must maintain all
|
||||
potentially valid chains, called *forks*. The process by which forks
|
||||
naturally form as a result of leader rotation is described in
|
||||
[fork generation](fork-generation.md). The *blocktree* data structure
|
||||
described here is how a validator copes with those forks until blocks
|
||||
are finalized.
|
||||
|
||||
The blocktree allows a validator to record every shred it observes
|
||||
on the network, in any order, as long as the shred is signed by the expected
|
||||
leader for a given slot.
|
||||
|
||||
Shreds are moved to a fork-able key space the tuple of `leader slot` + `shred
|
||||
index` (within the slot). This permits the skip-list structure of the Solana
|
||||
protocol to be stored in its entirety, without a-priori choosing which fork to
|
||||
follow, which Entries to persist or when to persist them.
|
||||
|
||||
Repair requests for recent shreds are served out of RAM or recent files and out
|
||||
of deeper storage for less recent shreds, as implemented by the store backing
|
||||
Blocktree.
|
||||
|
||||
### Functionalities of Blocktree
|
||||
|
||||
1. Persistence: the Blocktree lives in the front of the nodes verification
|
||||
pipeline, right behind network receive and signature verification. If the
|
||||
shred received is consistent with the leader schedule (i.e. was signed by the
|
||||
leader for the indicated slot), it is immediately stored.
|
||||
2. Repair: repair is the same as window repair above, but able to serve any
|
||||
shred that's been received. Blocktree stores shreds with signatures,
|
||||
preserving the chain of origination.
|
||||
3. Forks: Blocktree supports random access of shreds, so can support a
|
||||
validator's need to rollback and replay from a Bank checkpoint.
|
||||
4. Restart: with proper pruning/culling, the Blocktree can be replayed by
|
||||
ordered enumeration of entries from slot 0. The logic of the replay stage
|
||||
(i.e. dealing with forks) will have to be used for the most recent entries in
|
||||
the Blocktree.
|
||||
|
||||
### Blocktree Design
|
||||
|
||||
1. Entries in the Blocktree are stored as key-value pairs, where the key is the concatenated
|
||||
slot index and shred index for an entry, and the value is the entry data. Note shred indexes are zero-based for each slot (i.e. they're slot-relative).
|
||||
|
||||
2. The Blocktree maintains metadata for each slot, in the `SlotMeta` struct containing:
|
||||
* `slot_index` - The index of this slot
|
||||
* `num_blocks` - The number of blocks in the slot (used for chaining to a previous slot)
|
||||
* `consumed` - The highest shred index `n`, such that for all `m < n`, there exists a shred in this slot with shred index equal to `n` (i.e. the highest consecutive shred index).
|
||||
* `received` - The highest received shred index for the slot
|
||||
* `next_slots` - A list of future slots this slot could chain to. Used when rebuilding
|
||||
the ledger to find possible fork points.
|
||||
* `last_index` - The index of the shred that is flagged as the last shred for this slot. This flag on a shred will be set by the leader for a slot when they are transmitting the last shred for a slot.
|
||||
* `is_rooted` - True iff every block from 0...slot forms a full sequence without any holes. We can derive is_rooted for each slot with the following rules. Let slot(n) be the slot with index `n`, and slot(n).is_full() is true if the slot with index `n` has all the ticks expected for that slot. Let is_rooted(n) be the statement that "the slot(n).is_rooted is true". Then:
|
||||
|
||||
is_rooted(0)
|
||||
is_rooted(n+1) iff (is_rooted(n) and slot(n).is_full()
|
||||
|
||||
3. Chaining - When a shred for a new slot `x` arrives, we check the number of blocks (`num_blocks`) for that new slot (this information is encoded in the shred). We then know that this new slot chains to slot `x - num_blocks`.
|
||||
|
||||
4. Subscriptions - The Blocktree records a set of slots that have been "subscribed" to. This means entries that chain to these slots will be sent on the Blocktree channel for consumption by the ReplayStage. See the `Blocktree APIs` for details.
|
||||
|
||||
5. Update notifications - The Blocktree notifies listeners when slot(n).is_rooted is flipped from false to true for any `n`.
|
||||
|
||||
### Blocktree APIs
|
||||
|
||||
The Blocktree offers a subscription based API that ReplayStage uses to ask for entries it's interested in. The entries will be sent on a channel exposed by the Blocktree. These subscription API's are as follows:
|
||||
1. `fn get_slots_since(slot_indexes: &[u64]) -> Vec<SlotMeta>`: Returns new slots connecting to any element of the list `slot_indexes`.
|
||||
|
||||
2. `fn get_slot_entries(slot_index: u64, entry_start_index: usize, max_entries: Option<u64>) -> Vec<Entry>`: Returns the entry vector for the slot starting with `entry_start_index`, capping the result at `max` if `max_entries == Some(max)`, otherwise, no upper limit on the length of the return vector is imposed.
|
||||
|
||||
Note: Cumulatively, this means that the replay stage will now have to know when a slot is finished, and subscribe to the next slot it's interested in to get the next set of entries. Previously, the burden of chaining slots fell on the Blocktree.
|
||||
|
||||
### Interfacing with Bank
|
||||
|
||||
The bank exposes to replay stage:
|
||||
|
||||
1. `prev_hash`: which PoH chain it's working on as indicated by the hash of the last
|
||||
entry it processed
|
||||
2. `tick_height`: the ticks in the PoH chain currently being verified by this
|
||||
bank
|
||||
3. `votes`: a stack of records that contain:
|
||||
|
||||
1. `prev_hashes`: what anything after this vote must chain to in PoH
|
||||
2. `tick_height`: the tick height at which this vote was cast
|
||||
3. `lockout period`: how long a chain must be observed to be in the ledger to
|
||||
be able to be chained below this vote
|
||||
|
||||
Replay stage uses Blocktree APIs to find the longest chain of entries it can
|
||||
hang off a previous vote. If that chain of entries does not hang off the
|
||||
latest vote, the replay stage rolls back the bank to that vote and replays the
|
||||
chain from there.
|
||||
|
||||
### Pruning Blocktree
|
||||
|
||||
Once Blocktree entries are old enough, representing all the possible forks
|
||||
becomes less useful, perhaps even problematic for replay upon restart. Once a
|
||||
validator's votes have reached max lockout, however, any Blocktree contents
|
||||
that are not on the PoH chain for that vote for can be pruned, expunged.
|
||||
|
||||
Replicator nodes will be responsible for storing really old ledger contents,
|
||||
and validators need only persist their bank periodically.
|
865
book/src/cli.md
Normal file
@@ -0,0 +1,865 @@
|
||||
## solana CLI
|
||||
|
||||
The [solana-cli crate](https://crates.io/crates/solana-cli) provides a command-line interface tool for Solana
|
||||
|
||||
### Examples
|
||||
|
||||
#### Get Pubkey
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana address
|
||||
|
||||
// Return
|
||||
<PUBKEY>
|
||||
```
|
||||
|
||||
#### Airdrop SOL/Lamports
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana airdrop 2
|
||||
|
||||
// Return
|
||||
"2.00000000 SOL"
|
||||
|
||||
// Command
|
||||
$ solana airdrop 123 --lamports
|
||||
|
||||
// Return
|
||||
"123 lamports"
|
||||
```
|
||||
|
||||
#### Get Balance
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana balance
|
||||
|
||||
// Return
|
||||
"3.00050001 SOL"
|
||||
```
|
||||
|
||||
#### Confirm Transaction
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana confirm <TX_SIGNATURE>
|
||||
|
||||
// Return
|
||||
"Confirmed" / "Not found" / "Transaction failed with error <ERR>"
|
||||
```
|
||||
|
||||
#### Deploy program
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana deploy <PATH>
|
||||
|
||||
// Return
|
||||
<PROGRAM_ID>
|
||||
```
|
||||
|
||||
#### Unconditional Immediate Transfer
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
#### Post-Dated Transfer
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--after 2018-12-24T23:59:00 --require-timestamp-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
*`require-timestamp-from` is optional. If not provided, the transaction will expect a timestamp signed by this wallet's private key*
|
||||
|
||||
#### Authorized Transfer
|
||||
|
||||
A third party must send a signature to unlock the lamports.
|
||||
```sh
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
#### Post-Dated and Authorized Transfer
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--after 2018-12-24T23:59 --require-timestamp-from <PUBKEY> \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
#### Multiple Witnesses
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY> \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
#### Cancelable Transfer
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY> \
|
||||
--cancelable
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
#### Cancel Transfer
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana cancel <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
#### Send Signature
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana send-signature <PUBKEY> <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
#### Indicate Elapsed Time
|
||||
|
||||
Use the current system time:
|
||||
```sh
|
||||
// Command
|
||||
$ solana send-timestamp <PUBKEY> <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
Or specify some other arbitrary timestamp:
|
||||
|
||||
```sh
|
||||
// Command
|
||||
$ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```manpage
|
||||
solana 0.12.0
|
||||
|
||||
USAGE:
|
||||
solana [FLAGS] [OPTIONS] [SUBCOMMAND]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
SUBCOMMANDS:
|
||||
address Get your public key
|
||||
airdrop Request lamports
|
||||
authorize-voter Authorize a new vote signing keypair for the given vote account
|
||||
balance Get your balance
|
||||
cancel Cancel a transfer
|
||||
claim-storage-reward Redeem storage reward credits
|
||||
cluster-version Get the version of the cluster entrypoint
|
||||
confirm Confirm transaction by signature
|
||||
create-replicator-storage-account Create a replicator storage account
|
||||
create-storage-mining-pool-account Create mining pool account
|
||||
create-validator-storage-account Create a validator storage account
|
||||
create-vote-account Create a vote account
|
||||
deactivate-stake Deactivate the delegated stake from the stake account
|
||||
delegate-stake Delegate stake to a vote account
|
||||
deploy Deploy a program
|
||||
fees Display current cluster fees
|
||||
get Get wallet config settings
|
||||
get-slot Get current slot
|
||||
get-transaction-count Get current transaction count
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
pay Send a payment
|
||||
ping Submit transactions sequentially
|
||||
redeem-vote-credits Redeem credits in the stake account
|
||||
send-signature Send a signature to authorize a transfer
|
||||
send-timestamp Send a timestamp to unlock a transfer
|
||||
set Set a wallet config setting
|
||||
show-account Show the contents of an account
|
||||
show-stake-account Show the contents of a stake account
|
||||
show-storage-account Show the contents of a storage account
|
||||
show-vote-account Show the contents of a vote account
|
||||
validator-info Publish/get Validator info on Solana
|
||||
withdraw-stake Withdraw the unstaked lamports from the stake account
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-address
|
||||
Get your public key
|
||||
|
||||
USAGE:
|
||||
solana address [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-airdrop
|
||||
Request a batch of lamports
|
||||
|
||||
USAGE:
|
||||
solana airdrop [OPTIONS] <AMOUNT> [unit]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: /Users/tyeraeulberg/.config/solana/wallet/config.yml]
|
||||
--drone-host <HOST> Drone host to use [default: the --url host]
|
||||
--drone-port <PORT> Drone port to use [default: 9900]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<AMOUNT> The airdrop amount to request (default unit SOL)
|
||||
<unit> Specify unit to use for request and balance display [possible values: SOL, lamports]
|
||||
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-authorize-voter
|
||||
Authorize a new vote signing keypair for the given vote account
|
||||
|
||||
USAGE:
|
||||
solana authorize-voter [OPTIONS] <VOTE ACCOUNT PUBKEY> <CURRENT VOTER KEYPAIR FILE> <NEW VOTER PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEY> Vote account in which to set the authorized voter
|
||||
<CURRENT VOTER KEYPAIR FILE> Keypair file for the currently authorized vote signer
|
||||
<NEW VOTER PUBKEY> New vote signer to authorize
|
||||
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-balance
|
||||
Get your balance
|
||||
|
||||
USAGE:
|
||||
solana balance [FLAGS] [OPTIONS] [PUBKEY]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--lamports Display balance in lamports instead of SOL
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The public key of the balance to check
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-cancel
|
||||
Cancel a transfer
|
||||
|
||||
USAGE:
|
||||
solana cancel [OPTIONS] <PROCESS ID>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<PROCESS ID> The process id of the transfer to cancel
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-claim-storage-reward
|
||||
Redeem storage reward credits
|
||||
|
||||
USAGE:
|
||||
solana claim-storage-reward [OPTIONS] <NODE PUBKEY> <STORAGE ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<NODE PUBKEY> The node account to credit the rewards to
|
||||
<STORAGE ACCOUNT PUBKEY> Storage account address to redeem credits for
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-cluster-version
|
||||
Get the version of the cluster entrypoint
|
||||
|
||||
USAGE:
|
||||
solana cluster-version [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-confirm
|
||||
Confirm transaction by signature
|
||||
|
||||
USAGE:
|
||||
solana confirm [OPTIONS] <SIGNATURE>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<SIGNATURE> The transaction signature to confirm
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-create-replicator-storage-account
|
||||
Create a replicator storage account
|
||||
|
||||
USAGE:
|
||||
solana create-replicator-storage-account [OPTIONS] <STORAGE ACCOUNT OWNER PUBKEY> <STORAGE ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STORAGE ACCOUNT OWNER PUBKEY>
|
||||
<STORAGE ACCOUNT PUBKEY>
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-create-storage-mining-pool-account
|
||||
Create mining pool account
|
||||
|
||||
USAGE:
|
||||
solana create-storage-mining-pool-account [OPTIONS] <STORAGE ACCOUNT PUBKEY> <AMOUNT> [unit]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: /Users/tyeraeulberg/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STORAGE ACCOUNT PUBKEY> Storage mining pool account address to fund
|
||||
<AMOUNT> The amount to assign to the storage mining pool account (default unit SOL)
|
||||
<unit> Specify unit to use for request [possible values: SOL, lamports]
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-create-validator-storage-account
|
||||
Create a validator storage account
|
||||
|
||||
USAGE:
|
||||
solana create-validator-storage-account [OPTIONS] <STORAGE ACCOUNT OWNER PUBKEY> <STORAGE ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STORAGE ACCOUNT OWNER PUBKEY>
|
||||
<STORAGE ACCOUNT PUBKEY>
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-create-vote-account
|
||||
Create a vote account
|
||||
|
||||
USAGE:
|
||||
solana create-vote-account [OPTIONS] <VOTE ACCOUNT PUBKEY> <VALIDATOR PUBKEY> <LAMPORTS>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--commission <NUM> The commission taken on reward redemption (0-255), default: 0
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEY> Vote account address to fund
|
||||
<VALIDATOR PUBKEY> Validator that will vote with this account
|
||||
<LAMPORTS> The amount of lamports to send to the vote account
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-deactivate-stake
|
||||
Deactivate the delegated stake from the stake account
|
||||
|
||||
USAGE:
|
||||
solana deactivate-stake [OPTIONS] <STAKE ACCOUNT KEYPAIR FILE> <PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT KEYPAIR FILE> Keypair file for the stake account, for signing the delegate transaction.
|
||||
<PUBKEY> The vote account to which the stake is currently delegated
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-delegate-stake
|
||||
Delegate stake to a vote account
|
||||
|
||||
USAGE:
|
||||
solana delegate-stake [OPTIONS] <STAKE ACCOUNT KEYPAIR FILE> <VOTE ACCOUNT PUBKEY> <AMOUNT> [unit]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: /Users/tyeraeulberg/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT KEYPAIR FILE> Keypair file for the new stake account
|
||||
<VOTE ACCOUNT PUBKEY> The vote account to which the stake will be delegated
|
||||
<AMOUNT> The amount to delegate (default unit SOL)
|
||||
<unit> Specify unit to use for request [possible values: SOL, lamports]
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-deploy
|
||||
Deploy a program
|
||||
|
||||
USAGE:
|
||||
solana deploy [OPTIONS] <PATH TO PROGRAM>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<PATH TO PROGRAM> /path/to/program.o
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-fees
|
||||
Display current cluster fees
|
||||
|
||||
USAGE:
|
||||
solana fees [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-get
|
||||
Get wallet config settings
|
||||
|
||||
USAGE:
|
||||
solana get [OPTIONS] [CONFIG_FIELD]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<CONFIG_FIELD> Return a specific config setting [possible values: url, keypair]
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-get-slot
|
||||
Get current slot
|
||||
|
||||
USAGE:
|
||||
solana get-slot [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-get-transaction-count
|
||||
Get current transaction count
|
||||
|
||||
USAGE:
|
||||
solana get-transaction-count [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-pay
|
||||
Send a payment
|
||||
|
||||
USAGE:
|
||||
solana pay [FLAGS] [OPTIONS] <PUBKEY> <AMOUNT> [--] [unit]
|
||||
|
||||
FLAGS:
|
||||
--cancelable
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
/Users/tyeraeulberg/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
--after <DATETIME> A timestamp after which transaction will execute
|
||||
--require-timestamp-from <PUBKEY> Require timestamp from this third party
|
||||
--require-signature-from <PUBKEY>... Any third party signatures required to unlock the lamports
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The public key of recipient
|
||||
<AMOUNT> The amount to send (default unit SOL)
|
||||
<unit> Specify unit to use for request [possible values: SOL, lamports]
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-ping
|
||||
Submit transactions sequentially
|
||||
|
||||
USAGE:
|
||||
solana ping [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/wallet/config.yml]
|
||||
-c, --count <NUMBER> Stop after submitting count transactions
|
||||
-i, --interval <SECONDS> Wait interval seconds between submitting the next transaction [default: 2]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-t, --timeout <SECONDS> Wait up to timeout seconds for transaction confirmation [default: 10]
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-redeem-vote-credits
|
||||
Redeem credits in the stake account
|
||||
|
||||
USAGE:
|
||||
solana redeem-vote-credits [OPTIONS] <STAKING ACCOUNT PUBKEY> <VOTE ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STAKING ACCOUNT PUBKEY> Staking account address to redeem credits for
|
||||
<VOTE ACCOUNT PUBKEY> The vote account to which the stake was previously delegated.
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-send-signature
|
||||
Send a signature to authorize a transfer
|
||||
|
||||
USAGE:
|
||||
solana send-signature [OPTIONS] <PUBKEY> <PROCESS ID>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The public key of recipient
|
||||
<PROCESS ID> The process id of the transfer to authorize
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-send-timestamp
|
||||
Send a timestamp to unlock a transfer
|
||||
|
||||
USAGE:
|
||||
solana send-timestamp [OPTIONS] <PUBKEY> <PROCESS ID>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
--date <DATETIME> Optional arbitrary timestamp to apply
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The public key of recipient
|
||||
<PROCESS ID> The process id of the transfer to unlock
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-set
|
||||
Set a wallet config setting
|
||||
|
||||
USAGE:
|
||||
solana set [OPTIONS] <--url <URL>|--keypair <PATH>>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-show-account
|
||||
Show the contents of an account
|
||||
|
||||
USAGE:
|
||||
solana show-account [FLAGS] [OPTIONS] <ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--lamports Display balance in lamports instead of SOL
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-o, --output <FILE> Write the account data to this file
|
||||
|
||||
ARGS:
|
||||
<ACCOUNT PUBKEY> Account public key
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-show-stake-account
|
||||
Show the contents of a stake account
|
||||
|
||||
USAGE:
|
||||
solana show-stake-account [OPTIONS] <STAKE ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT PUBKEY> Stake account public key
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-show-storage-account
|
||||
Show the contents of a storage account
|
||||
|
||||
USAGE:
|
||||
solana show-storage-account [OPTIONS] <STORAGE ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STORAGE ACCOUNT PUBKEY> Storage account public key
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-show-vote-account
|
||||
Show the contents of a vote account
|
||||
|
||||
USAGE:
|
||||
solana show-vote-account [OPTIONS] <VOTE ACCOUNT PUBKEY>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEY> Vote account public key
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-validator-info
|
||||
Publish/get Validator info on Solana
|
||||
|
||||
USAGE:
|
||||
solana validator-info [OPTIONS] [SUBCOMMAND]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
SUBCOMMANDS:
|
||||
get Get and parse Solana Validator info
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
publish Publish Validator info on Solana
|
||||
```
|
||||
|
||||
```manpage
|
||||
solana-withdraw-stake
|
||||
Withdraw the unstaked lamports from the stake account
|
||||
|
||||
USAGE:
|
||||
solana withdraw-stake [OPTIONS] <STAKE ACCOUNT KEYPAIR FILE> <DESTINATION PUBKEY> <AMOUNT> [unit]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default: /Users/tyeraeulberg/.config/solana/wallet/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT KEYPAIR FILE> Keypair file for the stake account, for signing the withdraw transaction.
|
||||
<DESTINATION PUBKEY> The account where the lamports should be transfered
|
||||
<AMOUNT> The amount to withdraw from the stake account (default unit SOL)
|
||||
<unit> Specify unit to use for request [possible values: SOL, lamports]
|
||||
```
|
@@ -1,171 +0,0 @@
|
||||
# solana CLI
|
||||
|
||||
The [solana-cli crate](https://crates.io/crates/solana-cli) provides a command-line interface tool for Solana
|
||||
|
||||
## Examples
|
||||
|
||||
### Get Pubkey
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana address
|
||||
|
||||
// Return
|
||||
<PUBKEY>
|
||||
```
|
||||
|
||||
### Airdrop SOL/Lamports
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana airdrop 2
|
||||
|
||||
// Return
|
||||
"2.00000000 SOL"
|
||||
```
|
||||
|
||||
### Get Balance
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana balance
|
||||
|
||||
// Return
|
||||
"3.00050001 SOL"
|
||||
```
|
||||
|
||||
### Confirm Transaction
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana confirm <TX_SIGNATURE>
|
||||
|
||||
// Return
|
||||
"Confirmed" / "Not found" / "Transaction failed with error <ERR>"
|
||||
```
|
||||
|
||||
### Deploy program
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana deploy <PATH>
|
||||
|
||||
// Return
|
||||
<PROGRAM_ID>
|
||||
```
|
||||
|
||||
### Unconditional Immediate Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
### Post-Dated Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--after 2018-12-24T23:59:00 --require-timestamp-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
_`require-timestamp-from` is optional. If not provided, the transaction will expect a timestamp signed by this wallet's private key_
|
||||
|
||||
### Authorized Transfer
|
||||
|
||||
A third party must send a signature to unlock the lamports.
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Post-Dated and Authorized Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--after 2018-12-24T23:59 --require-timestamp-from <PUBKEY> \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Multiple Witnesses
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY> \
|
||||
--require-signature-from <PUBKEY>
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Cancelable Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana pay <PUBKEY> 123 \
|
||||
--require-signature-from <PUBKEY> \
|
||||
--cancelable
|
||||
|
||||
// Return
|
||||
{signature: <TX_SIGNATURE>, processId: <PROCESS_ID>}
|
||||
```
|
||||
|
||||
### Cancel Transfer
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana cancel <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
### Send Signature
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana send-signature <PUBKEY> <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
### Indicate Elapsed Time
|
||||
|
||||
Use the current system time:
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana send-timestamp <PUBKEY> <PROCESS_ID>
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
Or specify some other arbitrary timestamp:
|
||||
|
||||
```bash
|
||||
// Command
|
||||
$ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
|
||||
|
||||
// Return
|
||||
<TX_SIGNATURE>
|
||||
```
|
||||
|
||||
## Usage
|
@@ -1,5 +0,0 @@
|
||||
# Using Solana from the Command-line
|
||||
|
||||
This chapter describes the command-line tools for interacting with Solana. One
|
||||
could use these tools to send payments, stake validators, and check account
|
||||
balances.
|
122
book/src/cluster-test-framework.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Cluster Test Framework
|
||||
|
||||
This document proposes the Cluster Test Framework (CTF). CTF is a test harness
|
||||
that allows tests to execute against a local, in-process cluster or a
|
||||
deployed cluster.
|
||||
|
||||
## Motivation
|
||||
|
||||
The goal of CTF is to provide a framework for writing tests independent of where
|
||||
and how the cluster is deployed. Regressions can be captured in these tests and
|
||||
the tests can be run against deployed clusters to verify the deployment. The
|
||||
focus of these tests should be on cluster stability, consensus, fault tolerance,
|
||||
API stability.
|
||||
|
||||
Tests should verify a single bug or scenario, and should be written with the
|
||||
least amount of internal plumbing exposed to the test.
|
||||
|
||||
## Design Overview
|
||||
|
||||
Tests are provided an entry point, which is a `contact_info::ContactInfo`
|
||||
structure, and a keypair that has already been funded.
|
||||
|
||||
Each node in the cluster is configured with a `fullnode::ValidatorConfig` at boot
|
||||
time. At boot time this configuration specifies any extra cluster configuration
|
||||
required for the test. The cluster should boot with the configuration when it
|
||||
is run in-process or in a data center.
|
||||
|
||||
Once booted, the test will discover the cluster through a gossip entry point and
|
||||
configure any runtime behaviors via fullnode RPC.
|
||||
|
||||
## Test Interface
|
||||
|
||||
Each CTF test starts with an opaque entry point and a funded keypair. The test
|
||||
should not depend on how the cluster is deployed, and should be able to exercise
|
||||
all the cluster functionality through the publicly available interfaces.
|
||||
|
||||
```rust,ignore
|
||||
use crate::contact_info::ContactInfo;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
pub fn test_this_behavior(
|
||||
entry_point_info: &ContactInfo,
|
||||
funding_keypair: &Keypair,
|
||||
num_nodes: usize,
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Cluster Discovery
|
||||
|
||||
At test start, the cluster has already been established and is fully connected.
|
||||
The test can discover most of the available nodes over a few second.
|
||||
|
||||
```rust,ignore
|
||||
use crate::gossip_service::discover_nodes;
|
||||
|
||||
// Discover the cluster over a few seconds.
|
||||
let cluster_nodes = discover_nodes(&entry_point_info, num_nodes);
|
||||
```
|
||||
|
||||
## Cluster Configuration
|
||||
|
||||
To enable specific scenarios, the cluster needs to be booted with special
|
||||
configurations. These configurations can be captured in
|
||||
`fullnode::ValidatorConfig`.
|
||||
|
||||
For example:
|
||||
|
||||
```rust,ignore
|
||||
let mut validator_config = ValidatorConfig::default();
|
||||
validator_config.rpc_config.enable_fullnode_exit = true;
|
||||
let local = LocalCluster::new_with_config(
|
||||
num_nodes,
|
||||
10_000,
|
||||
100,
|
||||
&validator_config
|
||||
);
|
||||
```
|
||||
|
||||
## How to design a new test
|
||||
|
||||
For example, there is a bug that shows that the cluster fails when it is flooded
|
||||
with invalid advertised gossip nodes. Our gossip library and protocol may
|
||||
change, but the cluster still needs to stay resilient to floods of invalid
|
||||
advertised gossip nodes.
|
||||
|
||||
Configure the RPC service:
|
||||
|
||||
```rust,ignore
|
||||
let mut validator_config = ValidatorConfig::default();
|
||||
validator_config.rpc_config.enable_rpc_gossip_push = true;
|
||||
validator_config.rpc_config.enable_rpc_gossip_refresh_active_set = true;
|
||||
```
|
||||
|
||||
Wire the RPCs and write a new test:
|
||||
|
||||
```rust,ignore
|
||||
pub fn test_large_invalid_gossip_nodes(
|
||||
entry_point_info: &ContactInfo,
|
||||
funding_keypair: &Keypair,
|
||||
num_nodes: usize,
|
||||
) {
|
||||
let cluster = discover_nodes(&entry_point_info, num_nodes);
|
||||
|
||||
// Poison the cluster.
|
||||
let client = create_client(entry_point_info.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||
for _ in 0..(num_nodes * 100) {
|
||||
client.gossip_push(
|
||||
cluster_info::invalid_contact_info()
|
||||
);
|
||||
}
|
||||
sleep(Durration::from_millis(1000));
|
||||
|
||||
// Force refresh of the active set.
|
||||
for node in &cluster {
|
||||
let client = create_client(node.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||
client.gossip_refresh_active_set();
|
||||
}
|
||||
|
||||
// Verify that spends still work.
|
||||
verify_spends(&cluster);
|
||||
}
|
||||
```
|
100
book/src/cluster.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# A Solana Cluster
|
||||
|
||||
A Solana cluster is a set of fullnodes working together to serve client
|
||||
transactions and maintain the integrity of the ledger. Many clusters may
|
||||
coexist. When two clusters share a common genesis block, they attempt to
|
||||
converge. Otherwise, they simply ignore the existence of the other.
|
||||
Transactions sent to the wrong one are quietly rejected. In this chapter, we'll
|
||||
discuss how a cluster is created, how nodes join the cluster, how they share
|
||||
the ledger, how they ensure the ledger is replicated, and how they cope with
|
||||
buggy and malicious nodes.
|
||||
|
||||
## Creating a Cluster
|
||||
|
||||
Before starting any fullnodes, one first needs to create a *genesis block*.
|
||||
The block contains entries referencing two public keys, a *mint* and a
|
||||
*bootstrap leader*. The fullnode holding the bootstrap leader's private key is
|
||||
responsible for appending the first entries to the ledger. It initializes its
|
||||
internal state with the mint's account. That account will hold the number of
|
||||
native tokens defined by the genesis block. The second fullnode then contacts
|
||||
the bootstrap leader to register as a *validator* or *replicator*. Additional
|
||||
fullnodes then register with any registered member of the cluster.
|
||||
|
||||
A validator receives all entries from the leader and submits votes confirming
|
||||
those entries are valid. After voting, the validator is expected to store those
|
||||
entries until replicator nodes submit proofs that they have stored copies of
|
||||
it. Once the validator observes a sufficient number of copies exist, it deletes
|
||||
its copy.
|
||||
|
||||
## Joining a Cluster
|
||||
|
||||
Validators and replicators enter the cluster via registration messages sent to
|
||||
its *control plane*. The control plane is implemented using a *gossip*
|
||||
protocol, meaning that a node may register with any existing node, and expect
|
||||
its registration to propagate to all nodes in the cluster. The time it takes
|
||||
for all nodes to synchronize is proportional to the square of the number of
|
||||
nodes participating in the cluster. Algorithmically, that's considered very
|
||||
slow, but in exchange for that time, a node is assured that it eventually has
|
||||
all the same information as every other node, and that that information cannot
|
||||
be censored by any one node.
|
||||
|
||||
## Sending Transactions to a Cluster
|
||||
|
||||
Clients send transactions to any fullnode's Transaction Processing Unit (TPU)
|
||||
port. If the node is in the validator role, it forwards the transaction to the
|
||||
designated leader. If in the leader role, the node bundles incoming
|
||||
transactions, timestamps them creating an *entry*, and pushes them onto the
|
||||
cluster's *data plane*. Once on the data plane, the transactions are validated
|
||||
by validator nodes and replicated by replicator nodes, effectively appending
|
||||
them to the ledger.
|
||||
|
||||
## Confirming Transactions
|
||||
|
||||
A Solana cluster is capable of subsecond *confirmation* for up to 150 nodes
|
||||
with plans to scale up to hundreds of thousands of nodes. Once fully
|
||||
implemented, confirmation times are expected to increase only with the
|
||||
logarithm of the number of validators, where the logarithm's base is very high.
|
||||
If the base is one thousand, for example, it means that for the first thousand
|
||||
nodes, confirmation will be the duration of three network hops plus the time it
|
||||
takes the slowest validator of a supermajority to vote. For the next million
|
||||
nodes, confirmation increases by only one network hop.
|
||||
|
||||
Solana defines confirmation as the duration of time from when the leader
|
||||
timestamps a new entry to the moment when it recognizes a supermajority of
|
||||
ledger votes.
|
||||
|
||||
A gossip network is much too slow to achieve subsecond confirmation once the
|
||||
network grows beyond a certain size. The time it takes to send messages to all
|
||||
nodes is proportional to the square of the number of nodes. If a blockchain
|
||||
wants to achieve low confirmation and attempts to do it using a gossip network,
|
||||
it will be forced to centralize to just a handful of nodes.
|
||||
|
||||
Scalable confirmation can be achieved using the follow combination of
|
||||
techniques:
|
||||
|
||||
1. Timestamp transactions with a VDF sample and sign the timestamp.
|
||||
2. Split the transactions into batches, send each to separate nodes and have
|
||||
each node share its batch with its peers.
|
||||
3. Repeat the previous step recursively until all nodes have all batches.
|
||||
|
||||
Solana rotates leaders at fixed intervals, called *slots*. Each leader may only
|
||||
produce entries during its allotted slot. The leader therefore timestamps
|
||||
transactions so that validators may lookup the public key of the designated
|
||||
leader. The leader then signs the timestamp so that a validator may verify the
|
||||
signature, proving the signer is owner of the designated leader's public key.
|
||||
|
||||
Next, transactions are broken into batches so that a node can send transactions
|
||||
to multiple parties without making multiple copies. If, for example, the leader
|
||||
needed to send 60 transactions to 6 nodes, it would break that collection of 60
|
||||
into batches of 10 transactions and send one to each node. This allows the
|
||||
leader to put 60 transactions on the wire, not 60 transactions for each node.
|
||||
Each node then shares its batch with its peers. Once the node has collected all
|
||||
6 batches, it reconstructs the original set of 60 transactions.
|
||||
|
||||
A batch of transactions can only be split so many times before it is so small
|
||||
that header information becomes the primary consumer of network bandwidth. At
|
||||
the time of this writing, the approach is scaling well up to about 150
|
||||
validators. To scale up to hundreds of thousands of validators, each node can
|
||||
apply the same technique as the leader node to another set of nodes of equal
|
||||
size. We call the technique *data plane fanout*; learn more in the [data plan
|
||||
fanout](data-plane-fanout.md) section.
|
@@ -1,20 +1,20 @@
|
||||
# A Solana Cluster
|
||||
|
||||
A Solana cluster is a set of validators working together to serve client transactions and maintain the integrity of the ledger. Many clusters may coexist. When two clusters share a common genesis block, they attempt to converge. Otherwise, they simply ignore the existence of the other. Transactions sent to the wrong one are quietly rejected. In this chapter, we'll discuss how a cluster is created, how nodes join the cluster, how they share the ledger, how they ensure the ledger is replicated, and how they cope with buggy and malicious nodes.
|
||||
A Solana cluster is a set of fullnodes working together to serve client transactions and maintain the integrity of the ledger. Many clusters may coexist. When two clusters share a common genesis block, they attempt to converge. Otherwise, they simply ignore the existence of the other. Transactions sent to the wrong one are quietly rejected. In this chapter, we'll discuss how a cluster is created, how nodes join the cluster, how they share the ledger, how they ensure the ledger is replicated, and how they cope with buggy and malicious nodes.
|
||||
|
||||
## Creating a Cluster
|
||||
|
||||
Before starting any validators, one first needs to create a _genesis config_. The config references two public keys, a _mint_ and a _bootstrap validator_. The validator holding the bootstrap validator's private key is responsible for appending the first entries to the ledger. It initializes its internal state with the mint's account. That account will hold the number of native tokens defined by the genesis config. The second validator then contacts the bootstrap validator to register as a _validator_ or _archiver_. Additional validators then register with any registered member of the cluster.
|
||||
Before starting any fullnodes, one first needs to create a _genesis block_. The block contains entries referencing two public keys, a _mint_ and a _bootstrap leader_. The fullnode holding the bootstrap leader's secret key is responsible for appending the first entries to the ledger. It initializes its internal state with the mint's account. That account will hold the number of native tokens defined by the genesis block. The second fullnode then contacts the bootstrap leader to register as a _validator_ or _replicator_. Additional fullnodes then register with any registered member of the cluster.
|
||||
|
||||
A validator receives all entries from the leader and submits votes confirming those entries are valid. After voting, the validator is expected to store those entries until archiver nodes submit proofs that they have stored copies of it. Once the validator observes a sufficient number of copies exist, it deletes its copy.
|
||||
A validator receives all entries from the leader and submits votes confirming those entries are valid. After voting, the validator is expected to store those entries until replicator nodes submit proofs that they have stored copies of it. Once the validator observes a sufficient number of copies exist, it deletes its copy.
|
||||
|
||||
## Joining a Cluster
|
||||
|
||||
Validators and archivers enter the cluster via registration messages sent to its _control plane_. The control plane is implemented using a _gossip_ protocol, meaning that a node may register with any existing node, and expect its registration to propagate to all nodes in the cluster. The time it takes for all nodes to synchronize is proportional to the square of the number of nodes participating in the cluster. Algorithmically, that's considered very slow, but in exchange for that time, a node is assured that it eventually has all the same information as every other node, and that that information cannot be censored by any one node.
|
||||
Validators and replicators enter the cluster via registration messages sent to its _control plane_. The control plane is implemented using a _gossip_ protocol, meaning that a node may register with any existing node, and expect its registration to propagate to all nodes in the cluster. The time it takes for all nodes to synchronize is proportional to the square of the number of nodes participating in the cluster. Algorithmically, that's considered very slow, but in exchange for that time, a node is assured that it eventually has all the same information as every other node, and that that information cannot be censored by any one node.
|
||||
|
||||
## Sending Transactions to a Cluster
|
||||
|
||||
Clients send transactions to any validator's Transaction Processing Unit \(TPU\) port. If the node is in the validator role, it forwards the transaction to the designated leader. If in the leader role, the node bundles incoming transactions, timestamps them creating an _entry_, and pushes them onto the cluster's _data plane_. Once on the data plane, the transactions are validated by validator nodes and replicated by archiver nodes, effectively appending them to the ledger.
|
||||
Clients send transactions to any fullnode's Transaction Processing Unit \(TPU\) port. If the node is in the validator role, it forwards the transaction to the designated leader. If in the leader role, the node bundles incoming transactions, timestamps them creating an _entry_, and pushes them onto the cluster's _data plane_. Once on the data plane, the transactions are validated by validator nodes and replicated by replicator nodes, effectively appending them to the ledger.
|
||||
|
||||
## Confirming Transactions
|
||||
|
||||
@@ -37,4 +37,5 @@ Solana rotates leaders at fixed intervals, called _slots_. Each leader may only
|
||||
|
||||
Next, transactions are broken into batches so that a node can send transactions to multiple parties without making multiple copies. If, for example, the leader needed to send 60 transactions to 6 nodes, it would break that collection of 60 into batches of 10 transactions and send one to each node. This allows the leader to put 60 transactions on the wire, not 60 transactions for each node. Each node then shares its batch with its peers. Once the node has collected all 6 batches, it reconstructs the original set of 60 transactions.
|
||||
|
||||
A batch of transactions can only be split so many times before it is so small that header information becomes the primary consumer of network bandwidth. At the time of this writing, the approach is scaling well up to about 150 validators. To scale up to hundreds of thousands of validators, each node can apply the same technique as the leader node to another set of nodes of equal size. We call the technique [_Turbine Block Propogation_](turbine-block-propagation.md).
|
||||
A batch of transactions can only be split so many times before it is so small that header information becomes the primary consumer of network bandwidth. At the time of this writing, the approach is scaling well up to about 150 validators. To scale up to hundreds of thousands of validators, each node can apply the same technique as the leader node to another set of nodes of equal size. We call the technique _data plane fanout_; learn more in the [data plan fanout](https://github.com/solana-labs/solana/tree/6b18db969dd1616eff07de35e7b823c75339fea8/book/src/data-plane-fanout.md) section.
|
||||
|
||||
|
@@ -12,14 +12,14 @@ Nodes take turns being leader and generating the PoH that encodes state changes.
|
||||
2. Leader filters valid transactions.
|
||||
3. Leader executes valid transactions updating its state.
|
||||
4. Leader packages transactions into entries based off its current PoH slot.
|
||||
5. Leader transmits the entries to validator nodes \(in signed shreds\) 1. The PoH stream includes ticks; empty entries that indicate liveness of
|
||||
5. Leader transmits the entries to validator nodes \(in signed blobs\)
|
||||
1. The PoH stream includes ticks; empty entries that indicate liveness of
|
||||
|
||||
the leader and the passage of time on the cluster.
|
||||
the leader and the passage of time on the cluster.
|
||||
|
||||
1. A leader's stream begins with the tick entries necessary complete the PoH
|
||||
2. A leader's stream begins with the tick entries necessary complete the PoH
|
||||
|
||||
back to the leaders most recently observed prior leader slot.
|
||||
|
||||
6. Validators retransmit entries to peers in their set and to further
|
||||
|
||||
downstream nodes.
|
||||
@@ -58,7 +58,7 @@ Validators vote based on a greedy choice to maximize their reward described in [
|
||||
|
||||
The diagram below represents a validator's view of the PoH stream with possible forks over time. L1, L2, etc. are leader slots, and `E`s represent entries from that leader during that leader's slot. The `x`s represent ticks only, and time flows downwards in the diagram.
|
||||
|
||||

|
||||

|
||||
|
||||
Note that an `E` appearing on 2 forks at the same slot is a slashable condition, so a validator observing `E3` and `E3'` can slash L3 and safely choose `x` for that slot. Once a validator commits to a forks, other forks can be discarded below that tick count. For any slot, validators need only consider a single "has entries" chain or a "ticks only" chain to be proposed by a leader. But multiple virtual entries may overlap as they link back to the a previous slot.
|
||||
|
||||
@@ -78,3 +78,4 @@ This arrangement of the network data streams permits nodes to save exactly this
|
||||
### Leader's View
|
||||
|
||||
When a new leader begins a slot, it must first transmit any PoH \(ticks\) required to link the new slot with the most recently observed and voted slot. The fork the leader proposes would link the current slot to a previous fork that the leader has voted on with virtual ticks.
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Leader Rotation
|
||||
|
||||
At any given moment, a cluster expects only one validator to produce ledger entries. By having only one leader at a time, all validators are able to replay identical copies of the ledger. The drawback of only one leader at a time, however, is that a malicious leader is capable of censoring votes and transactions. Since censoring cannot be distinguished from the network dropping packets, the cluster cannot simply elect a single node to hold the leader role indefinitely. Instead, the cluster minimizes the influence of a malicious leader by rotating which node takes the lead.
|
||||
At any given moment, a cluster expects only one fullnode to produce ledger entries. By having only one leader at a time, all validators are able to replay identical copies of the ledger. The drawback of only one leader at a time, however, is that a malicious leader is capable of censoring votes and transactions. Since censoring cannot be distinguished from the network dropping packets, the cluster cannot simply elect a single node to hold the leader role indefinitely. Instead, the cluster minimizes the influence of a malicious leader by rotating which node takes the lead.
|
||||
|
||||
Each validator selects the expected leader using the same algorithm, described below. When the validator receives a new signed ledger entry, it can be certain that entry was produced by the expected leader. The order of slots which each leader is assigned a slot is called a _leader schedule_.
|
||||
|
||||
@@ -40,7 +40,7 @@ After observing the cluster for a sufficient amount of time, the leader schedule
|
||||
|
||||
## Leader Schedule Generation at Genesis
|
||||
|
||||
The genesis config declares the first leader for the first epoch. This leader ends up scheduled for the first two epochs because the leader schedule is also generated at slot 0 for the next epoch. The length of the first two epochs can be specified in the genesis config as well. The minimum length of the first epochs must be greater than or equal to the maximum rollback depth as defined in [Tower BFT](../implemented-proposals/tower-bft.md).
|
||||
The genesis block declares the first leader for the first epoch. This leader ends up scheduled for the first two epochs because the leader schedule is also generated at slot 0 for the next epoch. The length of the first two epochs can be specified in the genesis block as well. The minimum length of the first epochs must be greater than or equal to the maximum rollback depth as defined in [Tower BFT](../implemented-proposals/tower-bft.md).
|
||||
|
||||
## Leader Schedule Generation Algorithm
|
||||
|
||||
@@ -73,7 +73,7 @@ The seed that is selected is predictable but unbiasable. There is no grinding at
|
||||
|
||||
A leader can bias the active set by censoring validator votes. Two possible ways exist for leaders to censor the active set:
|
||||
|
||||
* Ignore votes from validators
|
||||
* Ignore votes from validators
|
||||
* Refuse to vote for blocks with votes from validators
|
||||
|
||||
To reduce the likelihood of censorship, the active set is calculated at the leader schedule offset boundary over an _active set sampling duration_. The active set sampling duration is long enough such that votes will have been collected by multiple leaders.
|
||||
@@ -95,3 +95,4 @@ The lifetime of a leader schedule is called an _epoch_. The epoch is split into
|
||||
A leader transmits entries during its slot. After `T` ticks, all the validators switch to the next scheduled leader. Validators must ignore entries sent outside a leader's assigned slot.
|
||||
|
||||
All `T` ticks must be observed by the next leader for it to build its own entries on. If entries are not observed \(leader is down\) or entries are invalid \(leader is buggy or malicious\), the next leader must produce ticks to fill the previous leader's slot. Note that the next leader should do repair requests in parallel, and postpone sending ticks until it is confident other validators also failed to observe the previous leader's entries. If a leader incorrectly builds on its own ticks, the leader following it must replace all its ticks.
|
||||
|
||||
|
@@ -10,9 +10,9 @@ Our improvement on this approach is to randomly sample the encrypted segments fa
|
||||
|
||||
## Network
|
||||
|
||||
Validators for PoRep are the same validators that are verifying transactions. If an archiver can prove that a validator verified a fake PoRep, then the validator will not receive a reward for that storage epoch.
|
||||
Validators for PoRep are the same validators that are verifying transactions. If a replicator can prove that a validator verified a fake PoRep, then the validator will not receive a reward for that storage epoch.
|
||||
|
||||
Archivers are specialized _light clients_. They download a part of the ledger \(a.k.a Segment\) and store it, and provide PoReps of storing the ledger. For each verified PoRep archivers earn a reward of sol from the mining pool.
|
||||
Replicators are specialized _light clients_. They download a part of the ledger \(a.k.a Segment\) and store it, and provide PoReps of storing the ledger. For each verified PoRep replicators earn a reward of sol from the mining pool.
|
||||
|
||||
## Constraints
|
||||
|
||||
@@ -40,9 +40,9 @@ We have the following constraints:
|
||||
|
||||
1. SLOTS\_PER\_SEGMENT: Number of slots in a segment of ledger data. The
|
||||
|
||||
unit of storage for an archiver.
|
||||
unit of storage for a replicator.
|
||||
|
||||
2. NUM\_KEY\_ROTATION\_SEGMENTS: Number of segments after which archivers
|
||||
2. NUM\_KEY\_ROTATION\_SEGMENTS: Number of segments after which replicators
|
||||
|
||||
regenerate their encryption keys and select a new dataset to store.
|
||||
|
||||
@@ -68,7 +68,7 @@ We have the following constraints:
|
||||
|
||||
### Validator behavior
|
||||
|
||||
1. Validators join the network and begin looking for archiver accounts at each
|
||||
1. Validators join the network and begin looking for replicator accounts at each
|
||||
|
||||
storage epoch/turn boundary.
|
||||
|
||||
@@ -78,11 +78,11 @@ We have the following constraints:
|
||||
|
||||
This signed value is also submitted to the validator's storage account and will be used by
|
||||
|
||||
archivers at a later stage to cross-verify.
|
||||
replicators at a later stage to cross-verify.
|
||||
|
||||
3. Every `NUM_SLOTS_PER_TURN` slots the validator advertises the PoH value. This is value
|
||||
|
||||
is also served to Archivers via RPC interfaces.
|
||||
is also served to Replicators via RPC interfaces.
|
||||
|
||||
4. For a given turn N, all validations get locked out until turn N+3 \(a gap of 2 turn/epoch\).
|
||||
|
||||
@@ -90,53 +90,53 @@ We have the following constraints:
|
||||
|
||||
5. Any incorrect validations will be marked during the turn in between.
|
||||
|
||||
### Archiver behavior
|
||||
### Replicator behavior
|
||||
|
||||
1. Since an archiver is somewhat of a light client and not downloading all the
|
||||
1. Since a replicator is somewhat of a light client and not downloading all the
|
||||
|
||||
ledger data, they have to rely on other validators and archivers for information.
|
||||
ledger data, they have to rely on other validators and replicators for information.
|
||||
|
||||
Any given validator may or may not be malicious and give incorrect information, although
|
||||
|
||||
there are not any obvious attack vectors that this could accomplish besides having the
|
||||
|
||||
archiver do extra wasted work. For many of the operations there are a number of options
|
||||
replicator do extra wasted work. For many of the operations there are a number of options
|
||||
|
||||
depending on how paranoid an archiver is:
|
||||
depending on how paranoid a replicator is:
|
||||
|
||||
* \(a\) archiver can ask a validator
|
||||
* \(b\) archiver can ask multiple validators
|
||||
* \(c\) archiver can ask other archivers
|
||||
* \(d\) archiver can subscribe to the full transaction stream and generate
|
||||
* \(a\) replicator can ask a validator
|
||||
* \(b\) replicator can ask multiple validators
|
||||
* \(c\) replicator can ask other replicators
|
||||
* \(d\) replicator can subscribe to the full transaction stream and generate
|
||||
|
||||
the information itself \(assuming the slot is recent enough\)
|
||||
|
||||
* \(e\) archiver can subscribe to an abbreviated transaction stream to
|
||||
* \(e\) replicator can subscribe to an abbreviated transaction stream to
|
||||
|
||||
generate the information itself \(assuming the slot is recent enough\)
|
||||
|
||||
2. An archiver obtains the PoH hash corresponding to the last turn with its slot.
|
||||
3. The archiver signs the PoH hash with its keypair. That signature is the
|
||||
2. A replicator obtains the PoH hash corresponding to the last turn with its slot.
|
||||
3. The replicator signs the PoH hash with its keypair. That signature is the
|
||||
|
||||
seed used to pick the segment to replicate and also the encryption key. The
|
||||
|
||||
archiver mods the signature with the slot to get which segment to
|
||||
replicator mods the signature with the slot to get which segment to
|
||||
|
||||
replicate.
|
||||
|
||||
4. The archiver retrives the ledger by asking peer validators and
|
||||
4. The replicator retrives the ledger by asking peer validators and
|
||||
|
||||
archivers. See 6.5.
|
||||
replicators. See 6.5.
|
||||
|
||||
5. The archiver then encrypts that segment with the key with chacha algorithm
|
||||
5. The replicator then encrypts that segment with the key with chacha algorithm
|
||||
|
||||
in CBC mode with `NUM_CHACHA_ROUNDS` of encryption.
|
||||
|
||||
6. The archiver initializes a chacha rng with the a signed recent PoH value as
|
||||
6. The replicator initializes a chacha rng with the a signed recent PoH value as
|
||||
|
||||
the seed.
|
||||
|
||||
7. The archiver generates `NUM_STORAGE_SAMPLES` samples in the range of the
|
||||
7. The replicator generates `NUM_STORAGE_SAMPLES` samples in the range of the
|
||||
|
||||
entry size and samples the encrypted segment with sha256 for 32-bytes at each
|
||||
|
||||
@@ -144,23 +144,23 @@ We have the following constraints:
|
||||
|
||||
segment.
|
||||
|
||||
8. The archiver sends a PoRep proof transaction which contains its sha state
|
||||
8. The replicator sends a PoRep proof transaction which contains its sha state
|
||||
|
||||
at the end of the sampling operation, its seed and the samples it used to the
|
||||
|
||||
current leader and it is put onto the ledger.
|
||||
|
||||
9. During a given turn the archiver should submit many proofs for the same segment
|
||||
9. During a given turn the replicator should submit many proofs for the same segment
|
||||
|
||||
and based on the `RATIO_OF_FAKE_PROOFS` some of those proofs must be fake.
|
||||
|
||||
10. As the PoRep game enters the next turn, the archiver must submit a
|
||||
10. As the PoRep game enters the next turn, the replicator must submit a
|
||||
|
||||
transaction with the mask of which proofs were fake during the last turn. This
|
||||
|
||||
transaction will define the rewards for both archivers and validators.
|
||||
transaction will define the rewards for both replicators and validators.
|
||||
|
||||
11. Finally for a turn N, as the PoRep game enters turn N + 3, archiver's proofs for
|
||||
11. Finally for a turn N, as the PoRep game enters turn N + 3, replicator's proofs for
|
||||
|
||||
turn N will be counted towards their rewards.
|
||||
|
||||
@@ -171,19 +171,19 @@ The Proof of Replication game has 4 primary stages. For each "turn" multiple PoR
|
||||
The 4 stages of the PoRep Game are as follows:
|
||||
|
||||
1. Proof submission stage
|
||||
* Archivers: submit as many proofs as possible during this stage
|
||||
* Replicators: submit as many proofs as possible during this stage
|
||||
* Validators: No-op
|
||||
2. Proof verification stage
|
||||
* Archivers: No-op
|
||||
* Validators: Select archivers and verify their proofs from the previous turn
|
||||
* Replicators: No-op
|
||||
* Validators: Select replicators and verify their proofs from the previous turn
|
||||
3. Proof challenge stage
|
||||
* Archivers: Submit the proof mask with justifications \(for fake proofs submitted 2 turns ago\)
|
||||
* Replicators: Submit the proof mask with justifications \(for fake proofs submitted 2 turns ago\)
|
||||
* Validators: No-op
|
||||
4. Reward collection stage
|
||||
* Archivers: Collect rewards for 3 turns ago
|
||||
* Replicators: Collect rewards for 3 turns ago
|
||||
* Validators: Collect rewards for 3 turns ago
|
||||
|
||||
For each turn of the PoRep game, both Validators and Archivers evaluate each stage. The stages are run as separate transactions on the storage program.
|
||||
For each turn of the PoRep game, both Validators and Replicators evaluate each stage. The stages are run as separate transactions on the storage program.
|
||||
|
||||
### Finding who has a given block of ledger
|
||||
|
||||
@@ -191,15 +191,15 @@ For each turn of the PoRep game, both Validators and Archivers evaluate each sta
|
||||
|
||||
at turn boundaries for any proofs.
|
||||
|
||||
2. Validators maintain a map of ledger segments and corresponding archiver public keys.
|
||||
2. Validators maintain a map of ledger segments and corresponding replicator public keys.
|
||||
|
||||
The map is updated when a Validator processes an archiver's proofs for a segment.
|
||||
The map is updated when a Validator processes a replicator's proofs for a segment.
|
||||
|
||||
The validator provides an RPC interface to access the this map. Using this API, clients
|
||||
|
||||
can map a segment to an archiver's network address \(correlating it via cluster\_info table\).
|
||||
can map a segment to a replicator's network address \(correlating it via cluster\_info table\).
|
||||
|
||||
The clients can then send repair requests to the archiver to retrieve segments.
|
||||
The clients can then send repair requests to the replicator to retrieve segments.
|
||||
|
||||
3. Validators would need to invalidate this list every N turns.
|
||||
|
||||
@@ -209,11 +209,11 @@ For any random seed, we force everyone to use a signature that is derived from a
|
||||
|
||||
Since there are many more client identities then encryption identities, we need to split the reward for multiple clients, and prevent Sybil attacks from generating many clients to acquire the same block of data. To remain BFT we want to avoid a single human entity from storing all the replications of a single chunk of the ledger.
|
||||
|
||||
Our solution to this is to force the clients to continue using the same identity. If the first round is used to acquire the same block for many client identities, the second round for the same client identities will force a redistribution of the signatures, and therefore PoRep identities and blocks. Thus to get a reward for archivers need to store the first block for free and the network can reward long lived client identities more than new ones.
|
||||
Our solution to this is to force the clients to continue using the same identity. If the first round is used to acquire the same block for many client identities, the second round for the same client identities will force a redistribution of the signatures, and therefore PoRep identities and blocks. Thus to get a reward for replicators need to store the first block for free and the network can reward long lived client identities more than new ones.
|
||||
|
||||
## Validator attacks
|
||||
|
||||
* If a validator approves fake proofs, archiver can easily out them by
|
||||
* If a validator approves fake proofs, replicator can easily out them by
|
||||
|
||||
showing the initial state for the hash.
|
||||
|
||||
@@ -221,11 +221,11 @@ Our solution to this is to force the clients to continue using the same identity
|
||||
|
||||
to distinguish who is correct. Rewards would have to rely on the results from
|
||||
|
||||
multiple validators to catch bad actors and archivers from being denied rewards.
|
||||
multiple validators to catch bad actors and replicators from being denied rewards.
|
||||
|
||||
* Validator stealing mining proof results for itself. The proofs are derived
|
||||
|
||||
from a signature from an archiver, since the validator does not know the
|
||||
from a signature from a replicator, since the validator does not know the
|
||||
|
||||
private key used to generate the encryption key, it cannot be the generator of
|
||||
|
||||
@@ -233,7 +233,7 @@ Our solution to this is to force the clients to continue using the same identity
|
||||
|
||||
## Reward incentives
|
||||
|
||||
Fake proofs are easy to generate but difficult to verify. For this reason, PoRep proof transactions generated by archivers may require a higher fee than a normal transaction to represent the computational cost required by validators.
|
||||
Fake proofs are easy to generate but difficult to verify. For this reason, PoRep proof transactions generated by replicators may require a higher fee than a normal transaction to represent the computational cost required by validators.
|
||||
|
||||
Some percentage of fake proofs are also necessary to receive a reward from storage mining.
|
||||
|
||||
@@ -247,13 +247,13 @@ Some percentage of fake proofs are also necessary to receive a reward from stora
|
||||
|
||||
use the signatures as the seed
|
||||
|
||||
* The game between validators and archivers is over random blocks and random
|
||||
* The game between validators and replicators is over random blocks and random
|
||||
|
||||
encryption identities and random data samples. The goal of randomization is
|
||||
|
||||
to prevent colluding groups from having overlap on data or validation.
|
||||
|
||||
* Archiver clients fish for lazy validators by submitting fake proofs that
|
||||
* Replicator clients fish for lazy validators by submitting fake proofs that
|
||||
|
||||
they can prove are fake.
|
||||
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# Managing Forks
|
||||
|
||||
The ledger is permitted to fork at slot boundaries. The resulting data structure forms a tree called a _blockstore_. When the validator interprets the blockstore, it must maintain state for each fork in the chain. We call each instance an _active fork_. It is the responsibility of a validator to weigh those forks, such that it may eventually select a fork.
|
||||
The ledger is permitted to fork at slot boundaries. The resulting data structure forms a tree called a _blocktree_. When the fullnode interprets the blocktree, it must maintain state for each fork in the chain. We call each instance an _active fork_. It is the responsibility of a fullnode to weigh those forks, such that it may eventually select a fork.
|
||||
|
||||
A validator selects a fork by submiting a vote to a slot leader on that fork. The vote commits the validator for a duration of time called a _lockout period_. The validator is not permitted to vote on a different fork until that lockout period expires. Each subsequent vote on the same fork doubles the length of the lockout period. After some cluster-configured number of votes \(currently 32\), the length of the lockout period reaches what's called _max lockout_. Until the max lockout is reached, the validator has the option to wait until the lockout period is over and then vote on another fork. When it votes on another fork, it performs a operation called _rollback_, whereby the state rolls back in time to a shared checkpoint and then jumps forward to the tip of the fork that it just voted on. The maximum distance that a fork may roll back is called the _rollback depth_. Rollback depth is the number of votes required to achieve max lockout. Whenever a validator votes, any checkpoints beyond the rollback depth become unreachable. That is, there is no scenario in which the validator will need to roll back beyond rollback depth. It therefore may safely _prune_ unreachable forks and _squash_ all checkpoints beyond rollback depth into the root checkpoint.
|
||||
A fullnode selects a fork by submiting a vote to a slot leader on that fork. The vote commits the fullnode for a duration of time called a _lockout period_. The fullnode is not permitted to vote on a different fork until that lockout period expires. Each subsequent vote on the same fork doubles the length of the lockout period. After some cluster-configured number of votes \(currently 32\), the length of the lockout period reaches what's called _max lockout_. Until the max lockout is reached, the fullnode has the option to wait until the lockout period is over and then vote on another fork. When it votes on another fork, it performs a operation called _rollback_, whereby the state rolls back in time to a shared checkpoint and then jumps forward to the tip of the fork that it just voted on. The maximum distance that a fork may roll back is called the _rollback depth_. Rollback depth is the number of votes required to achieve max lockout. Whenever a fullnode votes, any checkpoints beyond the rollback depth become unreachable. That is, there is no scenario in which the fullnode will need to roll back beyond rollback depth. It therefore may safely _prune_ unreachable forks and _squash_ all checkpoints beyond rollback depth into the root checkpoint.
|
||||
|
||||
## Active Forks
|
||||
|
||||
An active fork is as a sequence of checkpoints that has a length at least one longer than the rollback depth. The shortest fork will have a length exactly one longer than the rollback depth. For example:
|
||||
|
||||

|
||||

|
||||
|
||||
The following sequences are _active forks_:
|
||||
|
||||
@@ -19,16 +19,17 @@ The following sequences are _active forks_:
|
||||
|
||||
## Pruning and Squashing
|
||||
|
||||
A validator may vote on any checkpoint in the tree. In the diagram above, that's every node except the leaves of the tree. After voting, the validator prunes nodes that fork from a distance farther than the rollback depth and then takes the opportunity to minimize its memory usage by squashing any nodes it can into the root.
|
||||
A fullnode may vote on any checkpoint in the tree. In the diagram above, that's every node except the leaves of the tree. After voting, the fullnode prunes nodes that fork from a distance farther than the rollback depth and then takes the opportunity to minimize its memory usage by squashing any nodes it can into the root.
|
||||
|
||||
Starting from the example above, wth a rollback depth of 2, consider a vote on 5 versus a vote on 6. First, a vote on 5:
|
||||
|
||||

|
||||

|
||||
|
||||
The new root is 2, and any active forks that are not descendants from 2 are pruned.
|
||||
|
||||
Alternatively, a vote on 6:
|
||||
|
||||

|
||||

|
||||
|
||||
The tree remains with a root of 1, since the active fork starting at 6 is only 2 checkpoints from the root.
|
||||
|
||||
|
@@ -14,11 +14,3 @@ Each validator node maintains a list of active ledger forks that are visible to
|
||||
|
||||
The node assigns a timestamp to every new fork, and computes the time it took to confirm the fork. This time is reflected as validator confirmation time in performance metrics. The performance dashboard displays the average of each validator node's confirmation time as a time series graph.
|
||||
|
||||
## Hardware setup
|
||||
|
||||
The validator software is deployed to GCP n1-standard-16 instances with 1TB pd-ssd disk, and 2x Nvidia V100 GPUs. These are deployed in the us-west-1 region.
|
||||
|
||||
solana-bench-tps is started after the network converges from a client machine with n1-standard-16 CPU-only instance with the following arguments: `--tx\_count=50000 --thread-batch-sleep 1000`
|
||||
|
||||
TPS and confirmation metrics are captured from the dashboard numbers over a 5 minute average of when the bench-tps transfer stage begins.
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Stake Delegation and Rewards
|
||||
|
||||
Stakers are rewarded for helping to validate the ledger. They do this by delegating their stake to validator nodes. Those validators do the legwork of replaying the ledger and send votes to a per-node vote account to which stakers can delegate their stakes. The rest of the cluster uses those stake-weighted votes to select a block when forks arise. Both the validator and staker need some economic incentive to play their part. The validator needs to be compensated for its hardware and the staker needs to be compensated for the risk of getting its stake slashed. The economics are covered in [staking rewards](../implemented-proposals/staking-rewards.md). This chapter, on the other hand, describes the underlying mechanics of its implementation.
|
||||
Stakers are rewarded for helping to validate the ledger. They do this by delegating their stake to validator nodes. Those validators do the legwork of replaying the ledger and send votes to a per-node vote account to which stakers can delegate their stakes. The rest of the cluster uses those stake-weighted votes to select a block when forks arise. Both the validator and staker need some economic incentive to play their part. The validator needs to be compensated for its hardware and the staker needs to be compensated for the risk of getting its stake slashed. The economics are covered in [staking rewards](../proposals/staking-rewards.md). This chapter, on the other hand, describes the underlying mechanics of its implementation.
|
||||
|
||||
## Basic Design
|
||||
|
||||
@@ -16,7 +16,7 @@ The total stake allocated to a Vote account can be calculated by the sum of all
|
||||
|
||||
## Vote and Stake accounts
|
||||
|
||||
The rewards process is split into two on-chain programs. The Vote program solves the problem of making stakes slashable. The Stake program acts as custodian of the rewards pool and provides for passive delegation. The Stake program is responsible for paying rewards to staker and voter when shown that a staker's delegate has participated in validating the ledger.
|
||||
The rewards process is split into two on-chain programs. The Vote program solves the problem of making stakes slashable. The Stake account acts as custodian of the rewards pool, and provides passive delegation. The Stake program is responsible for paying out each staker once the staker proves to the Stake program that its delegate has participated in validating the ledger.
|
||||
|
||||
### VoteState
|
||||
|
||||
@@ -27,42 +27,42 @@ VoteState is the current state of all the votes the validator has submitted to t
|
||||
* `root_slot` - The last slot to reach the full lockout commitment necessary for rewards.
|
||||
* `commission` - The commission taken by this VoteState for any rewards claimed by staker's Stake accounts. This is the percentage ceiling of the reward.
|
||||
* Account::lamports - The accumulated lamports from the commission. These do not count as stakes.
|
||||
* `authorized_voter` - Only this identity is authorized to submit votes. This field can only modified by this identity.
|
||||
* `node_pubkey` - The Solana node that votes in this account.
|
||||
* `authorized_withdrawer` - the identity of the entity in charge of the lamports of this account, separate from the account's
|
||||
* `authorized_vote_signer` - Only this identity is authorized to submit votes. This field can only modified by this identity.
|
||||
|
||||
```text
|
||||
address and the authorized vote signer
|
||||
```
|
||||
|
||||
### VoteInstruction::Initialize\(VoteInit\)
|
||||
### VoteInstruction::Initialize
|
||||
|
||||
* `account[0]` - RW - The VoteState
|
||||
|
||||
`VoteInit` carries the new vote account's `node_pubkey`, `authorized_voter`, `authorized_withdrawer`, and `commission`
|
||||
`VoteState::authorized_vote_signer` is initialized to `account[0]`
|
||||
|
||||
other VoteState members defaulted
|
||||
other VoteState members defaulted
|
||||
|
||||
### VoteInstruction::Authorize\(Pubkey, VoteAuthorize\)
|
||||
|
||||
Updates the account with a new authorized voter or withdrawer, according to the VoteAuthorize parameter \(`Voter` or `Withdrawer`\). The transaction must be by signed by the Vote account's current `authorized_voter` or `authorized_withdrawer`.
|
||||
### VoteInstruction::AuthorizeVoteSigner\(Pubkey\)
|
||||
|
||||
* `account[0]` - RW - The VoteState
|
||||
|
||||
`VoteState::authorized_voter` or `authorized_withdrawer` is set to to `Pubkey`.
|
||||
`VoteState::authorized_vote_signer` is set to to `Pubkey`, the transaction must by
|
||||
|
||||
### VoteInstruction::Vote\(Vote\)
|
||||
signed by the Vote account's current `authorized_vote_signer`.
|
||||
|
||||
|
||||
`VoteInstruction::AuthorizeVoter` allows a staker to choose a signing service
|
||||
|
||||
for its votes. That service is responsible for ensuring the vote won't cause
|
||||
|
||||
the staker to be slashed.
|
||||
|
||||
### VoteInstruction::Vote\(Vec\)
|
||||
|
||||
* `account[0]` - RW - The VoteState
|
||||
|
||||
`VoteState::lockouts` and `VoteState::credits` are updated according to voting lockout rules see [Tower BFT](../implemented-proposals/tower-bft.md)
|
||||
|
||||
* `account[1]` - RO - `sysvar::slot_hashes` A list of some N most recent slots and their hashes for the vote to be verified against.
|
||||
* `account[2]` - RO - `sysvar::clock` The current network time, expressed in slots, epochs.
|
||||
* `account[1]` - RO - A list of some N most recent slots and their hashes for the vote to be verified against.
|
||||
|
||||
### StakeState
|
||||
|
||||
A StakeState takes one of four forms, StakeState::Uninitialized, StakeState::Initialized, StakeState::Stake, and StakeState::RewardsPool. Only the first three forms are used in staking, but only StakeState::Stake is interesting. All RewardsPools are created at genesis.
|
||||
A StakeState takes one of three forms, StakeState::Uninitialized, StakeState::Stake and StakeState::RewardsPool.
|
||||
|
||||
### StakeState::Stake
|
||||
|
||||
@@ -73,18 +73,7 @@ StakeState::Stake is the current delegation preference of the **staker** and con
|
||||
* `voter_pubkey` - The pubkey of the VoteState instance the lamports are delegated to.
|
||||
* `credits_observed` - The total credits claimed over the lifetime of the program.
|
||||
* `activated` - the epoch at which this stake was activated/delegated. The full stake will be counted after warm up.
|
||||
* `deactivated` - the epoch at which this stake was de-activated, some cool down epochs are required before the account
|
||||
|
||||
```text
|
||||
is fully deactivated, and the stake available for withdrawal
|
||||
```
|
||||
|
||||
* `authorized_staker` - the pubkey of the entity that must sign delegation, activation, and deactivation transactions
|
||||
* `authorized_withdrawer` - the identity of the entity in charge of the lamports of this account, separate from the account's
|
||||
|
||||
```text
|
||||
address, and the authorized staker
|
||||
```
|
||||
* `deactivated` - the epoch at which this stake will be completely de-activated, which is `cool down` epochs after StakeInstruction::Deactivate is issued.
|
||||
|
||||
### StakeState::RewardsPool
|
||||
|
||||
@@ -92,41 +81,53 @@ To avoid a single network wide lock or contention in redemption, 256 RewardsPool
|
||||
|
||||
The Stakes and the RewardsPool are accounts that are owned by the same `Stake` program.
|
||||
|
||||
### StakeInstruction::DelegateStake
|
||||
### StakeInstruction::DelegateStake\(u64\)
|
||||
|
||||
The Stake account is moved from Initialized to StakeState::Stake form, or from a deactivated (i.e. fully cooled-down) StakeState::Stake to activated StakeState::Stake. This is how stakers choose the vote account and validator node to which their stake account lamports are delegated. The transaction must be signed by the stake's `authorized_staker`.
|
||||
The Stake account is moved from Uninitialized to StakeState::Stake form. This is how stakers choose their initial delegate validator node and activate their stake account lamports.
|
||||
|
||||
* `account[0]` - RW - The StakeState::Stake instance. `StakeState::Stake::credits_observed` is initialized to `VoteState::credits`, `StakeState::Stake::voter_pubkey` is initialized to `account[1]`. If this is the initial delegation of stake, `StakeState::Stake::stake` is initialized to the account's balance in lamports, `StakeState::Stake::activated` is initialized to the current Bank epoch, and `StakeState::Stake::deactivated` is initialized to std::u64::MAX
|
||||
* `account[0]` - RW - The StakeState::Stake instance. `StakeState::Stake::credits_observed` is initialized to `VoteState::credits`, `StakeState::Stake::voter_pubkey` is initialized to `account[1]`, `StakeState::Stake::stake` is initialized to the u64 passed as an argument above, `StakeState::Stake::activated` is initialized to current Bank epoch, and `StakeState::Stake::deactivated` is initialized to std::u64::MAX
|
||||
* `account[1]` - R - The VoteState instance.
|
||||
* `account[2]` - R - sysvar::clock account, carries information about current Bank epoch
|
||||
* `account[3]` - R - sysvar::stakehistory account, carries information about stake history
|
||||
* `account[4]` - R - stake::Config accoount, carries warmup, cooldown, and slashing configuration
|
||||
* `account[2]` - R - sysvar::current account, carries information about current Bank epoch
|
||||
* `account[3]` - R - stake\_api::Config accoount, carries warmup, cooldown, and slashing configuration
|
||||
|
||||
### StakeInstruction::Authorize\(Pubkey, StakeAuthorize\)
|
||||
### StakeInstruction::RedeemVoteCredits
|
||||
|
||||
Updates the account with a new authorized staker or withdrawer, according to the StakeAuthorize parameter \(`Staker` or `Withdrawer`\). The transaction must be by signed by the Stakee account's current `authorized_staker` or `authorized_withdrawer`. Any stake lock-up must have expired, or the lock-up custodian must also sign the transaction.
|
||||
The staker or the owner of the Stake account sends a transaction with this instruction to claim rewards.
|
||||
|
||||
* `account[0]` - RW - The StakeState
|
||||
The Vote account and the Stake account pair maintain a lifetime counter of total rewards generated and claimed. Rewards are paid according to a point value supplied by the Bank from inflation. A `point` is one credit \* one staked lamport, rewards paid are proportional to the number of lamports staked.
|
||||
|
||||
`StakeState::authorized_staker` or `authorized_withdrawer` is set to to `Pubkey`.
|
||||
* `account[0]` - RW - The StakeState::Stake instance that is redeeming rewards.
|
||||
* `account[1]` - R - The VoteState instance, must be the same as `StakeState::voter_pubkey`
|
||||
* `account[2]` - RW - The StakeState::RewardsPool instance that will fulfill the request \(picked at random\).
|
||||
* `account[3]` - R - sysvar::rewards account from the Bank that carries point value.
|
||||
* `account[4]` - R - sysvar::stake\_history account from the Bank that carries stake warmup/cooldown history
|
||||
|
||||
Reward is paid out for the difference between `VoteState::credits` to `StakeState::Stake::credits_observed`, multiplied by `sysvar::rewards::Rewards::validator_point_value`. `StakeState::Stake::credits_observed` is updated to`VoteState::credits`. The commission is deposited into the Vote account token balance, and the reward is deposited to the Stake account token balance.
|
||||
|
||||
```text
|
||||
let credits_to_claim = vote_state.credits - stake_state.credits_observed;
|
||||
stake_state.credits_observed = vote_state.credits;
|
||||
```
|
||||
|
||||
`credits_to_claim` is used to compute the reward and commission, and `StakeState::Stake::credits_observed` is updated to the latest `VoteState::credits` value.
|
||||
|
||||
### StakeInstruction::Deactivate
|
||||
|
||||
A staker may wish to withdraw from the network. To do so he must first deactivate his stake, and wait for cool down.
|
||||
The transaction must be signed by the stake's `authorized_staker`.
|
||||
|
||||
* `account[0]` - RW - The StakeState::Stake instance that is deactivating.
|
||||
* `account[1]` - R - sysvar::clock account from the Bank that carries current epoch
|
||||
* `account[0]` - RW - The StakeState::Stake instance that is deactivating, the transaction must be signed by this key.
|
||||
* `account[1]` - R - The VoteState instance to which this stake is delegated, required in case of slashing
|
||||
* `account[2]` - R - sysvar::current account from the Bank that carries current epoch
|
||||
|
||||
StakeState::Stake::deactivated is set to the current epoch + cool down. The account's stake will ramp down to zero by that epoch, and Account::lamports will be available for withdrawal.
|
||||
|
||||
### StakeInstruction::Withdraw\(u64\)
|
||||
|
||||
Lamports build up over time in a Stake account and any excess over activated stake can be withdrawn. The transaction must be signed by the stake's `authorized_withdrawer`.
|
||||
Lamports build up over time in a Stake account and any excess over activated stake can be withdrawn.
|
||||
|
||||
* `account[0]` - RW - The StakeState::Stake from which to withdraw.
|
||||
* `account[0]` - RW - The StakeState::Stake from which to withdraw, the transaction must be signed by this key.
|
||||
* `account[1]` - RW - Account that should be credited with the withdrawn lamports.
|
||||
* `account[2]` - R - sysvar::clock account from the Bank that carries current epoch, to calculate stake.
|
||||
* `account[2]` - R - sysvar::current account from the Bank that carries current epoch, to calculate stake.
|
||||
* `account[3]` - R - sysvar::stake\_history account from the Bank that carries stake warmup/cooldown history
|
||||
|
||||
## Benefits of the design
|
||||
@@ -138,15 +139,15 @@ Lamports build up over time in a Stake account and any excess over activated sta
|
||||
|
||||
## Example Callflow
|
||||
|
||||

|
||||

|
||||
|
||||
## Staking Rewards
|
||||
|
||||
The specific mechanics and rules of the validator rewards regime is outlined here. Rewards are earned by delegating stake to a validator that is voting correctly. Voting incorrectly exposes that validator's stakes to [slashing](../proposals/slashing.md).
|
||||
The specific mechanics and rules of the validator rewards regime is outlined here. Rewards are earned by delegating stake to a validator that is voting correctly. Voting incorrectly exposes that validator's stakes to [slashing](https://github.com/solana-labs/solana/tree/6b18db969dd1616eff07de35e7b823c75339fea8/book/src/staking-and-rewards.md).
|
||||
|
||||
### Basics
|
||||
|
||||
The network pays rewards from a portion of network [inflation](../terminology.md#inflation). The number of lamports available to pay rewards for an epoch is fixed and must be evenly divided among all staked nodes according to their relative stake weight and participation. The weighting unit is called a [point](../terminology.md#point).
|
||||
The network pays rewards from a portion of network [inflation](https://github.com/solana-labs/solana/tree/6b18db969dd1616eff07de35e7b823c75339fea8/book/src/inflation.md). The number of lamports available to pay rewards for an epoch is fixed and must be evenly divided among all staked nodes according to their relative stake weight and participation. The weighting unit is called a [point](../terminology.md#point).
|
||||
|
||||
Rewards for an epoch are not available until the end of that epoch.
|
||||
|
||||
@@ -168,7 +169,7 @@ Stakers who have delegated to that validator earn points in proportion to their
|
||||
|
||||
Stakes, once delegated, do not become effective immediately. They must first pass through a warm up period. During this period some portion of the stake is considered "effective", the rest is considered "activating". Changes occur on epoch boundaries.
|
||||
|
||||
The stake program limits the rate of change to total network stake, reflected in the stake program's `config::warmup_rate` \(typically 25% per epoch\).
|
||||
The stake program limits the rate of change to total network stake, reflected in the stake program's `config::warmup_rate` \(typically 15% per epoch\).
|
||||
|
||||
The amount of stake that can be warmed up each epoch is a function of the previous epoch's total effective stake, total activating stake, and the stake program's configured warmup rate.
|
||||
|
||||
@@ -198,14 +199,11 @@ Were 2 stakes \(X and Y\) to activate at epoch N, they would be awarded a portio
|
||||
| :--- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| N-1 | | | | | 2,000 | 0 |
|
||||
| N | 0 | 1,000 | 0 | 200 | 2,000 | 1,200 |
|
||||
| N+1 | 333 | 667 | 67 | 133 | 2,400 | 800 |
|
||||
| N+2 | 733 | 267 | 146 | 54 | 2,880 | 321 |
|
||||
| N+1 | 320 | 680 | 80 | 120 | 2,400 | 800 |
|
||||
| N+2 | 728 | 272 | 152 | 48 | 2,880 | 320 |
|
||||
| N+3 | 1000 | 0 | 200 | 0 | 3,200 | 0 |
|
||||
|
||||
### Withdrawal
|
||||
|
||||
Only lamports in excess of effective+activating stake may be withdrawn at any time. This means that during warmup, effectively no stake can be withdrawn. During cooldown, any tokens in excess of effective stake may be withdrawn \(activating == 0\). Because earned rewards are automatically added to stake, withdrawal is generally only possible after deactivation.
|
||||
As rewards are earned lamports can be withdrawn from a stake account. Only lamports in excess of effective+activating stake may be withdrawn at any time. This means that during warmup, effectively no stake can be withdrawn. During cooldown, any tokens in excess of effective stake may be withdrawn \(activating == 0\);
|
||||
|
||||
### Lock-up
|
||||
|
||||
Stake accounts support the notion of lock-up, wherein the stake account balance is unavailable for withdrawal until a specified time. Lock-up is specified as an epoch height, i.e. the minimum epoch height that must be reached by the network before the stake account balance is available for withdrawal, unless the transaction is also signed by a specified custodian. This information is gathered when the stake account is created, and stored in the Lockup field of the stake account's state. Changing the authorized staker or withdrawer is also subject to lock-up, as such an operation is effectively a transfer.
|
||||
|
@@ -1,10 +1,10 @@
|
||||
# Synchronization
|
||||
|
||||
Fast, reliable synchronization is the biggest reason Solana is able to achieve such high throughput. Traditional blockchains synchronize on large chunks of transactions called blocks. By synchronizing on blocks, a transaction cannot be processed until a duration called "block time" has passed. In Proof of Work consensus, these block times need to be very large \(~10 minutes\) to minimize the odds of multiple validators producing a new valid block at the same time. There's no such constraint in Proof of Stake consensus, but without reliable timestamps, a validator cannot determine the order of incoming blocks. The popular workaround is to tag each block with a [wallclock timestamp](https://en.bitcoin.it/wiki/Block_timestamp). Because of clock drift and variance in network latencies, the timestamp is only accurate within an hour or two. To workaround the workaround, these systems lengthen block times to provide reasonable certainty that the median timestamp on each block is always increasing.
|
||||
Fast, reliable synchronization is the biggest reason Solana is able to achieve such high throughput. Traditional blockchains synchronize on large chunks of transactions called blocks. By synchronizing on blocks, a transaction cannot be processed until a duration called "block time" has passed. In Proof of Work consensus, these block times need to be very large \(~10 minutes\) to minimize the odds of multiple fullnodes producing a new valid block at the same time. There's no such constraint in Proof of Stake consensus, but without reliable timestamps, a fullnode cannot determine the order of incoming blocks. The popular workaround is to tag each block with a [wallclock timestamp](https://en.bitcoin.it/wiki/Block_timestamp). Because of clock drift and variance in network latencies, the timestamp is only accurate within an hour or two. To workaround the workaround, these systems lengthen block times to provide reasonable certainty that the median timestamp on each block is always increasing.
|
||||
|
||||
Solana takes a very different approach, which it calls _Proof of History_ or _PoH_. Leader nodes "timestamp" blocks with cryptographic proofs that some duration of time has passed since the last proof. All data hashed into the proof most certainly have occurred before the proof was generated. The node then shares the new block with validator nodes, which are able to verify those proofs. The blocks can arrive at validators in any order or even could be replayed years later. With such reliable synchronization guarantees, Solana is able to break blocks into smaller batches of transactions called _entries_. Entries are streamed to validators in realtime, before any notion of block consensus.
|
||||
|
||||
Solana technically never sends a _block_, but uses the term to describe the sequence of entries that validators vote on to achieve _confirmation_. In that way, Solana's confirmation times can be compared apples to apples to block-based systems. The current implementation sets block time to 800ms.
|
||||
Solana technically never sends a _block_, but uses the term to describe the sequence of entries that fullnodes vote on to achieve _confirmation_. In that way, Solana's confirmation times can be compared apples to apples to block-based systems. The current implementation sets block time to 800ms.
|
||||
|
||||
What's happening under the hood is that entries are streamed to validators as quickly as a leader node can batch a set of valid transactions into an entry. Validators process those entries long before it is time to vote on their validity. By processing the transactions optimistically, there is effectively no delay between the time the last entry is received and the time when the node can vote. In the event consensus is **not** achieved, a node simply rolls back its state. This optimisic processing technique was introduced in 1981 and called [Optimistic Concurrency Control](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.65.4735). It can be applied to blockchain architecture where a cluster votes on a hash that represents the full ledger up to some _block height_. In Solana, it is implemented trivially using the last entry's PoH hash.
|
||||
|
||||
|
@@ -1,34 +1,34 @@
|
||||
# Turbine Block Propagation
|
||||
|
||||
A Solana cluster uses a multi-layer block propagation mechanism called _Turbine_ to broadcast transaction shreds to all nodes with minimal amount of duplicate messages. The cluster divides itself into small collections of nodes, called _neighborhoods_. Each node is responsible for sharing any data it receives with the other nodes in its neighborhood, as well as propagating the data on to a small set of nodes in other neighborhoods. This way each node only has to communicate with a small number of nodes.
|
||||
A Solana cluster uses a multi-layer block propagation mechanism called _Turbine_ to broadcast transaction blobs to all nodes with minimal amount of duplicate messages. The cluster divides itself into small collections of nodes, called _neighborhoods_. Each node is responsible for sharing any data it receives with the other nodes in its neighborhood, as well as propagating the data on to a small set of nodes in other neighborhoods. This way each node only has to communicate with a small number of nodes.
|
||||
|
||||
During its slot, the leader node distributes shreds between the validator nodes in the first neighborhood \(layer 0\). Each validator shares its data within its neighborhood, but also retransmits the shreds to one node in some neighborhoods in the next layer \(layer 1\). The layer-1 nodes each share their data with their neighborhood peers, and retransmit to nodes in the next layer, etc, until all nodes in the cluster have received all the shreds.
|
||||
During its slot, the leader node distributes blobs between the validator nodes in the first neighborhood \(layer 0\). Each validator shares its data within its neighborhood, but also retransmits the blobs to one node in some neighborhoods in the next layer \(layer 1\). The layer-1 nodes each share their data with their neighborhood peers, and retransmit to nodes in the next layer, etc, until all nodes in the cluster have received all the blobs.
|
||||
|
||||
## Neighborhood Assignment - Weighted Selection
|
||||
|
||||
In order for data plane fanout to work, the entire cluster must agree on how the cluster is divided into neighborhoods. To achieve this, all the recognized validator nodes \(the TVU peers\) are sorted by stake and stored in a list. This list is then indexed in different ways to figure out neighborhood boundaries and retransmit peers. For example, the leader will simply select the first nodes to make up layer 0. These will automatically be the highest stake holders, allowing the heaviest votes to come back to the leader first. Layer-0 and lower-layer nodes use the same logic to find their neighbors and next layer peers.
|
||||
|
||||
To reduce the possibility of attack vectors, each shred is transmitted over a random tree of neighborhoods. Each node uses the same set of nodes representing the cluster. A random tree is generated from the set for each shred using randomness derived from the shred itself. Since the random seed is not known in advance, attacks that try to eclipse neighborhoods from certain leaders or blocks become very difficult, and should require almost complete control of the stake in the cluster.
|
||||
To reduce the possibility of attack vectors, each blob is transmitted over a random tree of neighborhoods. Each node uses the same set of nodes representing the cluster. A random tree is generated from the set for each blob using randomness derived from the blob itself. Since the random seed is not known in advance, attacks that try to eclipse neighborhoods from certain leaders or blocks become very difficult, and should require almost complete control of the stake in the cluster.
|
||||
|
||||
## Layer and Neighborhood Structure
|
||||
|
||||
The current leader makes its initial broadcasts to at most `DATA_PLANE_FANOUT` nodes. If this layer 0 is smaller than the number of nodes in the cluster, then the data plane fanout mechanism adds layers below. Subsequent layers follow these constraints to determine layer-capacity: Each neighborhood contains `DATA_PLANE_FANOUT` nodes. Layer-0 starts with 1 neighborhood with fanout nodes. The number of nodes in each additional layer grows by a factor of fanout.
|
||||
|
||||
As mentioned above, each node in a layer only has to broadcast its shreds to its neighbors and to exactly 1 node in some next-layer neighborhoods, instead of to every TVU peer in the cluster. A good way to think about this is, layer-0 starts with 1 neighborhood with fanout nodes, layer-1 adds "fanout" neighborhoods, each with fanout nodes and layer-2 will have `fanout * number of nodes in layer-1` and so on.
|
||||
As mentioned above, each node in a layer only has to broadcast its blobs to its neighbors and to exactly 1 node in some next-layer neighborhoods, instead of to every TVU peer in the cluster. A good way to think about this is, layer-0 starts with 1 neighborhood with fanout nodes, layer-1 adds "fanout" neighborhoods, each with fanout nodes and layer-2 will have `fanout * number of nodes in layer-1` and so on.
|
||||
|
||||
This way each node only has to communicate with a maximum of `2 * DATA_PLANE_FANOUT - 1` nodes.
|
||||
|
||||
The following diagram shows how the Leader sends shreds with a Fanout of 2 to Neighborhood 0 in Layer 0 and how the nodes in Neighborhood 0 share their data with each other.
|
||||
The following diagram shows how the Leader sends blobs with a Fanout of 2 to Neighborhood 0 in Layer 0 and how the nodes in Neighborhood 0 share their data with each other.
|
||||
|
||||

|
||||

|
||||
|
||||
The following diagram shows how Neighborhood 0 fans out to Neighborhoods 1 and 2.
|
||||
|
||||

|
||||

|
||||
|
||||
Finally, the following diagram shows a two layer cluster with a Fanout of 2.
|
||||
|
||||

|
||||

|
||||
|
||||
### Configuration Values
|
||||
|
||||
@@ -36,61 +36,9 @@ Finally, the following diagram shows a two layer cluster with a Fanout of 2.
|
||||
|
||||
Currently, configuration is set when the cluster is launched. In the future, these parameters may be hosted on-chain, allowing modification on the fly as the cluster sizes change.
|
||||
|
||||
## Calcuating the required FEC rate
|
||||
|
||||
Turbine relies on retransmission of packets between validators. Due to
|
||||
retransmission, any network wide packet loss is compounded, and the
|
||||
probability of the packet failing to reach is destination increases
|
||||
on each hop. The FEC rate needs to take into account the network wide
|
||||
packet loss, and the propagation depth.
|
||||
|
||||
A shred group is the set of data and coding packets that can be used
|
||||
to reconstruct each other. Each shred group has a chance of failure,
|
||||
based on the likelyhood of the number of packets failing that exceeds
|
||||
the FEC rate. If a validator fails to reconstruct the shred group,
|
||||
then the block cannot be reconstructed, and the validator has to rely
|
||||
on repair to fixup the blocks.
|
||||
|
||||
The probability of the shred group failing can be computed using the
|
||||
binomial distribution. If the FEC rate is `16:4`, then the group size
|
||||
is 20, and at least 4 of the shreds must fail for the group to fail.
|
||||
Which is equal to the sum of the probability of 4 or more trails failing
|
||||
out of 20.
|
||||
|
||||
Probability of a block succeeding in turbine:
|
||||
|
||||
* Probability of packet failure: `P = 1 - (1 - network_packet_loss_rate)^2`
|
||||
* FEC rate: `K:M`
|
||||
* Number of trials: `N = K + M`
|
||||
* Shred group failure rate: `S = SUM of i=0 -> M for binomial(prob_failure = P, trials = N, failures = i)`
|
||||
* Shreds per block: `G`
|
||||
* Block success rate: `B = (1 - S) ^ (G / N) `
|
||||
* Binomial distribution for exactly `i` results with probability of P in N trials is defined as `(N choose i) * P^i * (1 - P)^(N-i)`
|
||||
|
||||
For example:
|
||||
|
||||
* Network packet loss rate is 15%.
|
||||
* 50kpts network generates 6400 shreds per second.
|
||||
* FEC rate increases the total shres per block by the FEC ratio.
|
||||
|
||||
With a FEC rate: `16:4`
|
||||
* `G = 8000`
|
||||
* `P = 1 - 0.85 * 0.85 = 1 - 0.7225 = 0.2775`
|
||||
* `S = SUM of i=0 -> 4 for binomial(prob_failure = 0.2775, trials = 20, failures = i) = 0.689414`
|
||||
* `B = (1 - 0.689) ^ (8000 / 20) = 10^-203`
|
||||
|
||||
With FEC rate of `16:16`
|
||||
* `G = 12800`
|
||||
* `S = SUM of i=0 -> 32 for binomial(prob_failure = 0.2775, trials = 64, failures = i) = 0.002132`
|
||||
* `B = (1 - 0.002132) ^ (12800 / 32) = 0.42583`
|
||||
|
||||
With FEC rate of `32:32`
|
||||
* `G = 12800`
|
||||
* `S = SUM of i=0 -> 32 for binomial(prob_failure = 0.2775, trials = 64, failures = i) = 0.000048`
|
||||
* `B = (1 - 0.000048) ^ (12800 / 64) = 0.99045`
|
||||
|
||||
## Neighborhoods
|
||||
|
||||
The following diagram shows how two neighborhoods in different layers interact. To cripple a neighborhood, enough nodes \(erasure codes +1\) from the neighborhood above need to fail. Since each neighborhood receives shreds from multiple nodes in a neighborhood in the upper layer, we'd need a big network failure in the upper layers to end up with incomplete data.
|
||||
The following diagram shows how two neighborhoods in different layers interact. To cripple a neighborhood, enough nodes \(erasure codes +1\) from the neighborhood above need to fail. Since each neighborhood receives blobs from multiple nodes in a neighborhood in the upper layer, we'd need a big network failure in the upper layers to end up with incomplete data.
|
||||
|
||||

|
||||
|
||||

|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Secure Vote Signing
|
||||
|
||||
A validator receives entries from the current leader and submits votes confirming those entries are valid. This vote submission presents a security challenge, because forged votes that violate consensus rules could be used to slash the validator's stake.
|
||||
A validator fullnode receives entries from the current leader and submits votes confirming those entries are valid. This vote submission presents a security challenge, because forged votes that violate consensus rules could be used to slash the validator's stake.
|
||||
|
||||
The validator votes on its chosen fork by submitting a transaction that uses an asymmetric key to sign the result of its validation work. Other entities can verify this signature using the validator's public key. If the validator's key is used to sign incorrect data \(e.g. votes on multiple forks of the ledger\), the node's stake or its resources could be compromised.
|
||||
|
||||
|
140
book/src/credit-only-credit-debit-accounts.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Credit-Only Accounts
|
||||
|
||||
This design covers the handling of credit-only and credit-debit accounts in the
|
||||
[runtime](runtime.md). Accounts already distinguish themselves as credit-only or
|
||||
credit-debit based on the program ID specified by the transaction's instruction.
|
||||
Programs must treat accounts that are not owned by them as credit-only.
|
||||
|
||||
To identify credit-only accounts by program id would require the account to be
|
||||
fetched and loaded from disk. This operation is expensive, and while it is
|
||||
occurring, the runtime would have to reject any transactions referencing the same
|
||||
account.
|
||||
|
||||
The proposal introduces a `num_readonly_accounts` field to the transaction
|
||||
structure, and removes the `program_ids` dedicated vector for program accounts.
|
||||
|
||||
This design doesn't change the runtime transaction processing rules.
|
||||
Programs still can't write or spend accounts that they do not own, but it
|
||||
allows the runtime to optimistically take the correct lock for each account
|
||||
specified in the transaction before loading the accounts from storage.
|
||||
|
||||
Accounts selected as credit-debit by the transaction can still be treated as
|
||||
credit-only by the instructions.
|
||||
|
||||
## Runtime handling
|
||||
|
||||
credit-only accounts have the following properties:
|
||||
|
||||
* Can be deposited into: Deposits can be implemented as a simple `atomic_add`.
|
||||
* read-only access to account data.
|
||||
|
||||
Instructions that debit or modify the credit-only account data will fail.
|
||||
|
||||
## Account Lock Optimizations
|
||||
|
||||
The Accounts module keeps track of current locked accounts in the runtime,
|
||||
which separates credit-only accounts from the credit-debit accounts. The credit-only
|
||||
accounts can be cached in memory and shared between all the threads executing
|
||||
transactions.
|
||||
|
||||
The current runtime can't predict whether an account is credit-only or credit-debit when
|
||||
the transaction account keys are locked at the start of the transaction
|
||||
processing pipeline. Accounts referenced by the transaction have not been
|
||||
loaded from the disk yet.
|
||||
|
||||
An ideal design would cache the credit-only accounts while they are referenced by
|
||||
any transaction moving through the runtime, and release the cache when the last
|
||||
transaction exits the runtime.
|
||||
|
||||
## Credit-only accounts and read-only account data
|
||||
|
||||
Credit-only account data can be treated as read-only. Credit-debit
|
||||
account data is treated as read-write.
|
||||
|
||||
## Transaction changes
|
||||
|
||||
To enable the possibility of caching accounts only while they are in the
|
||||
runtime, the Transaction structure should be changed in the following way:
|
||||
|
||||
* `program_ids: Vec<Pubkey>` - This vector is removed. Program keys can be
|
||||
placed at the end of the `account_keys` vector within the `num_readonly_accounts`
|
||||
number set to the number of programs.
|
||||
|
||||
* `num_readonly_accounts: u8` - The number of keys from the **end** of the
|
||||
transaction's `account_keys` array that is credit-only.
|
||||
|
||||
The following possible accounts are present in an transaction:
|
||||
|
||||
* paying account
|
||||
* RW accounts
|
||||
* R accounts
|
||||
* Program IDs
|
||||
|
||||
The paying account must be credit-debit, and program IDs must be credit-only. The
|
||||
first account in the `account_keys` array is always the account that pays for
|
||||
the transaction fee, therefore it cannot be credit-only. For these reasons the
|
||||
credit-only accounts are all grouped together at the end of the `account_keys`
|
||||
vector. Counting credit-only accounts from the end allow for the default `0`
|
||||
value to still be functionally correct, since a transaction will succeed with
|
||||
all credit-debit accounts.
|
||||
|
||||
Since accounts can only appear once in the transaction's `account_keys` array,
|
||||
an account can only be credit-only or credit-debit in a single transaction, not
|
||||
both. The runtime treats a transaction as one atomic unit of execution. If any
|
||||
instruction needs credit-debit access to an account, a copy needs to be made. The
|
||||
write lock is held for the entire time the transaction is being processed by
|
||||
the runtime.
|
||||
|
||||
## Starvation
|
||||
|
||||
Read locks for credit-only accounts can keep the runtime from executing
|
||||
transactions requesting a write lock to a credit-debit account.
|
||||
|
||||
When a request for a write lock is made while a read lock is open, the
|
||||
transaction requesting the write lock should be cached. Upon closing the read
|
||||
lock, the pending transactions can be pushed through the runtime.
|
||||
|
||||
While a pending write transaction exists, any additional read lock requests for
|
||||
that account should fail. It follows that any other write lock requests will also
|
||||
fail. Currently, clients must retransmit when a transaction fails because of
|
||||
a pending transaction. This approach would mimic that behavior as closely as
|
||||
possible while preventing write starvation.
|
||||
|
||||
## Program execution with credit-only accounts
|
||||
|
||||
Before handing off the accounts to program execution, the runtime can mark each
|
||||
account in each instruction as a credit-only account. The credit-only accounts can
|
||||
be passed as references without an extra copy. The transaction will abort on a
|
||||
write to credit-only.
|
||||
|
||||
An alternative is to detect writes to credit-only accounts and fail the
|
||||
transactions before commit.
|
||||
|
||||
## Alternative design
|
||||
|
||||
This design attempts to cache a credit-only account after loading without the use
|
||||
of a transaction-specified credit-only accounts list. Instead, the credit-only
|
||||
accounts are held in a reference-counted table inside the runtime as the
|
||||
transactions are processed.
|
||||
|
||||
1. Transaction accounts are locked.
|
||||
a. If the account is present in the ‘credit-only' table, the TX does not fail.
|
||||
The pending state for this TX is marked NeedReadLock.
|
||||
2. Transaction accounts are loaded.
|
||||
a. Transaction accounts that are credit-only increase their reference
|
||||
count in the `credit-only` table.
|
||||
b. Transaction accounts that need a write lock and are present in the
|
||||
`credit-only` table fail.
|
||||
3. Transaction accounts are unlocked.
|
||||
a. Decrement the `credit-only` lock table reference count; remove if its 0
|
||||
b. Remove from the `lock` set if the account is not in the `credit-only`
|
||||
table.
|
||||
|
||||
The downside with this approach is that if the `lock` set mutex is released
|
||||
between lock and load to allow better pipelining of transactions, a request for
|
||||
a credit-only account may fail. Therefore, this approach is not suitable for
|
||||
treating programs as credit-only accounts.
|
||||
|
||||
Holding the accounts lock mutex while fetching the account from disk would
|
||||
potentially have a significant performance hit on the runtime. Fetching from
|
||||
disk is expected to be slow, but can be parallelized between multiple disks.
|