Compare commits

..

73 Commits

Author SHA1 Message Date
mergify[bot]
66d9c56a77 Add buildkite-agent key for colo access (bp #6205) (#6206)
* Add buildkite-agent key for colo access (#6205)

(cherry picked from commit 58139ce5ae)

# Conflicts:
#	net/scripts/solana-user-authorized_keys.sh

* Update solana-user-authorized_keys.sh
2019-10-15 11:53:33 -04:00
Michael Vines
a77ca870c2 Disable cargo audit, not going to cherry-pick audit fixes into v0.19 2019-10-13 21:28:38 -07:00
Michael Vines
15686e7879 Skip deploy attempt on sanity failure 2019-10-12 22:21:12 -07:00
Greg Fitzgerald
068f9a7e60 GitBook: [v0.19] 13 pages and 55 assets modified 2019-10-08 23:35:36 +00:00
mergify[bot]
6d9560717a Bench tps: improve fund_keys (#6225) (#6237)
automerge
2019-10-04 01:45:25 -07:00
Michael Vines
d44b6016d1 Keep the build green when there's nowhere to publish 2019-10-03 14:55:18 -07:00
Michael Vines
4b0577baff Increase testnets to 4 validator nodes to avoid the need for 100% consensus 2019-10-03 09:53:17 -07:00
Michael Vines
f200862230 Cargo.lock 2019-10-03 00:23:12 -07:00
Michael Vines
e2b954fd78 Revert "GitBook: [v0.19] 14 pages and 19 assets modified"
This reverts commit 88950d2fb4.
2019-10-02 23:53:43 -07:00
Michael Vines
88950d2fb4 GitBook: [v0.19] 14 pages and 19 assets modified 2019-10-03 06:41:04 +00:00
Michael Vines
252a55da37 Fix crate metadata 2019-10-02 23:20:59 -07:00
Michael Vines
e419b3cf9f Add description tag 2019-10-02 23:12:55 -07:00
Michael Vines
713818b5eb Add description tag 2019-10-02 23:00:54 -07:00
Michael Vines
882068424d Enable patch branches 2019-10-02 22:48:04 -07:00
Michael Vines
10df95e36f Enable patch branches 2019-10-02 22:24:09 -07:00
Michael Vines
c43f57912c Bump version to 0.19.2 2019-10-02 22:16:16 -07:00
mergify[bot]
8f0cd4e456 Switch to solana-reed-solomon-erasure temporarily to fix windows build (#6220)
automerge
2019-10-02 19:39:45 -07:00
mergify[bot]
382663aef6 Bench-tps: flush tx queue when too old (#6201) (#6208)
automerge
2019-10-01 15:09:53 -07:00
mergify[bot]
ff79693568 Use built-in solana-gossip timeout for better error messages (#6189) (#6203)
automerge
2019-10-01 13:23:10 -07:00
mergify[bot]
517cebf7f0 Add native_token module to sdk (bp #6192) (#6196)
automerge
2019-10-01 13:02:43 -07:00
mergify[bot]
fcae91d7fa Rename bank height to block_height and expose method (#6199) (#6202)
automerge
2019-10-01 12:52:09 -07:00
Justin Starry
c44d3139fd Expose current stake accounts of a bank for use in cli tooling (#6184) (#6190)
automerge
2019-10-01 10:05:49 -07:00
mergify[bot]
effd2fd835 Cleanly error when trying to delegate-stake an existing stake account (#6158) (#6160)
(cherry picked from commit 284273a73f)
2019-10-01 09:02:28 -07:00
mergify[bot]
2cd832521a Clear download progress bar to avoid flicker during archive extraction (#6162) (#6176)
(cherry picked from commit 11fc684f3c)
2019-09-29 18:56:50 -07:00
mergify[bot]
77a4354b93 Don't try signging coding shred if fec rate is 0 (#6171) (#6172)
automerge

(cherry picked from commit 5637f88aff)
2019-09-28 19:38:00 -07:00
mergify[bot]
31f86158e8 Add get-epoch-info command (#6161) (#6173)
automerge
2019-09-27 22:53:47 -07:00
Jack May
29c892ee00 Validator-info doc update (#6152) (#6157) 2019-09-27 14:27:41 -07:00
mergify[bot]
06e34ef1cf Add ability to manually create a db (#6151) (#6154)
automerge
2019-09-27 12:42:17 -07:00
Ryan Shea
11bc11f0b7 Fix URLs for images (#6148) 2019-09-27 12:00:46 -07:00
mergify[bot]
8f27240211 get_new_blockhash() now retries longer (5s instead of 2s) (#6143) (#6150)
automerge
2019-09-27 11:27:43 -07:00
mergify[bot]
b8f680d1e7 doc: update validator-info publish arguments (bp #6146) (#6153)
* doc: update validator-info publish arguments (#6146)

(cherry picked from commit bf199a2ebc)

# Conflicts:
#	book/src/running-validator/validator-info.md

* Update validator-info.md
2019-09-27 11:25:57 -07:00
Michael Vines
2d4a081c3c Create vote account with 1 lamport instead of 1 SOL 2019-09-27 08:14:35 -07:00
mergify[bot]
be5f879e42 Avoid storing epoch 0 credits if no credits where earned in epoch 0 (#6132) (#6138)
automerge
2019-09-26 21:27:29 -07:00
mergify[bot]
8b353da83c Prevent subtract overflow panic when slot < MAX_LOCKOUT_HISTORY (#6135) (#6136)
automerge
2019-09-26 19:08:20 -07:00
Tyera Eulberg
61930c0dd3 Cherry-pick vote and stake authority changes (#6127)
* add authorized parameters to vote api (#6072)

* add authorized parameters to vote api

* code review

* add authorities to stake init (#6104)

* add authorities to stake init

* fixups

* code review
2019-09-26 17:18:31 -06:00
Michael Vines
232d2b3899 Remove CUDA feature (#6094) (#6126)
automerge
2019-09-26 14:38:30 -07:00
mergify[bot]
0f3a8314ae Enable SOL or lamports for create-vote-account, show-{stake,vote}-account commands (#6114) (#6116)
automerge
2019-09-26 11:35:14 -07:00
mergify[bot]
562034efe3 Remove serializing all ForkHashes (#6110) (#6111)
automerge
2019-09-26 02:43:15 -07:00
mergify[bot]
01c98c5e53 Remove some AccountStorage Serialization (#6047) (#6108)
automerge
2019-09-25 19:14:08 -07:00
mergify[bot]
a4128e886a Fix Bench-tps being too strict (#6105) (#6107)
automerge
2019-09-25 18:50:19 -07:00
mergify[bot]
2589816245 Change formula used in erasure statistics graph (#6102) (#6103)
automerge
2019-09-25 15:24:48 -07:00
mergify[bot]
0245e4cf94 Move status cache serialization to the Snapshot Packager service (#6081) (#6101)
automerge
2019-09-25 14:40:18 -07:00
Dan Albert
2293ad20d5 GitBook: [v0.19] 10 pages and 59 assets modified 2019-09-25 20:58:38 +00:00
mergify[bot]
c0195a2121 Prune fork_hashes with dead forks (#6085) (#6095)
automerge
2019-09-25 12:06:47 -07:00
mergify[bot]
d203adc3d9 Remove brace expansion in arg list (#6091) (#6092)
automerge
2019-09-25 10:28:19 -07:00
Dan Albert
0249d53a14 Update cargo files to 0.19.1 (#6093) 2019-09-25 13:12:00 -04:00
mergify[bot]
1db0fbc77e Add erasure recovery stats to dashboard (#6079) (#6087)
automerge
2019-09-24 22:34:12 -07:00
mergify[bot]
82153f934f Fix staker / voter credit redemption (#6074) (#6086)
automerge
2019-09-24 22:05:41 -07:00
mergify[bot]
4f3f662cfe Remove local_cluster tests from stable-perf job, removee other tests from local-cluster job (#6067) (#6084)
automerge
2019-09-24 21:00:10 -07:00
mergify[bot]
1942c47d73 Avoid accidential tx_count mismatches when using an accounts file (#6069) (#6082)
automerge
2019-09-24 20:37:22 -07:00
Tyera Eulberg
a90281ad5c Reduce poll sleep (#6068) (#6073)
* Reduce sleep in poll_for_signature_confirmations

* Unignore test_repairman_catchup
2019-09-24 20:01:04 -06:00
mergify[bot]
6360bac5e8 Fix using temp file for archive (#6058) (#6071)
* Fix using temp file for archive

* Rename the temp archive instead of hardlinking it

(cherry picked from commit 374b776a3e)
2019-09-24 18:29:11 -07:00
mergify[bot]
c54b23b9d5 Additional tests for should_retransmit_and_persist (#6062) (#6070)
automerge
2019-09-24 17:48:39 -07:00
mergify[bot]
04aaa714e6 Revert back to reqwest, using rustls feature (bp #6041) (#6064)
automerge
2019-09-24 15:35:07 -07:00
Michael Vines
2cc0ab2c5f Tweak Bank Slot Distance graph 2019-09-24 14:52:50 -07:00
mergify[bot]
aac0d7f2f5 Window service is filtering out coding shreds (#6052) (#6059)
automerge
2019-09-24 13:48:13 -07:00
mergify[bot]
9fd29b0575 Fix vote metrics (#6038) (#6040)
automerge
2019-09-24 13:20:27 -07:00
mergify[bot]
6abe3c2804 Fix race between observing tick height being set to last tick and blockhash being observed on a bank (#6013) (#6056)
automerge
2019-09-24 12:20:46 -07:00
mergify[bot]
01ce5beb19 Support primordial accounts with no data (#6053) (#6055)
automerge
2019-09-24 11:52:57 -07:00
mergify[bot]
2bd72318f6 Remove dead code from cluster_info (#6051) (#6054)
automerge
2019-09-24 11:29:52 -07:00
carllin
68a9224604 Fix race between observing tick height being set to last tick and blockhash being observed on a bank (#6013) 2019-09-24 11:16:24 -07:00
Rob Walker
181cad233c rename balance (#5984) 2019-09-24 11:15:15 -07:00
Justin Starry
0b66529f11 Fix BPF program static linking (#5992) (#6049)
automerge
2019-09-24 09:40:33 -07:00
Michael Vines
e20e4180a9 Flip order of arg to ensure -t sticks 2019-09-23 22:21:12 -07:00
mergify[bot]
de4309a905 Avoid hardlinking as that confuses tar (#6042) (#6045)
(cherry picked from commit 7fa809c16d)
2019-09-23 21:40:09 -07:00
mergify[bot]
d5248c936f Skip considering banks older than the latest vote slot (#6037) (#6043)
automerge
2019-09-23 20:44:47 -07:00
mergify[bot]
40bc37266d Don't recover coding shreds (#6034) (#6039)
automerge
2019-09-23 18:35:50 -07:00
mergify[bot]
1819f263f1 ' => " (#6035) (#6036)
automerge
2019-09-23 17:31:05 -07:00
Pankaj Garg
24a055e490 Upgrade to ReedSolomon 4.0 release (#6026) (#6030)
automerge
2019-09-23 15:29:52 -07:00
mergify[bot]
dfbd77f18d Fix really old banks triggering log spam (#6025) (#6029)
automerge
2019-09-23 15:27:38 -07:00
mergify[bot]
7c5557d69b Dump tar stdout/err on failure for better debug (#6024) (#6027)
automerge
2019-09-23 13:57:39 -07:00
mergify[bot]
d180eedd17 Remove the _/deps symlink, just copy instead (#6020) (#6021)
automerge
2019-09-23 09:50:07 -07:00
Michael Vines
3b274ca8db GitBook: [v0.19] 79 pages and 12 assets modified 2019-09-23 03:38:36 +00:00
565 changed files with 27746 additions and 25103 deletions

View File

@@ -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=]"
}
}

View File

@@ -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

2111
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ members = [
"gossip",
"install",
"keygen",
"ledger",
"kvstore",
"ledger-tool",
"local_cluster",
"logger",
@@ -43,11 +43,11 @@ members = [
"programs/stake_tests",
"programs/storage_api",
"programs/storage_program",
"programs/vest_api",
"programs/vest_program",
"programs/token_api",
"programs/token_program",
"programs/vote_api",
"programs/vote_program",
"archiver",
"replicator",
"runtime",
"sdk",
"sdk-c",

View File

@@ -78,7 +78,7 @@ $ source $HOME/.cargo/env
$ rustup component add rustfmt
```
If your rustc version is lower than 1.38.0, please update it:
If your rustc version is lower than 1.37.0, please update it:
```bash
$ rustup update

View File

@@ -138,7 +138,7 @@ 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.
#### Publish updated Book
We maintain three copies of the "book" as official documentation:

View File

@@ -1,18 +0,0 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-archiver"
version = "0.20.1"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
console = "0.9.0"
solana-core = { path = "../core", version = "0.20.1" }
solana-logger = { path = "../logger", version = "0.20.1" }
solana-metrics = { path = "../metrics", version = "0.20.1" }
solana-netutil = { path = "../netutil", version = "0.20.1" }
solana-sdk = { path = "../sdk", version = "0.20.1" }

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-banking-bench"
version = "0.20.1"
version = "0.19.2"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -10,11 +10,10 @@ homepage = "https://solana.com/"
[dependencies]
log = "0.4.6"
rayon = "1.2.0"
solana-core = { path = "../core", version = "0.20.1" }
solana-ledger = { path = "../ledger", version = "0.20.1" }
solana-logger = { path = "../logger", version = "0.20.1" }
solana-runtime = { path = "../runtime", version = "0.20.1" }
solana-measure = { path = "../measure", version = "0.20.1" }
solana-sdk = { path = "../sdk", version = "0.20.1" }
solana-core = { path = "../core", version = "0.19.2" }
solana-logger = { path = "../logger", version = "0.19.2" }
solana-runtime = { path = "../runtime", version = "0.19.2" }
solana-measure = { path = "../measure", version = "0.19.2" }
solana-sdk = { path = "../sdk", version = "0.19.2" }
rand = "0.6.5"
crossbeam-channel = "0.3"

View File

@@ -1,12 +1,14 @@
#[macro_use]
extern crate solana_ledger;
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_block, GenesisBlockInfo};
@@ -14,8 +16,6 @@ use solana_core::packet::to_packets_chunked;
use solana_core::poh_recorder::PohRecorder;
use solana_core::poh_recorder::WorkingBankEntry;
use solana_core::service::Service;
use solana_ledger::bank_forks::BankForks;
use solana_ledger::blocktree::{get_tmp_ledger_path, Blocktree};
use solana_measure::measure::Measure;
use solana_runtime::bank::Bank;
use solana_sdk::hash::Hash;
@@ -41,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();
}
@@ -155,10 +155,10 @@ fn main() {
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, &blocktree, 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,
@@ -309,11 +309,8 @@ fn main() {
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");

View File

@@ -2,38 +2,38 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-exchange"
version = "0.20.1"
version = "0.19.2"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
publish = false
[dependencies]
bincode = "1.2.0"
bincode = "1.1.4"
bs58 = "0.3.0"
clap = "2.32.0"
env_logger = "0.7.1"
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.101"
serde_derive = "1.0.101"
serde_json = "1.0.41"
serde_yaml = "0.8.11"
serde_json = "1.0.40"
serde_yaml = "0.8.9"
# solana-runtime = { path = "../solana/runtime"}
solana-core = { path = "../core", version = "0.20.1" }
solana-genesis = { path = "../genesis", version = "0.20.1" }
solana-client = { path = "../client", version = "0.20.1" }
solana-drone = { path = "../drone", version = "0.20.1" }
solana-exchange-api = { path = "../programs/exchange_api", version = "0.20.1" }
solana-exchange-program = { path = "../programs/exchange_program", version = "0.20.1" }
solana-logger = { path = "../logger", version = "0.20.1" }
solana-metrics = { path = "../metrics", version = "0.20.1" }
solana-netutil = { path = "../netutil", version = "0.20.1" }
solana-runtime = { path = "../runtime", version = "0.20.1" }
solana-sdk = { path = "../sdk", version = "0.20.1" }
solana-core = { path = "../core", version = "0.19.2" }
solana-genesis = { path = "../genesis", version = "0.19.2" }
solana-client = { path = "../client", version = "0.19.2" }
solana-drone = { path = "../drone", version = "0.19.2" }
solana-exchange-api = { path = "../programs/exchange_api", version = "0.19.2" }
solana-exchange-program = { path = "../programs/exchange_program", version = "0.19.2" }
solana-logger = { path = "../logger", version = "0.19.2" }
solana-metrics = { path = "../metrics", version = "0.19.2" }
solana-netutil = { path = "../netutil", version = "0.19.2" }
solana-runtime = { path = "../runtime", version = "0.19.2" }
solana-sdk = { path = "../sdk", version = "0.19.2" }
untrusted = "0.7.0"
ws = "0.9.1"
ws = "0.9.0"

View File

@@ -11,7 +11,7 @@ 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::Base64Account;
use solana_genesis::PrimordialAccountDetails;
use solana_metrics::datapoint_info;
use solana_sdk::client::Client;
use solana_sdk::client::SyncClient;
@@ -89,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(),
@@ -140,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, _)| {
@@ -633,7 +634,7 @@ 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,
{
@@ -657,13 +658,11 @@ 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(a).unwrap_or(0) >= amount {
return true;
}
}
}
false
}
@@ -832,11 +831,11 @@ pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], acco
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
@@ -849,7 +848,7 @@ pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], acco
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",

View File

@@ -1,7 +1,7 @@
use clap::{crate_description, crate_name, crate_version, value_t, App, Arg, ArgMatches};
use solana_core::gen_keys::GenKeys;
use solana_drone::drone::DRONE_PORT;
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use std::net::SocketAddr;
use std::process::exit;
use std::time::Duration;
@@ -179,7 +179,7 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
});
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 = {

View File

@@ -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");
});

View File

@@ -2,13 +2,13 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-streamer"
version = "0.20.1"
version = "0.19.2"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
solana-core = { path = "../core", version = "0.20.1" }
solana-logger = { path = "../logger", version = "0.20.1" }
solana-netutil = { path = "../netutil", version = "0.20.1" }
solana-core = { path = "../core", version = "0.19.2" }
solana-logger = { path = "../logger", version = "0.19.2" }
solana-netutil = { path = "../netutil", version = "0.19.2" }

View File

@@ -2,37 +2,34 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-tps"
version = "0.20.1"
version = "0.19.2"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
bincode = "1.2.0"
bincode = "1.1.4"
clap = "2.33.0"
log = "0.4.8"
rayon = "1.2.0"
serde = "1.0.101"
serde_derive = "1.0.101"
serde_json = "1.0.41"
serde_yaml = "0.8.11"
solana-core = { path = "../core", version = "0.20.1" }
solana-genesis = { path = "../genesis", version = "0.20.1" }
solana-client = { path = "../client", version = "0.20.1" }
solana-drone = { path = "../drone", version = "0.20.1" }
solana-librapay-api = { path = "../programs/librapay_api", version = "0.20.1", optional = true }
solana-logger = { path = "../logger", version = "0.20.1" }
solana-metrics = { path = "../metrics", version = "0.20.1" }
solana-measure = { path = "../measure", version = "0.20.1" }
solana-netutil = { path = "../netutil", version = "0.20.1" }
solana-runtime = { path = "../runtime", version = "0.20.1" }
solana-sdk = { path = "../sdk", version = "0.20.1" }
solana-move-loader-program = { path = "../programs/move_loader_program", version = "0.20.1", optional = true }
solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.20.1", optional = true }
serde_json = "1.0.40"
serde_yaml = "0.8.9"
solana-core = { path = "../core", version = "0.19.2" }
solana-genesis = { path = "../genesis", version = "0.19.2" }
solana-client = { path = "../client", version = "0.19.2" }
solana-drone = { path = "../drone", version = "0.19.2" }
solana-librapay-api = { path = "../programs/librapay_api", version = "0.19.2" }
solana-logger = { path = "../logger", version = "0.19.2" }
solana-metrics = { path = "../metrics", version = "0.19.2" }
solana-measure = { path = "../measure", version = "0.19.2" }
solana-netutil = { path = "../netutil", version = "0.19.2" }
solana-runtime = { path = "../runtime", version = "0.19.2" }
solana-sdk = { path = "../sdk", version = "0.19.2" }
solana-move-loader-program = { path = "../programs/move_loader_program", version = "0.19.2" }
solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.19.2" }
[dev-dependencies]
serial_test = "0.2.0"
serial_test_derive = "0.2.0"
[features]
move = ["solana-core/move", "solana-librapay-api", "solana-move-loader-program", "solana-move-loader-api"]

View File

@@ -1,15 +1,15 @@
use solana_metrics;
use crate::cli::Config;
use bincode;
use log::*;
use rayon::prelude::*;
use solana_client::perf_utils::{sample_txs, SampleStats};
use solana_core::gen_keys::GenKeys;
use solana_drone::drone::request_airdrop_transaction;
#[cfg(feature = "move")]
use solana_librapay_api::{create_genesis, upload_mint_program, upload_payment_program};
use solana_measure::measure::Measure;
use solana_metrics::datapoint_debug;
use solana_metrics::datapoint_info;
use solana_sdk::{
client::Client,
clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE},
@@ -35,9 +35,8 @@ use std::{
// The point at which transactions become "too old", in seconds.
const MAX_TX_QUEUE_AGE: u64 =
MAX_PROCESSING_AGE as u64 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND;
MAX_PROCESSING_AGE as u64 * DEFAULT_TICKS_PER_SECOND / DEFAULT_TICKS_PER_SLOT;
#[cfg(feature = "move")]
use solana_librapay_api::librapay_transaction;
pub const MAX_SPENDS_PER_TX: u64 = 4;
@@ -173,10 +172,6 @@ where
sleep(Duration::from_millis(100));
continue;
}
info!(
"Took {} ms for new blockhash",
duration_as_ms(&blockhash_time.elapsed())
);
blockhash_time = Instant::now();
let balance = client.get_balance(&id.pubkey()).unwrap_or(0);
metrics_submit_lamport_balance(balance);
@@ -238,13 +233,12 @@ where
fn metrics_submit_lamport_balance(lamport_balance: u64) {
info!("Token balance: {}", lamport_balance);
datapoint_debug!(
datapoint_info!(
"bench-tps-lamport_balance",
("balance", lamport_balance, i64)
);
}
#[cfg(feature = "move")]
fn generate_move_txs(
source: &[Keypair],
dest: &[Keypair],
@@ -306,7 +300,7 @@ fn generate_system_txs(
.par_iter()
.map(|(from, to)| {
(
system_transaction::transfer(from, &to.pubkey(), 1, *blockhash),
system_transaction::create_user_account(from, &to.pubkey(), 1, *blockhash),
timestamp(),
)
})
@@ -327,29 +321,21 @@ fn generate_txs(
let signing_start = Instant::now();
let transactions = if let Some((
_libra_genesis_keypair,
_libra_pay_program_id,
libra_genesis_keypair,
libra_pay_program_id,
_libra_mint_program_id,
_libra_keys,
libra_keys,
)) = libra_args
{
#[cfg(not(feature = "move"))]
{
return;
}
#[cfg(feature = "move")]
{
generate_move_txs(
source,
dest,
reclaim,
&_libra_keys,
_libra_pay_program_id,
&_libra_genesis_keypair.pubkey(),
&libra_keys,
libra_pay_program_id,
&libra_genesis_keypair.pubkey(),
blockhash,
)
}
} else {
generate_system_txs(source, dest, reclaim, blockhash)
};
@@ -365,7 +351,7 @@ fn generate_txs(
duration_as_ms(&duration),
blockhash,
);
datapoint_debug!(
datapoint_info!(
"bench-tps-generate_txs",
("duration", duration_as_ms(&duration), i64)
);
@@ -430,7 +416,7 @@ fn do_tx_transfers<T: Client>(
duration_as_ms(&transfer_start.elapsed()),
tx_len as f32 / duration_as_s(&transfer_start.elapsed()),
);
datapoint_debug!(
datapoint_info!(
"bench-tps-do_tx_transfers",
("duration", duration_as_ms(&transfer_start.elapsed()), i64),
("count", tx_len, i64)
@@ -634,22 +620,15 @@ pub fn airdrop_lamports<T: Client>(
let (blockhash, _fee_calculator) = get_recent_blockhash(client);
match request_airdrop_transaction(&drone_addr, &id.pubkey(), airdrop_amount, blockhash) {
Ok(transaction) => {
let mut tries = 0;
loop {
tries += 1;
let signature = client.async_send_transaction(transaction.clone()).unwrap();
let result = client.poll_for_signature_confirmation(&signature, 1);
if result.is_ok() {
break;
}
if tries >= 5 {
let signature = client.async_send_transaction(transaction).unwrap();
client
.poll_for_signature_confirmation(&signature, 1)
.unwrap_or_else(|_| {
panic!(
"Error requesting airdrop: to addr: {:?} amount: {} {:?}",
drone_addr, airdrop_amount, result
"Error requesting airdrop: to addr: {:?} amount: {}",
drone_addr, airdrop_amount
)
}
}
})
}
Err(err) => {
panic!(
@@ -769,7 +748,6 @@ pub fn generate_keypairs(seed_keypair: &Keypair, count: u64) -> (Vec<Keypair>, u
(rnd.gen_n_keypairs(total_keys), extra)
}
#[cfg(feature = "move")]
fn fund_move_keys<T: Client>(
client: &T,
funding_key: &Keypair,
@@ -943,12 +921,8 @@ pub fn generate_and_fund_keypairs<T: Client>(
.get_balance(&keypairs[tx_count * 2 - 1].pubkey())
.unwrap_or(0);
#[cfg(feature = "move")]
let mut move_keypairs_ret = None;
#[cfg(not(feature = "move"))]
let move_keypairs_ret = None;
if lamports_per_account > last_keypair_balance {
let (_blockhash, fee_calculator) = get_recent_blockhash(client);
let account_desired_balance =
@@ -968,8 +942,6 @@ pub fn generate_and_fund_keypairs<T: Client>(
airdrop_lamports(client, &drone_addr.unwrap(), funding_key, total)?;
}
#[cfg(feature = "move")]
{
if use_move {
let libra_genesis_keypair = create_genesis(&funding_key, client, 10_000_000);
let libra_mint_program_id = upload_mint_program(&funding_key, client);
@@ -999,7 +971,6 @@ pub fn generate_and_fund_keypairs<T: Client>(
// Give solana keys 1/3 and move keys 1/3 the lamports. Keep 1/3 for fees.
total /= 3;
}
}
fund_keys(
client,
@@ -1078,7 +1049,7 @@ mod tests {
#[test]
fn test_bench_tps_fund_keys_with_fees() {
let (mut genesis_block, id) = create_genesis_block(10_000);
let fee_calculator = FeeCalculator::new(11, 0);
let fee_calculator = FeeCalculator::new(11);
genesis_block.fee_calculator = fee_calculator;
let bank = Bank::new(&genesis_block);
let client = BankClient::new(bank);

View File

@@ -1,10 +1,10 @@
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_file, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use std::{net::SocketAddr, process::exit, time::Duration};
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::SOL_LAMPORTS;
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = 64 * 1024;
/// Holds the configuration for a single run of the benchmark
pub struct Config {
@@ -34,8 +34,8 @@ impl Default for Config {
threads: 4,
num_nodes: 1,
duration: Duration::new(std::u64::MAX, 0),
tx_count: 50_000,
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,
@@ -181,7 +181,7 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
}
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");
}

View File

@@ -2,7 +2,7 @@ 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_multi_client};
use solana_genesis::Base64Account;
use solana_genesis::PrimordialAccountDetails;
use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::system_program;
@@ -37,8 +37,7 @@ fn main() {
info!("Generating {} keypairs", *tx_count * 2);
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;
@@ -46,7 +45,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(),
@@ -64,7 +63,7 @@ fn main() {
}
info!("Connecting to the cluster");
let (nodes, _archivers) =
let (nodes, _replicators) =
discover_cluster(&entrypoint_addr, *num_nodes).unwrap_or_else(|err| {
eprintln!("Failed to discover {} nodes: {:?}", num_nodes, err);
exit(1);
@@ -85,7 +84,8 @@ fn main() {
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;

15
book/art/consensus.msc Normal file
View 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" ] ;
}

View File

@@ -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/api-reference/cli.md}
cat src/api-reference/.cli.md > "$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"

View File

@@ -5,8 +5,6 @@ cd "$(dirname "$0")"
make -j"$(nproc)" -B svg
#TODO figure out why book wants to change, but local and CI differ
exit 0
if [[ -n $CI ]]; then
# In CI confirm that no svgs need to be built
git diff --exit-code

View File

@@ -1,39 +1,40 @@
<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"/>
<polygon class="fg_fill" 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"/>
<polygon class="bg_fill" points="2,2 2,12 18,7 2,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"/>
<circle class="fg_fill" cx="10" cy="10" 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"/>
<rect class="fg_fill" 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"/>
<circle class="bg_fill" cx="10" cy="10" r="4"/>
</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"/>
<circle class="bg_fill" cx="20" cy="20" r="6"/>
</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;
rect.backdrop {
fill: white;
}
line.dashed {
stroke-dasharray: 5;
}
circle.solid {
text{
fill: black;
}
circle {
fill: none;
stroke: black;
stroke-width: 2;
}
line {
stroke: black;
stroke-width: 2;
stroke-opacity: 1;
@@ -41,7 +42,8 @@
stroke-linecap: round;
stroke-linejoin: miter;
}
circle.open {
path {
fill: none;
stroke: black;
stroke-width: 2;
@@ -50,13 +52,29 @@
stroke-linecap: round;
stroke-linejoin: miter;
}
line.dashed {
stroke-dasharray: 5;
}
.fg_fill {
fill: black;
}
.bg_fill {
fill: white;
stroke: black;
stroke-width: 2;
}
tspan.head{
fill: none;
stroke: none;
}
</style>
<rect fill="white" height="144" width="48" x="0" y="0"/>
<rect class="backdrop" 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"/>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,39 +1,40 @@
<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"/>
<polygon class="fg_fill" 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"/>
<polygon class="bg_fill" points="2,2 2,12 18,7 2,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"/>
<circle class="fg_fill" cx="10" cy="10" 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"/>
<rect class="fg_fill" 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"/>
<circle class="bg_fill" cx="10" cy="10" r="4"/>
</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"/>
<circle class="bg_fill" cx="20" cy="20" r="6"/>
</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;
rect.backdrop {
fill: white;
}
line.dashed {
stroke-dasharray: 5;
}
circle.solid {
text{
fill: black;
}
circle {
fill: none;
stroke: black;
stroke-width: 2;
}
line {
stroke: black;
stroke-width: 2;
stroke-opacity: 1;
@@ -41,7 +42,8 @@
stroke-linecap: round;
stroke-linejoin: miter;
}
circle.open {
path {
fill: none;
stroke: black;
stroke-width: 2;
@@ -50,13 +52,29 @@
stroke-linecap: round;
stroke-linejoin: miter;
}
line.dashed {
stroke-dasharray: 5;
}
.fg_fill {
fill: black;
}
.bg_fill {
fill: white;
stroke: black;
stroke-width: 2;
}
tspan.head{
fill: none;
stroke: none;
}
</style>
<rect fill="white" height="208" width="96" x="0" y="0"/>
<rect class="backdrop" 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"/>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,39 +1,40 @@
<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"/>
<polygon class="fg_fill" 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"/>
<polygon class="bg_fill" points="2,2 2,12 18,7 2,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"/>
<circle class="fg_fill" cx="10" cy="10" 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"/>
<rect class="fg_fill" 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"/>
<circle class="bg_fill" cx="10" cy="10" r="4"/>
</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"/>
<circle class="bg_fill" cx="20" cy="20" r="6"/>
</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;
rect.backdrop {
fill: white;
}
line.dashed {
stroke-dasharray: 5;
}
circle.solid {
text{
fill: black;
}
circle {
fill: none;
stroke: black;
stroke-width: 2;
}
line {
stroke: black;
stroke-width: 2;
stroke-opacity: 1;
@@ -41,7 +42,8 @@
stroke-linecap: round;
stroke-linejoin: miter;
}
circle.open {
path {
fill: none;
stroke: black;
stroke-width: 2;
@@ -50,25 +52,41 @@
stroke-linecap: round;
stroke-linejoin: miter;
}
line.dashed {
stroke-dasharray: 5;
}
.fg_fill {
fill: black;
}
.bg_fill {
fill: white;
stroke: black;
stroke-width: 2;
}
tspan.head{
fill: none;
stroke: none;
}
</style>
<rect fill="white" height="304" width="696" x="0" y="0"/>
<rect class="backdrop" 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"/>
<path d="M 12 164 A 4 4 0 0 0 16 168"/>
<path d="M 16 136 A 4 4 0 0 0 12 140"/>
</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"/>
<path d="M 92 140 A 4 4 0 0 0 88 136"/>
</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"/>
<path d="M 88 168 A 4 4 0 0 0 92 164"/>
</g>
<g>
<line x1="92" x2="92" y1="140" y2="164"/>
@@ -78,35 +96,35 @@
</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"/>
<path d="M 112 88 A 4 4 0 0 0 108 92"/>
</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"/>
<path d="M 108 212 A 4 4 0 0 0 112 216"/>
</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"/>
<path d="M 564 92 A 4 4 0 0 0 560 88"/>
</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"/>
<path d="M 560 216 A 4 4 0 0 0 564 212"/>
</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"/>
<path d="M 132 180 A 4 4 0 0 0 136 184"/>
<path d="M 136 120 A 4 4 0 0 0 132 124"/>
</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"/>
<path d="M 196 124 A 4 4 0 0 0 192 120"/>
</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"/>
<path d="M 192 184 A 4 4 0 0 0 196 180"/>
</g>
<g>
<line x1="196" x2="196" y1="124" y2="180"/>
@@ -116,16 +134,16 @@
</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"/>
<path d="M 220 180 A 4 4 0 0 0 224 184"/>
<path d="M 224 120 A 4 4 0 0 0 220 124"/>
</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"/>
<path d="M 316 124 A 4 4 0 0 0 312 120"/>
</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"/>
<path d="M 312 184 A 4 4 0 0 0 316 180"/>
</g>
<g>
<line x1="316" x2="316" y1="124" y2="180"/>
@@ -135,51 +153,51 @@
</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"/>
<path d="M 324 52 A 4 4 0 0 0 328 56"/>
<path d="M 328 24 A 4 4 0 0 0 324 28"/>
</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"/>
<path d="M 436 28 A 4 4 0 0 0 432 24"/>
</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"/>
<path d="M 432 56 A 4 4 0 0 0 436 52"/>
</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"/>
<path d="M 340 180 A 4 4 0 0 0 344 184"/>
<path d="M 344 120 A 4 4 0 0 0 340 124"/>
</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"/>
<path d="M 420 124 A 4 4 0 0 0 416 120"/>
</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"/>
<path d="M 416 184 A 4 4 0 0 0 420 180"/>
</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"/>
<path d="M 356 292 A 4 4 0 0 0 360 296"/>
<path d="M 360 264 A 4 4 0 0 0 356 268"/>
</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"/>
<path d="M 412 268 A 4 4 0 0 0 408 264"/>
</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"/>
<path d="M 408 296 A 4 4 0 0 0 412 292"/>
</g>
<g>
<line x1="412" x2="412" y1="268" y2="292"/>
@@ -195,16 +213,16 @@
</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"/>
<path d="M 444 180 A 4 4 0 0 0 448 184"/>
<path d="M 448 120 A 4 4 0 0 0 444 124"/>
</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"/>
<path d="M 540 124 A 4 4 0 0 0 536 120"/>
</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"/>
<path d="M 536 184 A 4 4 0 0 0 540 180"/>
</g>
<g>
<line x1="540" x2="540" y1="124" y2="180"/>
@@ -220,16 +238,16 @@
</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"/>
<path d="M 588 180 A 4 4 0 0 0 592 184"/>
<path d="M 592 120 A 4 4 0 0 0 588 124"/>
</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"/>
<path d="M 692 124 A 4 4 0 0 0 688 120"/>
</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"/>
<path d="M 688 184 A 4 4 0 0 0 692 180"/>
</g>
<g>
<line x1="692" x2="692" y1="124" y2="180"/>

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -35,7 +35,7 @@
* [Publishing Validator Info](running-validator/validator-info.md)
* [Troubleshooting](running-validator/validator-troubleshoot.md)
* [FAQ](running-validator/validator-faq.md)
* [Running an Archiver](running-archiver.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)
@@ -47,34 +47,31 @@
* [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)
* [Rent](proposals/rent.md)
* [Inter-chain Transaction Verification](proposals/interchain-transaction-verification.md)
* [Snapshot Verification](proposals/snapshot-verification.md)
* [Bankless Leader](proposals/bankless-leader.md)
* [Implemented Design Proposals](implemented-proposals/README.md)
* [Blocktree](implemented-proposals/blocktree.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)
* [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)
* [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)

View 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.

View File

@@ -1,177 +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"
// Command
$ solana airdrop 123 --lamports
// Return
"123 lamports"
```
### 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

View File

@@ -1,6 +1,6 @@
# Blockstreamer
Solana supports a node type called an _blockstreamer_. This validator variation is intended for applications that need to observe the data plane without participating in transaction validation or ledger replication.
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.

View File

@@ -14,20 +14,14 @@ $ solana address
<PUBKEY>
```
### Airdrop SOL/Lamports
### Airdrop Lamports
```bash
// Command
$ solana airdrop 2
$ solana airdrop 123
// Return
"2.00000000 SOL"
// Command
$ solana airdrop 123 --lamports
// Return
"123 lamports"
"Your balance is: 123"
```
### Get Balance
@@ -37,7 +31,7 @@ $ solana airdrop 123 --lamports
$ solana balance
// Return
"3.00050001 SOL"
"Your balance is: 123"
```
### Confirm Transaction
@@ -81,7 +75,7 @@ $ solana pay <PUBKEY> 123 \
{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_
_`require-timestamp-from` is optional. If not provided, the transaction will expect a timestamp signed by this wallet's secret key_
### Authorized Transfer
@@ -175,503 +169,154 @@ $ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
```
## Usage
### solana-cli
```text
solana-cli 0.20.1
Blockchain, Rebuilt for Scale
solana 0.12.0
USAGE:
solana [OPTIONS] <SUBCOMMAND>
solana [FLAGS] [OPTIONS] [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
--rpc-tls Enable TLS for the RPC endpoint
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
--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 lamports
airdrop Request a batch of lamports
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-archiver-storage-account Create an archiver storage account
create-stake-account Create a stake 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 cli config settings
get-epoch-info Get information about the current epoch
get-genesis-blockhash Get the genesis blockhash
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 cli 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-validators Show information about the current validators
show-vote-account Show the contents of a vote account
stake-authorize-staker Authorize a new stake signing keypair for the given stake account
stake-authorize-withdrawer Authorize a new withdraw signing keypair for the given stake account
uptime Show the uptime of a validator, based on epoch voting history
validator-info Publish/get Validator info on Solana
vote-authorize-voter Authorize a new vote signing keypair for the given vote account
vote-authorize-withdrawer Authorize a new withdraw signing keypair for the given vote account
withdraw-stake Withdraw the unstaked lamports from the stake account
```
#### solana-address
```text
solana-address
Get your public key
USAGE:
solana address [OPTIONS]
solana address
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-airdrop
```text
solana-airdrop
Request lamports
Request a batch of lamports
USAGE:
solana airdrop [OPTIONS] <AMOUNT> [UNIT]
solana airdrop <NUM>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/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]
<NUM> The number of lamports to request
```
#### solana-balance
```text
solana-balance
Get your balance
USAGE:
solana balance [FLAGS] [OPTIONS] [PUBKEY]
solana balance
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/cli/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
```
#### solana-cancel
```text
solana-cancel
Cancel a transfer
USAGE:
solana cancel [OPTIONS] <PROCESS ID>
solana cancel <PROCESS_ID>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/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
<PROCESS_ID> The process id of the transfer to cancel
```
#### solana-claim-storage-reward
```text
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/cli/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
```
#### solana-cluster-version
```text
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/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-confirm
```text
solana-confirm
Confirm transaction by signature
USAGE:
solana confirm [OPTIONS] <SIGNATURE>
solana confirm <SIGNATURE>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/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
```
#### solana-create-archiver-storage-account
```text
solana-create-archiver-storage-account
Create an archiver storage account
USAGE:
solana create-archiver-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/cli/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>
```
#### solana-create-stake-account
```text
solana-create-stake-account
Create a stake account
USAGE:
solana create-stake-account [OPTIONS] <STAKE ACCOUNT> <AMOUNT> [UNIT]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
--authorized-staker <PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
--authorized-withdrawer <PUBKEY> Public key of the authorized withdrawer (defaults to cli config pubkey)
-C, --config <PATH> Configuration file to use [default:
~/.config/solana/cli/config.yml]
--custodian <PUBKEY> Identity of the custodian (can withdraw before lockup expires)
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
--lockup <SLOT> The slot height at which this account will be available for withdrawal
ARGS:
<STAKE ACCOUNT> Address of the stake account to fund (pubkey or keypair)
<AMOUNT> The amount of send to the vote account (default unit SOL)
<UNIT> Specify unit to use for request [possible values: SOL, lamports]
```
#### solana-create-validator-storage-account
```text
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/cli/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>
```
#### solana-create-vote-account
```text
solana-create-vote-account
Create a vote account
USAGE:
solana create-vote-account [OPTIONS] <VOTE ACCOUNT PUBKEY> <VALIDATOR PUBKEY>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
--authorized-voter <PUBKEY> Public key of the authorized voter (defaults to vote account)
--authorized-withdrawer <PUBKEY> Public key of the authorized withdrawer (defaults to cli config pubkey)
--commission <NUM> The commission taken on reward redemption (0-255), default: 0
-C, --config <PATH> Configuration file to use [default:
~/.config/solana/cli/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
```
#### solana-deactivate-stake
```text
solana-deactivate-stake
Deactivate the delegated stake from the stake account
USAGE:
solana deactivate-stake [OPTIONS] <STAKE ACCOUNT>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<STAKE ACCOUNT> Stake account to be deactivated.
```
#### solana-delegate-stake
```text
solana-delegate-stake
Delegate stake to a vote account
USAGE:
solana delegate-stake [OPTIONS] <STAKE ACCOUNT> <VOTE ACCOUNT>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<STAKE ACCOUNT> Stake account to delegate
<VOTE ACCOUNT> The vote account to which the stake will be delegated
```
#### solana-deploy
```text
solana-deploy
Deploy a program
USAGE:
solana deploy [OPTIONS] <PATH TO PROGRAM>
solana deploy <PATH>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/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
<PATH> /path/to/program.o
```
#### solana-fees
```text
solana-fees
Display current cluster fees
USAGE:
solana fees [OPTIONS]
solana fees
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-get
```text
solana-get
Get cli 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/cli/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]
```
#### solana-get-epoch-info
```text
solana-get-epoch-info
Get information about the current epoch
USAGE:
solana get-epoch-info [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-get-genesis-blockhash
```text
solana-get-genesis-blockhash
Get the genesis blockhash
USAGE:
solana get-genesis-blockhash [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-get-slot
```text
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/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-get-transaction-count
```text
solana-get-transaction-count
Get current transaction count
USAGE:
solana get-transaction-count [OPTIONS]
solana get-transaction-count
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-help
```text
solana-help
Prints this message or the help of the given subcommand(s)
USAGE:
solana help [subcommand]...
ARGS:
<subcommand>... The subcommand whose help message to display
```
#### solana-pay
```text
solana-pay
Send a payment
USAGE:
solana pay [FLAGS] [OPTIONS] <PUBKEY> <AMOUNT> [--] [UNIT]
solana pay [FLAGS] [OPTIONS] <PUBKEY> <NUM>
FLAGS:
--cancelable
@@ -679,388 +324,47 @@ FLAGS:
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default:
~/.config/solana/cli/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 pubkey of recipient
<AMOUNT> The amount to send (default unit SOL)
<UNIT> Specify unit to use for request [possible values: SOL, lamports]
<NUM> The number of lamports to send
```
#### solana-ping
```text
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/cli/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]
```
#### solana-redeem-vote-credits
```text
solana-redeem-vote-credits
Redeem credits in the stake account
USAGE:
solana redeem-vote-credits [OPTIONS] <STAKE ACCOUNT> <VOTE ACCOUNT>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<STAKE ACCOUNT> Address of the stake account in which to redeem credits
<VOTE ACCOUNT> The vote account to which the stake is currently delegated.
```
#### solana-send-signature
```text
solana-send-signature
Send a signature to authorize a transfer
USAGE:
solana send-signature [OPTIONS] <PUBKEY> <PROCESS ID>
solana send-signature <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/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<PUBKEY> The pubkey of recipient
<PROCESS ID> The process id of the transfer to authorize
<PROCESS_ID> The process id of the transfer to authorize
```
#### solana-send-timestamp
```text
solana-send-timestamp
Send a timestamp to unlock a transfer
USAGE:
solana send-timestamp [OPTIONS] <PUBKEY> <PROCESS ID>
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/cli/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 pubkey of recipient
<PROCESS ID> The process id of the transfer to unlock
```
#### solana-set
```text
solana-set
Set a cli 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/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-show-account
```text
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/cli/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 pubkey
```
#### solana-show-stake-account
```text
solana-show-stake-account
Show the contents of a stake account
USAGE:
solana show-stake-account [FLAGS] [OPTIONS] <STAKE ACCOUNT>
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/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<STAKE ACCOUNT> Address of the stake account to display
```
#### solana-show-storage-account
```text
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/cli/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 pubkey
```
#### solana-show-validators
```text
solana-show-validators
Show information about the current validators
USAGE:
solana show-validators [FLAGS] [OPTIONS]
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/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
```
#### solana-show-vote-account
```text
solana-show-vote-account
Show the contents of a vote account
USAGE:
solana show-vote-account [FLAGS] [OPTIONS] <VOTE 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/cli/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 pubkey
```
#### solana-stake-authorize-staker
```text
solana-stake-authorize-staker
Authorize a new stake signing keypair for the given stake account
USAGE:
solana stake-authorize-staker [OPTIONS] <STAKE ACCOUNT> <AUTHORIZE PUBKEY>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<STAKE ACCOUNT> Stake account in which to set the authorized staker
<AUTHORIZE PUBKEY> New authorized staker
```
#### solana-stake-authorize-withdrawer
```text
solana-stake-authorize-withdrawer
Authorize a new withdraw signing keypair for the given stake account
USAGE:
solana stake-authorize-withdrawer [OPTIONS] <STAKE ACCOUNT> <AUTHORIZE PUBKEY>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<STAKE ACCOUNT> Stake account in which to set the authorized withdrawer
<AUTHORIZE PUBKEY> New authorized withdrawer
```
#### solana-uptime
```text
solana-uptime
Show the uptime of a validator, based on epoch voting history
USAGE:
solana uptime [FLAGS] [OPTIONS] <VOTE ACCOUNT PUBKEY>
FLAGS:
--aggregate Aggregate uptime data across span
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
--span <NUM OF EPOCHS> Number of recent epochs to examine
ARGS:
<VOTE ACCOUNT PUBKEY> Vote account pubkey
```
#### solana-validator-info
```text
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/cli/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
```
#### solana-vote-authorize-voter
```text
solana-vote-authorize-voter
Authorize a new vote signing keypair for the given vote account
USAGE:
solana vote-authorize-voter [OPTIONS] <VOTE ACCOUNT PUBKEY> <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/cli/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
<NEW VOTER PUBKEY> New vote signer to authorize
```
#### solana-vote-authorize-withdrawer
```text
solana-vote-authorize-withdrawer
Authorize a new withdraw signing keypair for the given vote account
USAGE:
solana vote-authorize-withdrawer [OPTIONS] <VOTE ACCOUNT PUBKEY> <NEW WITHDRAWER PUBKEY>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/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 withdrawer
<NEW WITHDRAWER PUBKEY> New withdrawer to authorize
```
#### solana-withdraw-stake
```text
solana-withdraw-stake
Withdraw the unstaked lamports from the stake account
USAGE:
solana withdraw-stake [OPTIONS] <STAKE ACCOUNT> <DESTINATION ACCOUNT> <AMOUNT> [UNIT]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-C, --config <PATH> Configuration file to use [default: ~/.config/solana/cli/config.yml]
-u, --url <URL> JSON RPC URL for the solana cluster
-k, --keypair <PATH> /path/to/id.json
ARGS:
<STAKE ACCOUNT> Stake account from which to withdraw
<DESTINATION ACCOUNT> The account to which 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]
<PROCESS_ID> The process id of the transfer to unlock
```

View File

@@ -17,14 +17,10 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [confirmTransaction](jsonrpc-api.md#confirmtransaction)
* [getAccountInfo](jsonrpc-api.md#getaccountinfo)
* [getBalance](jsonrpc-api.md#getbalance)
* [getBlockConfidence](jsonrpc-api.md#getblockconfidence)
* [getClusterNodes](jsonrpc-api.md#getclusternodes)
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
* [getGenesisBlockhash](jsonrpc-api.md#getgenesisblockhash)
* [getLeaderSchedule](jsonrpc-api.md#getleaderschedule)
* [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption)
* [getNumBlocksSinceSignatureConfirmation](jsonrpc-api.md#getnumblockssincesignatureconfirmation)
* [getProgramAccounts](jsonrpc-api.md#getprogramaccounts)
* [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash)
* [getSignatureStatus](jsonrpc-api.md#getsignaturestatus)
@@ -33,6 +29,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [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)
@@ -126,7 +123,7 @@ The result field will be a JSON object with the following sub fields:
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.1,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}
{"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
@@ -151,34 +148,6 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "
{"jsonrpc":"2.0","result":0,"id":1}
```
### getBlockConfidence
Returns confidence for particular block
#### Parameters:
* `u64` - block, identified by Slot
#### Results:
The result field will be an array with two fields:
* Confidence
* `null` - Unknown block
* `object` - BankConfidence
* `array` - confidence, array of u64 integers logging the amount of cluster stake in lamports that has voted on the block at each depth from 0 to `MAX_LOCKOUT_HISTORY`
* 'integer' - total active stake, in lamports, of the current epoch
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getBlockConfidence","params":[5]}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":[{"confidence":[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,10,32]},42],"id":1}
```
### getClusterNodes
Returns information about all the nodes participating in the cluster
@@ -232,34 +201,6 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
{"jsonrpc":"2.0","result":{"epoch":3,"slotIndex":126,"slotsInEpoch":256},"id":1}
```
### getEpochSchedule
Returns epoch schedule information from this cluster's genesis block
#### Parameters:
None
#### Results:
The result field will be an object with the following fields:
* `slots_per_epoch`, the maximum number of slots in each epoch
* `leader_schedule_slot_offset`, the number of slots before beginning of an epoch to calculate a leader schedule for that epoch
* `warmup`, whether epochs start short and grow
* `first_normal_epoch`, first normal-length epoch, log2(slots_per_epoch) - log2(MINIMUM_SLOTS_PER_EPOCH)
* `first_normal_slot`, MINIMUM_SLOTS_PER_EPOCH * (2.pow(first_normal_epoch) - 1)
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochSchedule"}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"first_normal_epoch":8,"first_normal_slot":8160,"leader_schedule_slot_offset":8192,"slots_per_epoch":8192,"warmup":true},"id":1}
```
### getGenesisBlockhash
Returns the genesis block hash
@@ -304,50 +245,6 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
{"jsonrpc":"2.0","result":[...],"id":1}
```
### getMinimumBalanceForRentExemption
Returns minimum balance required to make account rent exempt.
#### Parameters:
* `integer` - account data length, as unsigned integer
#### Results:
* `integer` - minimum lamports required in account, as unsigned 64-bit integer
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getMinimumBalanceForRentExemption", "params":[50]}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":500,"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}
```
### getProgramAccounts
Returns all accounts owned by the provided program Pubkey
@@ -536,6 +433,28 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
{"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
@@ -729,7 +648,7 @@ Subscribe to an account to receive notifications when the lamports or data for a
#### 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.1,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}}
{"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
@@ -787,7 +706,7 @@ Subscribe to a program to receive notifications when the lamports or data for a
* `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.1,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}}
{"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
@@ -866,3 +785,4 @@ Unsubscribe from signature confirmation notification
// Result
{"jsonrpc": "2.0","result": true,"id": 1}
```

View File

@@ -24,21 +24,21 @@
* **num\_credit\_only\_unsigned\_accounts:** The last
`num_credit_only_unsigned_accounts` public keys in `account_keys` refer
`num_credit_only_unsigned_accounts` pubkeys in `account_keys` refer
to non-signing credit only accounts
* **account\_keys:** List of public keys used by the transaction, including
* **account\_keys:** List of pubkeys used by the transaction, including
by the instructions and for signatures. The first
`num_required_signatures` public keys must sign the transaction.
`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/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/instruction.md) that are
* **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
@@ -47,7 +47,7 @@
list is always of length `num_required_signatures`, and the signature
at index `i` corresponds to the public key at index `i` in `account_keys`.
at index `i` corresponds to the pubkey at index `i` in `account_keys`.
The list is initialized with empty signatures \(i.e. zeros\), and
@@ -55,7 +55,7 @@
## 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 public key in `account_keys`.
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

View File

@@ -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
View 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
View 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
View 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]
```

View 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
View 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.

View File

@@ -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 block_. The block contains entries referencing two public keys, a _mint_ and a _bootstrap leader_. The validator 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 validator then contacts the bootstrap leader 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,5 +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 _data plane fanout_; learn more in the [data plan fanout](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/data-plane-fanout.md) section.
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.

View File

@@ -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.
![Fork generation](../.gitbook/assets/fork-generation-3.svg)
![Fork generation](../.gitbook/assets/fork-generation%20%284%29.svg)
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.

View File

@@ -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_.

View File

@@ -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.

View File

@@ -1,8 +1,8 @@
# Managing Forks
The ledger is permitted to fork at slot boundaries. The resulting data structure forms a tree called a _blocktree_. When the validator 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 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
@@ -19,17 +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:
![Forks after pruning](../.gitbook/assets/forks-pruned-3.svg)
![Forks after pruning](../.gitbook/assets/forks-pruned.svg)
The new root is 2, and any active forks that are not descendants from 2 are pruned.
Alternatively, a vote on 6:
![Forks](../.gitbook/assets/forks-pruned2-1.svg)
![Forks](../.gitbook/assets/forks-pruned2%20%284%29.svg)
The tree remains with a root of 1, since the active fork starting at 6 is only 2 checkpoints from the root.

View File

@@ -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.

View File

@@ -27,42 +27,41 @@ 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
### 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 +72,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,23 +80,15 @@ 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 Ininitialized to StakeState::Stake form. This is how stakers choose their initial delegate validator node and activate their stake account lamports. If the stake account is already StakeState::Stake \(i.e. already activated\), the stake is re-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[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\)
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`.
* `account[0]` - RW - The StakeState
`StakeState::authorized_staker` or `authorized_withdrawer` is set to to `Pubkey`.
### StakeInstruction::RedeemVoteCredits
The staker or the owner of the Stake account sends a transaction with this instruction to claim rewards.
@@ -121,7 +101,7 @@ The Vote account and the Stake account pair maintain a lifetime counter of total
* `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 and the stake account's `stake` is increased by the same amount \(re-invested\).
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;
@@ -133,20 +113,20 @@ stake_state.credits_observed = vote_state.credits;
### 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
@@ -158,15 +138,15 @@ Lamports build up over time in a Stake account and any excess over activated sta
## Example Callflow
![Passive Staking Callflow](../.gitbook/assets/passive-staking-callflow-3.svg)
![Passive Staking Callflow](../.gitbook/assets/passive-staking-callflow%20%284%29.svg)
## 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](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/staking-and-rewards.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](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/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).
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.
@@ -188,7 +168,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.
@@ -218,15 +198,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.
### 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 a slot height, i.e. the minimum slot height that must be reached by the network before the stake account balance is available for withdrawal, except to a specified custodian. This information is gathered when the stake account is created.
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\);

View File

@@ -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.

View File

@@ -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.
![Leader sends shreds to Neighborhood 0 in Layer 0](../.gitbook/assets/data-plane-seeding%20%283%29.svg)
![Leader sends blobs to Neighborhood 0 in Layer 0](../.gitbook/assets/data-plane-seeding%20%283%29.svg)
The following diagram shows how Neighborhood 0 fans out to Neighborhoods 1 and 2.
![Neighborhood 0 Fanout to Neighborhood 1 and 2](../.gitbook/assets/data-plane-fanout-3.svg)
![Neighborhood 0 Fanout to Neighborhood 1 and 2](../.gitbook/assets/data-plane-fanout%20%282%29.svg)
Finally, the following diagram shows a two layer cluster with a Fanout of 2.
![Two layer cluster with a Fanout of 2](../.gitbook/assets/data-plane-3.svg)
![Two layer cluster with a Fanout of 2](../.gitbook/assets/data-plane%20%285%29.svg)
### Configuration Values
@@ -38,7 +38,7 @@ Currently, configuration is set when the cluster is launched. In the future, the
## 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.
![Inner workings of a neighborhood](../.gitbook/assets/data-plane-neighborhood-3.svg)
![Inner workings of a neighborhood](../.gitbook/assets/data-plane-neighborhood%20%281%29.svg)

View File

@@ -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.

View 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.

View File

@@ -0,0 +1,111 @@
# Cross-Program Invocation
## Problem
In today's implementation a client can create a transaction that modifies two
accounts, each owned by a separate on-chain program:
```rust,ignore
let message = Message::new(vec![
token_instruction::pay(&alice_pubkey),
acme_instruction::launch_missiles(&bob_pubkey),
]);
client.send_message(&[&alice_keypair, &bob_keypair], &message);
```
The current implementation does not, however, allow the `acme` program to
conveniently invoke `token` instructions on the client's behalf:
```rust,ignore
let message = Message::new(vec![
acme_instruction::pay_and_launch_missiles(&alice_pubkey, &bob_pubkey),
]);
client.send_message(&[&alice_keypair, &bob_keypair], &message);
```
Currently, there is no way to create instruction `pay_and_launch_missiles` that executes
`token_instruction::pay` from the `acme` program. The workaround is to extend the
`acme` program with the implementation of the `token` program, and create `token`
accounts with `ACME_PROGRAM_ID`, which the `acme` program is permitted to modify.
With that workaround, `acme` can modify token-like accounts created by the `acme`
program, but not token accounts created by the `token` program.
## Proposed Solution
The goal of this design is to modify Solana's runtime such that an on-chain
program can invoke an instruction from another program.
Given two on-chain programs `token` and `acme`, each implementing instructions
`pay()` and `launch_missiles()` respectively, we would ideally like to implement
the `acme` module with a call to a function defined in the `token` module:
```rust,ignore
use token;
fn launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
...
}
fn pay_and_launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
token::pay(&keyed_accounts[1..])?;
launch_missiles(keyed_accounts)?;
}
```
The above code would require that the `token` crate be dynamically linked,
so that a custom linker could intercept calls and validate accesses to
`keyed_accounts`. That is, even though the client intends to modify both
`token` and `acme` accounts, only `token` program is permitted to modify
the `token` account, and only the `acme` program is permitted to modify
the `acme` account.
Backing off from that ideal cross-program call, a slightly more
verbose solution is to expose token's existing `process_instruction()`
entrypoint to the acme program:
```rust,ignore
use token_instruction;
fn launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
...
}
fn pay_and_launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
let alice_pubkey = keyed_accounts[1].key;
let instruction = token_instruction::pay(&alice_pubkey);
process_instruction(&instruction)?;
launch_missiles(keyed_accounts)?;
}
```
where `process_instruction()` is built into Solana's runtime and responsible
for routing the given instruction to the `token` program via the instruction's
`program_id` field. Before invoking `pay()`, the runtime must also ensure that
`acme` didn't modify any accounts owned by `token`. It does this by calling
`runtime::verify_instruction()` and then afterward updating all the `pre_*`
variables to tentatively commit `acme`'s account modifications. After `pay()`
completes, the runtime must again ensure that `token` didn't modify any
accounts owned by `acme`. It should call `verify_instruction()` again, but this
time with the `token` program ID. Lastly, after `pay_and_launch_missiles()`
completes, the runtime must call `verify_instruction()` one more time, where it
normally would, but using all updated `pre_*` variables. If executing
`pay_and_launch_missiles()` up to `pay()` made no invalid account changes,
`pay()` made no invalid changes, and executing from `pay()` until
`pay_and_launch_missiles()` returns made no invalid changes, then the runtime
can transitively assume `pay_and_launch_missiles()` as whole made no invalid
account changes, and therefore commit all account modifications.
### Setting `KeyedAccount.is_signer`
When `process_instruction()` is invoked, the runtime must create a new
`KeyedAccounts` parameter using the signatures from the *original* transaction
data. Since the `token` program is immutable and existed on-chain prior to the
`acme` program, the runtime can safely treat the transaction signature as a
signature of a transaction with a `token` instruction. When the runtime sees
the given instruction references `alice_pubkey`, it looks up the key in the
transaction to see if that key corresponds to a transaction signature. In this
case it does and so sets `KeyedAccount.is_signer`, thereby authorizing the
`token` program to modify Alice's account.

86
book/src/drones.md Normal file
View File

@@ -0,0 +1,86 @@
# Creating Signing Services with Drones
This chapter defines an off-chain service called a *drone*, which acts as
custodian of a user's private key. In its simplest form, it can be used to
create *airdrop* transactions, a token transfer from the drone's account to a
client's account.
## Signing Service
A drone is a simple signing service. It listens for requests to sign
*transaction data*. Once received, the drone validates the request however it
sees fit. It may, for example, only accept transaction data with a
`SystemInstruction::Transfer` instruction transferring only up to a certain amount
of tokens. If the drone accepts the transaction, it returns an `Ok(Signature)`
where `Signature` is a signature of the transaction data using the drone's
private key. If it rejects the transaction data, it returns a `DroneError`
describing why.
## Examples
### Granting access to an on-chain game
Creator of on-chain game tic-tac-toe hosts a drone that responds to airdrop
requests containing an `InitGame` instruction. The drone signs the transaction
data in the request and returns it, thereby authorizing its account to pay the
transaction fee and as well as seeding the game's account with enough tokens to
play it. The user then creates a transaction for its transaction data and the
drones signature and submits it to the Solana cluster. Each time the user
interacts with the game, the game pays the user enough tokens to pay the next
transaction fee to advance the game. At that point, the user may choose to keep
the tokens instead of advancing the game. If the creator wants to defend
against that case, they could require the user to return to the drone to sign
each instruction.
### Worldwide airdrop of a new token
Creator of a new on-chain token (ERC-20 interface), may wish to do a worldwide
airdrop to distribute its tokens to millions of users over just a few seconds.
That drone cannot spend resources interacting with the Solana cluster. Instead,
the drone should only verify the client is unique and human, and then return
the signature. It may also want to listen to the Solana cluster for recent
entry IDs to support client retries and to ensure the airdrop is targeting the
desired cluster.
## Attack vectors
### Invalid recent_blockhash
The drone may prefer its airdrops only target a particular Solana cluster. To
do that, it listens to the cluster for new entry IDs and ensure any requests
reference a recent one.
Note: to listen for new entry IDs assumes the drone is either a fullnode or a
*light* client. At the time of this writing, light clients have not been
implemented and no proposal describes them. This document assumes one of the
following approaches be taken:
1. Define and implement a light client
2. Embed a fullnode
3. Query the jsonrpc API for the latest last id at a rate slightly faster than
ticks are produced.
### Double spends
A client may request multiple airdrops before the first has been submitted to
the ledger. The client may do this maliciously or simply because it thinks the
first request was dropped. The drone should not simply query the cluster to
ensure the client has not already received an airdrop. Instead, it should use
`recent_blockhash` to ensure the previous request is expired before signing another.
Note that the Solana cluster will reject any transaction with a `recent_blockhash`
beyond a certain *age*.
### Denial of Service
If the transaction data size is smaller than the size of the returned signature
(or descriptive error), a single client can flood the network. Considering
that a simple `Transfer` operation requires two public keys (each 32 bytes) and a
`fee` field, and that the returned signature is 64 bytes (and a byte to
indicate `Ok`), consideration for this attack may not be required.
In the current design, the drone accepts TCP connections. This allows clients
to DoS the service by simply opening lots of idle connections. Switching to UDP
may be preferred. The transaction data will be smaller than a UDP packet since
the transaction sent to the Solana cluster is already pinned to using UDP.

View File

@@ -0,0 +1,11 @@
## Attack Vectors
### Colluding validation and replication clients
A colluding validation-client, may take the strategy to mark PoReps from non-colluding replicator nodes as invalid as an attempt to maximize the rewards for the colluding replicator nodes. In this case, it isnt feasible for the offended-against replicator nodes to petition the network for resolution as this would result in a network-wide vote on each offending PoRep and create too much overhead for the network to progress adequately. Also, this mitigation attempt would still be vulnerable to a >= 51% staked colluder.
Alternatively, transaction fees from submitted PoReps are pooled and distributed across validation-clients in proportion to the number of valid PoReps discounted by the number of invalid PoReps as voted by each validator-client. Thus invalid votes are directly dis-incentivized through this reward channel. Invalid votes that are revealed by replicator nodes as fishing PoReps, will not be discounted from the payout PoRep count.
Another collusion attack involves a validator-client who may take the strategy to ignore invalid PoReps from colluding replicator and vote them as valid. In this case, colluding replicator-clients would not have to store the data while still receiving rewards for validated PoReps. Additionally, colluding validator nodes would also receive rewards for validating these PoReps. To mitigate this attack, validators must randomly sample PoReps corresponding to the ledger block they are validating and because of this, there will be multiple validators that will receive the colluding replicators invalid submissions. These non-colluding validators will be incentivized to mark these PoReps as invalid as they have no way to determine whether the proposed invalid PoRep is actually a fishing PoRep, for which a confirmation vote would result in the validators stake being slashed.
In this case, the proportion of time a colluding pair will be successful has an upper limit determined by the % of stake of the network claimed by the colluding validator. This also sets bounds to the value of such an attack. For example, if a colluding validator controls 10% of the total validator stake, transaction fees will be lost (likely sent to mining pool) by the colluding replicator 90% of the time and so the attack vector is only profitable if the per-PoRep reward at least 90% higher than the average PoRep transaction fee. While, probabilistically, some colluding replicator-client PoReps will find their way to colluding validation-clients, the network can also monitor rates of paired (validator + replicator) discrepancies in voting patterns and censor identified colluders in these cases.

View File

@@ -0,0 +1,18 @@
## Economic Sustainability
Long term economic sustainability is one of the guiding principles of Solanas economic design. While it is impossible to predict how decentralized economies will develop over time, especially economies with flexible decentralized governances, we can arrange economic components such that, under certain conditions, a sustainable economy may take shape in the long term. In the case of Solanas network, these components take the form of token issuance (via inflation) and token burning.
The dominant remittances from the Solana mining pool are validator and replicator rewards. The disinflationary mechanism is a flat, protocol-specified and adjusted, % of each transaction fee.
The Replicator rewards are to be delivered to replicators as a portion of the network inflation after successful PoRep validation. The per-PoRep reward amount is determined as a function of the total network storage redundancy at the time of the PoRep validation and the network goal redundancy. This function is likely to take the form of a discount from a base reward to be delivered when the network has achieved and maintained its goal redundancy. An example of such a reward function is shown in **Figure 3**
<!-- ![image alt text](porep_reward.png) -->
<p style="text-align:center;"><img src=".gitbook/assets/porep_reward.png" alt="==PoRep Reward Curve ==" width="800"/></p>
**Figure 3**: Example PoRep reward design as a function of global network storage redundancy.
In the example shown in Figure 1, multiple per PoRep base rewards are explored (as a % of Tx Fee) to be delivered when the global ledger replication redundancy meets 10X. When the global ledger replication redundancy is less than 10X, the base reward is discounted as a function of the square of the ratio of the actual ledger replication redundancy to the goal redundancy (i.e. 10X).
<!-- The other protocol-based remittance goes to validation-clients as a reward distributed in proportion to stake-weight for voting to validate the ledger state. The functional issuance of this reward is described in [State-validation Protocol-based Rewards](ed_vce_state_validation_protocol_based_rewards.md) and is designed to reduce over time until validators are incentivized solely through collection of transaction fees. Therefore, in the long-run, protocol-based rewards to replication-nodes will be the only remittances from the mining pool, and will have to be countered by the portion of each non-PoRep transaction fee that is directed back into the mining pool. I.e. for a long-term self-sustaining economy, replicator-client rewards must be subsidized through a minimum fee on each non-PoRep transaction pre-allocated to the mining pool. Through this constraint, we can write the following inequality:
-->
<!-- **== WIP [here](https://docs.google.com/document/d/1HBDasdkjS4Ja9wC_tIUsZPVcxGAWTuYOq9zf6xoQNps/edit?usp=sharing) ==** -->

13
book/src/ed_mvp.md Normal file
View File

@@ -0,0 +1,13 @@
## Proposed MVP of Economic Design
The preceeding sections, outlined in the [Economic Design Overview](ed_overview.md), describe a long-term vision of a sustainable Solana economy. Of course, we don't expect the final implementation to perfectly match what has been described above. We intend to fully engage with network stakeholders throughout the implementation phases (i.e. pre-testnet, testnet, mainnet) to ensure the system supports, and is representative of, the various network participants' interests. The first step toward this goal, however, is outlining a some desired MVP economic features to be available for early pre-testnet and testnet participants. Below is a rough sketch outlining basic economic functionality from which a more complete and functional system can be developed.
### MVP Economic Features
* Faucet to deliver testnet SOLs to validators for staking and dapp development.
* Mechanism by which validators are rewarded via network inflation.
* Ability to delegate tokens to validator nodes
* Validator set commission fees on interest from delegated tokens.
* Replicators to receive fixed, arbitrary reward for submitting validated PoReps. Reward size mechanism (i.e. PoRep reward as a function of total ledger redundancy) to come later.
* Pooling of replicator PoRep transaction fees and weighted distribution to validators based on PoRep verification (see [Replication-validation Transaction Fees](ed_vce_replication_validation_transaction_fees.md). It will be useful to test this protection against attacks on testnet.
* Nice-to-have: auto-delegation of replicator rewards to validator.

16
book/src/ed_overview.md Normal file
View File

@@ -0,0 +1,16 @@
## Economic Design Overview
Solanas crypto-economic system is designed to promote a healthy, long term self-sustaining economy with participant incentives aligned to the security and decentralization of the network. The main participants in this economy are validation-clients and replication-clients. Their contributions to the network, state validation and data storage respectively, and their requisite incentive mechanisms are discussed below.
The main channels of participant remittances are referred to as protocol-based rewards and transaction fees. Protocol-based rewards are issuances from a global, protocol-defined, inflation rate. These rewards will constitute the total reward delivered to replication and validation clients, the remaining sourced from transaction fees. In the early days of the network, it is likely that protocol-based rewards, deployed based on predefined issuance schedule, will drive the majority of participant incentives to participate in the network.
These protocol-based rewards, to be distributed to participating validation and replication clients, are to be a result of a global supply inflation rate, calculated per Solana epoch and distributed amongst the active validator set. As discussed further below, the per annum inflation rate is based on a pre-determined disinflationary schedule. This provides the network with monetary supply predictability which supports long term economic stability and security.
Transaction fees are market-based participant-to-participant transfers, attached to network interactions as a necessary motivation and compensation for the inclusion and execution of a proposed transaction (be it a state execution or proof-of-replication verification). A mechanism for long-term economic stability and forking protection through partial burning of each transaction fee is also discussed below.
A high-level schematic of Solanas crypto-economic design is shown below in **Figure 1**. The specifics of validation-client economics are described in sections: [Validation-client Economics](ed_validation_client_economics.md), [State-validation Protocol-based Rewards](ed_vce_state_validation_protocol_based_rewards.md), [State-validation Transaction Fees](ed_vce_state_validation_transaction_fees.md) and [Replication-validation Transaction Fees](ed_vce_replication_validation_transaction_fees.md). Also, the chapter titled [Validation Stake Delegation](ed_vce_validation_stake_delegation.md) closes with a discussion of validator delegation opportunties and marketplace. Additionally, in [Storage Rent Economics](ed_storage_rent_economics.md), we describe an implementation of storage rent to account for the externality costs of maintaining the active state of the ledger. The [Replication-client Economics](ed_replication_client_economics.md) chapter will review the Solana network design for global ledger storage/redundancy and replicator-client economics ([Storage-replication rewards](ed_rce_storage_replication_rewards.md)) along with a replicator-to-validator delegation mechanism designed to aide participant on-boarding into the Solana economy discussed in [Replication-client Reward Auto-delegation](ed_rce_replication_client_reward_auto_delegation.md). <!-- The [Economic Sustainability](ed_economic_sustainability.md) section dives deeper into Solanas design for long-term economic sustainability and outlines the constraints and conditions for a self-sustaining economy.--> An outline of features for an MVP economic design is discussed in the [Economic Design MVP](ed_mvp.md) section. Finally, in chapter [Attack Vectors](ed_attack_vectors.md), various attack vectors will be described and potential vulnerabilities explored and parameterized.
<!-- ![img alt text](solana_economic_design.png) -->
<p style="text-align:center;"><img src=".gitbook/assets/economic_design_infl_230719.png" alt="== Solana Economic Design Diagram ==" width="800"/></p>
**Figure 1**: Schematic overview of Solana economic incentive design.

View File

@@ -1,8 +1,5 @@
# Replication-client Reward Auto-delegation
**Subject to change.**
### Replication-client Reward Auto-delegation
The ability for Solana network participants to earn rewards by providing storage service is a unique on-boarding path that requires little hardware overhead and minimal upfront capital. It offers an avenue for individuals with extra-storage space on their home laptops or PCs to contribute to the security of the network and become integrated into the Solana economy.
To enhance this on-boarding ramp and facilitate further participation and investment in the Solana economy, replication-clients have the opportunity to auto-delegate their rewards to validation-clients of their choice. Much like the automatic reinvestment of stock dividends, in this scenario, an archiver-client can earn Solana tokens by providing some storage capacity to the network \(i.e. via submitting valid PoReps\), have the protocol-based rewards automatically assigned as delegation to a staked validator node of the archiver's choice and earn interest, less a fee, from the validation-client's network participation.
To enhance this on-boarding ramp and facilitate further participation and investment in the Solana economy, replication-clients have the opportunity to auto-delegate their rewards to validation-clients of their choice. Much like the automatic reinvestment of stock dividends, in this scenario, a replicator-client can earn Solana tokens by providing some storage capacity to the network (i.e. via submitting valid PoReps), have the protocol-based rewards automatically assigned as delegation to a staked validator node of the replicator's choice and earn interest, less a fee, from the validation-client's network participation.

View File

@@ -0,0 +1,5 @@
### Storage-replication Rewards
Replicator-clients download, encrypt and submit PoReps for ledger block sections.3 PoReps submitted to the PoH stream, and subsequently validated, function as evidence that the submitting replicator client is indeed storing the assigned ledger block sections on local hard drive space as a service to the network. Therefore, replicator clients should earn protocol rewards proportional to the amount of storage, and the number of successfully validated PoReps, that they are verifiably providing to the network.
Additionally, replicator clients have the opportunity to capture a portion of slashed bounties [TBD] of dishonest validator clients. This can be accomplished by a replicator client submitting a verifiably false PoRep for which a dishonest validator client receives and signs as a valid PoRep. This reward incentive is to prevent lazy validators and minimize validator-replicator collusion attacks, more on this below.

View File

@@ -0,0 +1,7 @@
## References
1. [https://blog.ethereum.org/2016/07/27/inflation-transaction-fees-cryptocurrency-monetary-policy/](https://blog.ethereum.org/2016/07/27/inflation-transaction-fees-cryptocurrency-monetary-policy/)
2. [https://medium.com/solana-labs/how-to-create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281](https://medium.com/solana-labs/how-to-create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281)
3. [https://medium.com/solana-labs/how-to-create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281](https://medium.com/solana-labs/how-to-create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281)

View File

@@ -0,0 +1,3 @@
## Replication-client economics
Replication-clients should be rewarded for providing the network with storage space. Incentivization of the set of replicators provides data security through redundancy of the historical ledger. Replication nodes are rewarded in proportion to the amount of ledger data storage provided, as proved by successfully submitting Proofs-of-Replication to the cluster.. These rewards are captured by generating and entering Proofs of Replication (PoReps) into the PoH stream which can be validated by Validation nodes as described above in the [Replication-validation Transaction Fees](ed_vce_replication_validation_transaction_fees.md) chapter.

View File

@@ -1,6 +1,6 @@
## Storage Rent Economics
Each transaction that is submitted to the Solana ledger imposes costs. Transaction fees paid by the submitter, and collected by a validator, in theory, account for the acute, transacitonal, costs of validating and adding that data to the ledger. At the same time, our compensation design for archivers (see [Replication-client Economics](ed_replication_client_economics.md)), in theory, accounts for the long term storage of the historical ledger. Unaccounted in this process is the mid-term storage of active ledger state, necessarily maintined by the rotating validator set. This type of storage imposes costs not only to validators but also to the broader network as active state grows so does data transmission and validation overhead. To account for these costs, we describe here our preliminary design and implementation of storage rent.
Each transaction that is submitted to the Solana ledger imposes costs. Transaction fees paid by the submitter, and collected by a validator, in theory, account for the acute, transacitonal, costs of validating and adding that data to the ledger. At the same time, our compensation design for replicators (see [Replication-client Economics](ed_replication_client_economics.md)), in theory, accounts for the long term storage of the historical ledger. Unaccounted in this process is the mid-term storage of active ledger state, necessarily maintined by the rotating validator set. This type of storage imposes costs not only to validators but also to the broader network as active state grows so does data transmission and validation overhead. To account for these costs, we describe here our preliminary design and implementation of storage rent.
Storage rent can be paid via one of two methods:

View File

@@ -0,0 +1,6 @@
## Validation-client Economics
Validator-clients are eligible to receive protocol-based (i.e. inflation-based) rewards issued via stake-based annual interest rates (calculated per epoch) by providing compute (CPU+GPU) resources to validate and vote on a given PoH state. These protocol-based rewards are determined through an algorithmic disinflationary schedule as a function of total amount of circulating tokens.
The network is expected to launch with an annual inflation rate around 15%, set to decrease by 15% per year until a long-term stable rate of 1-2% is reached. These issuances are to be split and distributed to participating validators and replicators, with around 90% of the issued tokens allocated for validator rewards. Because the network will be distributing a fixed amount of inflation rewards across the stake-weighted valdiator set, any individual validator's interest rate will be a function of the amount of staked SOL in relation to the circulating SOL.
Additionally, validator clients may earn revenue through fees via state-validation transactions and Proof-of-Replication (PoRep) transactions. For clarity, we separately describe the design and motivation of these revenue distriubutions for validation-clients below: state-validation protocol-based rewards, state-validation transaction fees and rent, and PoRep-validation transaction fees.

View File

@@ -0,0 +1,9 @@
### Replication-validation Transaction Fees
As previously mentioned, validator-clients will also be responsible for validating PoReps submitted into the PoH stream by replicator-clients. In this case, validators are providing compute (CPU/GPU) and light storage resources to confirm that these replication proofs could only be generated by a client that is storing the referenced PoH leger block.
While replication-clients are incentivized and rewarded through protocol-based rewards schedule (see [Replication-client Economics](ed_replication_client_economics.md)), validator-clients will be incentivized to include and validate PoReps in PoH through collection of transaction fees associated with the submitted PoReps and distribution of protocol rewards proportional to the validated PoReps. As will be described in detail in the Section 3.1, replication-client rewards are protocol-based and designed to reward based on a global data redundancy factor. I.e. the protocol will incentivize replication-client participation through rewards based on a target ledger redundancy (e.g. 10x data redundancy).
The validation of PoReps by validation-clients is computationally more expensive than state-validation (detail in the [Economic Sustainability](ed_economic_sustainability.md) chapter), thus the transaction fees are expected to be proportionally higher.
There are various attack vectors available for colluding validation and replication clients, also described in detail below in [Economic Sustainability](ed_economic_sustainability). To protect against various collusion attack vectors, for a given epoch, validator rewards are distributed across participating validation-clients in proportion to the number of validated PoReps in the epoch less the number of PoReps that mismatch the replicators challenge. The PoRep challenge game is described in [Ledger Replication](https://github.com/solana-labs/solana/blob/master/book/src/ledger-replication.md#the-porep-game). This design rewards validators proportional to the number of PoReps they process and validate, while providing negative pressure for validation-clients to submit lazy or malicious invalid votes on submitted PoReps (note that it is computationally prohibitive to determine whether a validator-client has marked a valid PoRep as invalid).

View File

@@ -0,0 +1,40 @@
### State-validation protocol-based rewards
Validator-clients have two functional roles in the Solana network:
* Validate (vote) the current global state of that PoH along with any Proofs-of-Replication (see [Replication Client Economics](ed_replication_client_economics.md)) that they are eligible to validate.
* Be elected as leader on a stake-weighted round-robin schedule during which time they are responsible for collecting outstanding transactions and Proofs-of-Replication and incorporating them into the PoH, thus updating the global state of the network and providing chain continuity.
Validator-client rewards for these services are to be distributed at the end of each Solana epoch. As previously discussed, compensation for validator-clients is provided via a protocol-based annual inflation rate dispersed in proportion to the stake-weight of each validator (see below) along with leader-claimed transaction fees available during each leader rotation. I.e. during the time a given validator-client is elected as leader, it has the opportunity to keep a portion of each transaction fee, less a protocol-specified amount that is destroyed (see [Validation-client State Transaction Fees](ed_vce_state_validation_transaction_fees.md)). PoRep transaction fees are also collected by the leader client and validator PoRep rewards are distributed in proportion to the number of validated PoReps less the number of PoReps that mismatch a replicator's challenge. (see [Replication-client Transaction Fees](ed_vce_replication_validation_transaction_fees.md))
The effective protocol-based annual interest rate (%) per epoch received by validation-clients is to be a function of:
* the current global inflation rate, derived from the pre-determined dis-inflationary issuance schedule (see [Validation-client Economics](ed_validartion_client_economics.md))
* the fraction of staked SOLs out of the current total circulating supply,
* the up-time/participation [% of available slots that validator had opportunity to vote on] of a given validator over the previous epoch.
The first factor is a function of protocol parameters only (i.e. independent of validator behavior in a given epoch) and results in a global validation reward schedule designed to incentivize early participation, provide clear montetary stability and provide optimal security in the network.
At any given point in time, a specific validator's interest rate can be determined based on the porportion of circulating supply that is staked by the network and the validator's uptime/activity in the previous epoch. For example, consider a hypothetical instance of the network with an initial circulating token supply of 250MM tokens with an additional 250MM vesting over 3 years. Additionally an inflation rate is specified at network launch of 7.5%, and a disinflationary schedule of 20% decrease in inflation rate per year (the actual rates to be implemented are to be worked out during the testnet experimentation phase of mainnet launch). With these broad assumptions, the 10-year inflation rate (adjusted daily for this example) is shown in **Figure 2**, while the total circulating token supply is illustrated in **Figure 3**. Neglected in this toy-model is the inflation supression due to the portion of each transaction fee that is to be destroyed.
<p style="text-align:center;"><img src=".gitbook/assets/p_ex_schedule.png" alt="drawing" width="800"/></p>
**Figure 2:** In this example schedule, the annual inflation rate [%] reduces at around 20% per year, until it reaches the long-term, fixed, 1.5% rate.
<p style="text-align:center;"><img src=".gitbook/assets/p_ex_supply.png" alt="drawing" width="800"/></p>
**Figure 3:** The total token supply over a 10-year period, based on an initial 250MM tokens with the disinflationary inflation schedule as shown in **Figure 2**
Over time, the interest rate, at a fixed network staked percentage, will reduce concordant with network inflation. Validation-client interest rates are designed to be higher in the early days of the network to incentivize participation and jumpstart the network economy. As previously mentioned, the inflation rate is expected to stabalize near 1-2% which also results in a fixed, long-term, interest rate to be provided to validator-clients. This value does not represent the total interest available to validator-clients as transaction fees for state-validation and ledger storage replication (PoReps) are not accounted for here.
Given these example parameters, annualized validator-specific interest rates can be determined based on the global fraction of tokens bonded as stake, as well as their uptime/activity in the previous epoch. For the purpose of this example, we assume 100% uptime for all validators and a split in interest-based rewards between validators and replicator nodes of 80%/20%. Additionally, the fraction of staked circulating supply is assummed to be constant. Based on these assumptions, an annualized validation-client interest rate schedule as a function of % circulating token supply that is staked is shown in** Figure 4**.
<!-- ![== Validation Client Interest Rates Figure ==](validation_client_interest_rates.png =250x) -->
<p style="text-align:center;"><img src=".gitbook/assets/p_ex_interest.png" alt="drawing" width="800"/></p>
**Figure 4:** Shown here are example validator interest rates over time, neglecting transaction fees, segmented by fraction of total circulating supply bonded as stake.
This epoch-specific protocol-defined interest rate sets an upper limit of *protocol-generated* annual interest rate (not absolute total interest rate) possible to be delivered to any validator-client per epoch. The distributed interest rate per epoch is then discounted from this value based on the participation of the validator-client during the previous epoch.

View File

@@ -0,0 +1,20 @@
### State-validation Transaction Fees
Each transaction sent through the network, to be processed by the current leader validation-client and confirmed as a global state transaction, must contain a transaction fee. Transaction fees offer many benefits in the Solana economic design, for example they:
* provide unit compensation to the validator network for the CPU/GPU resources necessary to process the state transaction,
* reduce network spam by introducing real cost to transactions,
* open avenues for a transaction market to incentivize validation-client to collect and process submitted transactions in their function as leader,
* and provide potential long-term economic stability of the network through a protocol-captured minimum fee amount per transaction, as described below.
Many current blockchain economies (e.g. Bitcoin, Ethereum), rely on protocol-based rewards to support the economy in the short term, with the assumption that the revenue generated through transaction fees will support the economy in the long term, when the protocol derived rewards expire. In an attempt to create a sustainable economy through protocol-based rewards and transaction fees, a fixed portion of each transaction fee is destroyed, with the remaining fee going to the current leader processing the transaction. A scheduled global inflation rate provides a source for rewards distributed to validation-clients, through the process described above, and replication-clients, as discussed below.
Transaction fees are set by the network cluster based on recent historical throughput, see [Congestion Driven Fees](transaction-fees.md#congestion-driven-fees). This minimum portion of each transaction fee can be dynamically adjusted depending on historical gas usage. In this way, the protocol can use the minimum fee to target a desired hardware utilisation. By monitoring a protocol specified gas usage with respect to a desired, target usage amount, the minimum fee can be raised/lowered which should, in turn, lower/raise the actual gas usage per block until it reaches the target amount. This adjustment process can be thought of as similar to the difficulty adjustment algorithm in the Bitcoin protocol, however in this case it is adjusting the minimum transaction fee to guide the transaction processing hardware usage to a desired level.
As mentioned, a fixed-proportion of each transaction fee is to be destroyed. The intent of this design is to retain leader incentive to include as many transactions as possible within the leader-slot time, while providing an inflation limiting mechansim that protects against "tax evasion" attacks (i.e. side-channel fee payments)<sup>[1](ed_referenced.md)</sup>.
Additionally, the burnt fees can be a consideration in fork selection. In the case of a PoH fork with a malicious, censoring leader, we would expect the total fees destroyed to be less than a comparable honest fork, due to the fees lost from censoring. If the censoring leader is to compensate for these lost protocol fees, they would have to replace the burnt fees on their fork themselves, thus potentially reducing the incentive to censor in the first place.

View File

@@ -0,0 +1,29 @@
### Validation Stake Delegation
Running a Solana validation-client required relatively modest upfront hardware capital investment. **Table 2** provides an example hardware configuration to support ~1M tx/s with estimated off-the-shelf costs:
|Component|Example|Estimated Cost|
|--- |--- |--- |
|GPU|2x 2080 Ti|$2500|
|or|4x 1080 Ti|$2800|
|OS/Ledger Storage|Samsung 860 Evo 2TB|$370|
|Accounts storage|2x Samsung 970 Pro M.2 512GB|$340|
|RAM|32 Gb|$300|
|Motherboard|AMD x399|$400|
|CPU|AMD Threadripper 2920x|$650|
|Case||$100|
|Power supply|EVGA 1600W|$300|
|Network|> 500 mbps||
|Network (1)|Google webpass business bay area 1gbps unlimited|$5500/mo|
|Network (2)|Hurricane Electric bay area colo 1gbps|$500/mo|
**Table 2** example high-end hardware setup for running a Solana client.
Despite the low-barrier to entry as a validation-client, from a capital investment perspective, as in any developing economy, there will be much opportunity and need for trusted validation services as evidenced by node reliability, UX/UI, APIs and other software accessibility tools. Additionally, although Solanas validator node startup costs are nominal when compared to similar networks, they may still be somewhat restrictive for some potential participants. In the spirit of developing a true decentralized, permissionless network, these interested parties still have two options to become involved in the Solana network/economy:
1. Delegation of previously acquired tokens with a reliable validation node to earn a portion of interest generated
2. Provide local storage space as a replication-client and receive rewards by submitting Proof-of-Replication (see [Replication-client Economics](ed_replication_client_economics.md)).
a. This participant has the additional option to directly delegate their earned storage rewards ([Replication-client Reward Auto-delegation](ed_rce_replication_client_reward_auto_delegation.md))
Delegation of tokens to validation-clients, via option 1, provides a way for passive Solana token holders to become part of the active Solana economy and earn interest rates proportional to the interest rate generated by the delegated validation-client. Additionally, this feature intends to create a healthy validation-client market, with potential validation-client nodes competing to build reliable, transparent and profitable delegation services.

View File

@@ -0,0 +1,66 @@
# Embedding the Move Language
## Problem
Solana enables developers to write on-chain programs in general purpose
programming languages such as C or Rust, but those programs contain
Solana-specific mechanisms. For example, there isn't another chain that asks
developers to create a Rust module with a `process_instruction(KeyedAccounts)`
function. Whenever practical, Solana should offer dApp developers more portable
options.
Until just recently, no popular blockchain offered a language that could expose
the value of Solana's massively parallel [runtime](runtime.md). Solidity
contracts, for example, do not separate references to shared data from contract
code, and therefore need to be executed serially to ensure deterministic
behavior. In practice we see that the most aggressively optimized EVM-based
blockchains all seem to peak out around 1,200 TPS - a small fraction of what
Solana can do. The Libra project, on the other hand, designed an on-chain
programming language called Move that is more suitable for parallel execution.
Like Solana's runtime, Move programs depend on accounts for all shared state.
The biggest design difference between Solana's runtime and Libra's Move VM is
how they manage safe invocations between modules. Solana took an operating
systems approach and Libra took the domain-specific language approach. In the
runtime, a module must trap back into the runtime to ensure the caller's module
did not write to data owned by the callee. Likewise, when the callee completes,
it must again trap back to the runtime to ensure the callee did not write to
data owned by the caller. Move, on the other hand, includes an advanced type
system that allows these checks to be run by its bytecode verifier. Because
Move bytecode can be verified, the cost of verification is paid just once, at
the time the module is loaded on-chain. In the runtime, the cost is paid each
time a transaction crosses between modules. The difference is similar in spirit
to the difference between a dynamically-typed language like Python versus a
statically-typed language like Java. Solana's runtime allows dApps to be
written in general purpose programming languages, but that comes with the cost
of runtime checks when jumping between programs.
This proposal attempts to define a way to embed the Move VM such that:
* cross-module invocations within Move do not require the runtime's
cross-program runtime checks
* Move programs can leverage functionality in other Solana programs and vice
versa
* Solana's runtime parallelism is exposed to batches of Move and non-Move
transactions
## Proposed Solution
### Move VM as a Solana loader
The Move VM shall be embedded as a Solana loader under the identifier
`MOVE_PROGRAM_ID`, so that Move modules can be marked as `executable` with the
VM as its `owner`. This will allow modules to load module dependencies, as well
as allow for parallel execution of Move scripts.
All data accounts owned by Move modules must set their owners to the loader,
`MOVE_PROGRAM_ID`. Since Move modules encapsulate their account data in the
same way Solana programs encapsulate theirs, the Move module owner should be
embedded in the account data. The runtime will grant write access to the Move
VM, and Move grants access to the module accounts.
### Interacting with Solana programs
To invoke instructions in non-Move programs, Solana would need to extend the
Move VM with a `process_instruction()` system call. It would work the same as
`process_instruction()` Rust BPF programs.

104
book/src/fork-generation.md Normal file
View File

@@ -0,0 +1,104 @@
# Fork Generation
The chapter describes how forks naturally occur as a consequence of [leader
rotation](leader-rotation.md).
## Overview
Nodes take turns being leader and generating the PoH that encodes state
changes. The cluster can tolerate loss of connection to any leader by
synthesizing what the leader ***would*** have generated had it been connected
but not ingesting any state changes. The possible number of forks is thereby
limited to a "there/not-there" skip list of forks that may arise on leader
rotation slot boundaries. At any given slot, only a single leader's
transactions will be accepted.
## Message Flow
1. Transactions are ingested by the current leader.
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 blobs)
1. The PoH stream includes ticks; empty entries that indicate liveness of
the leader and the passage of time on the cluster.
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.
7. Validators validate the transactions and execute them on their state.
8. Validators compute the hash of the state.
9. At specific times, i.e. specific PoH tick counts, validators transmit votes
to the leader.
1. Votes are signatures of the hash of the computed state at that PoH tick
count
2. Votes are also propagated via gossip
10. Leader executes the votes as any other transaction and broadcasts them to
the cluster.
11. Validators observe their votes and all the votes from the cluster.
## Partitions, Forks
Forks can arise at PoH tick counts that correspond to a vote. The next leader
may not have observed the last vote slot and may start their slot with
generated virtual PoH entries. These empty ticks are generated by all nodes in
the cluster at a cluster-configured rate for hashes/per/tick `Z`.
There are only two possible versions of the PoH during a voting slot: PoH with
`T` ticks and entries generated by the current leader, or PoH with just ticks.
The "just ticks" version of the PoH can be thought of as a virtual ledger, one
that all nodes in the cluster can derive from the last tick in the previous
slot.
Validators can ignore forks at other points (e.g. from the wrong leader), or
slash the leader responsible for the fork.
Validators vote based on a greedy choice to maximize their reward described in
[Tower BFT](tower-bft.md).
### Validator's View
#### Time Progression
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.
<img alt="Fork generation" src=".gitbook/assets/fork-generation.svg" class="center"/>
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.
#### Time Division
It's useful to consider leader rotation over PoH tick count as time division of
the job of encoding state for the cluster. The following table presents the
above tree of forks as a time-divided ledger.
leader slot | L1 | L2 | L3 | L4 | L5
-------|----|----|----|----|----
data | E1| E2 | E3 | E4 | E5
ticks since prev | | | | x | xx
Note that only data from leader L3 will be accepted during leader slot L3.
Data from L3 may include "catchup" ticks back to a slot other than L2 if L3 did
not observe L2's data. L4 and L5's transmissions include the "ticks to prev"
PoH entries.
This arrangement of the network data streams permits nodes to save exactly this
to the ledger for replay, restart, and checkpoints.
### 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.

168
book/src/getting-started.md Normal file
View File

@@ -0,0 +1,168 @@
# Getting Started
The Solana git repository contains all the scripts you might need to spin up your
own local testnet. Depending on what you're looking to achieve, you may want to
run a different variation, as the full-fledged, performance-enhanced
multinode testnet is considerably more complex to set up than a Rust-only,
singlenode testnode. If you are looking to develop high-level features, such
as experimenting with smart contracts, save yourself some setup headaches and
stick to the Rust-only singlenode demo. If you're doing performance optimization
of the transaction pipeline, consider the enhanced singlenode demo. If you're
doing consensus work, you'll need at least a Rust-only multinode demo. If you want
to reproduce our TPS metrics, run the enhanced multinode demo.
For all four variations, you'd need the latest Rust toolchain and the Solana
source code:
First, install Rust's package manager Cargo.
```bash
$ curl https://sh.rustup.rs -sSf | sh
$ source $HOME/.cargo/env
```
Now checkout the code from github:
```bash
$ git clone https://github.com/solana-labs/solana.git
$ cd solana
```
The demo code is sometimes broken between releases as we add new low-level
features, so if this is your first time running the demo, you'll improve
your odds of success if you check out the
[latest release](https://github.com/solana-labs/solana/releases)
before proceeding:
```bash
$ TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
$ git checkout $TAG
```
### Configuration Setup
Ensure important programs such as the vote program are built before any
nodes are started
```bash
$ cargo build
```
The network is initialized with a genesis ledger generated by running the
following script.
```bash
$ ./multinode-demo/setup.sh
```
### Drone
In order for the fullnodes and clients to work, we'll need to
spin up a drone to give out some test tokens. The drone delivers Milton
Friedman-style "air drops" (free tokens to requesting clients) to be used in
test transactions.
Start the drone with:
```bash
$ ./multinode-demo/drone.sh
```
### Singlenode Testnet
Before you start a validator, make sure you know the IP address of the machine you
want to be the bootstrap leader for the demo, and make sure that udp ports 8000-10000 are
open on all the machines you want to test with.
Now start the bootstrap leader in a separate shell:
```bash
$ ./multinode-demo/bootstrap-leader.sh
```
Wait a few seconds for the server to initialize. It will print "leader ready..." when it's ready to
receive transactions. The leader will request some tokens from the drone if it doesn't have any.
The drone does not need to be running for subsequent leader starts.
### Multinode Testnet
To run a multinode testnet, after starting a leader node, spin up some
additional validators in separate shells:
```bash
$ ./multinode-demo/validator-x.sh
```
To run a performance-enhanced full node on Linux,
[CUDA 10.0](https://developer.nvidia.com/cuda-downloads) must be installed on
your system:
```bash
$ ./fetch-perf-libs.sh
$ SOLANA_CUDA=1 ./multinode-demo/bootstrap-leader.sh
$ SOLANA_CUDA=1 ./multinode-demo/validator.sh
```
### Testnet Client Demo
Now that your singlenode or multinode testnet is up and running let's send it
some transactions!
In a separate shell start the client:
```bash
$ ./multinode-demo/bench-tps.sh # runs against localhost by default
```
What just happened? The client demo spins up several threads to send 500,000 transactions
to the testnet as quickly as it can. The client then pings the testnet periodically to see
how many transactions it processed in that time. Take note that the demo intentionally
floods the network with UDP packets, such that the network will almost certainly drop a
bunch of them. This ensures the testnet has an opportunity to reach 710k TPS. The client
demo completes after it has convinced itself the testnet won't process any additional
transactions. You should see several TPS measurements printed to the screen. In the
multinode variation, you'll see TPS measurements for each validator node as well.
### Testnet Debugging
There are some useful debug messages in the code, you can enable them on a per-module and per-level
basis. Before running a leader or validator set the normal RUST\_LOG environment variable.
For example
* To enable `info` everywhere and `debug` only in the solana::banking_stage module:
```bash
$ export RUST_LOG=solana=info,solana::banking_stage=debug
```
* To enable BPF program logging:
```bash
$ export RUST_LOG=solana_bpf_loader=trace
```
Generally we are using `debug` for infrequent debug messages, `trace` for potentially frequent
messages and `info` for performance-related logging.
You can also attach to a running process with GDB. The leader's process is named
_solana-validator_:
```bash
$ sudo gdb
attach <PID>
set logging on
thread apply all bt
```
This will dump all the threads stack traces into gdb.txt
## Public Testnet
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`.
```bash
$ ./multinode-demo/bench-tps.sh --entrypoint testnet.solana.com:8001 --drone testnet.solana.com:9900 --duration 60 --tx_count 50
```
You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet)

View File

@@ -30,7 +30,7 @@ $ git checkout $TAG
Ensure important programs such as the vote program are built before any nodes are started
```bash
$ cargo build
$ cargo build --all
```
The network is initialized with a genesis ledger generated by running the following script.
@@ -41,7 +41,7 @@ $ ./multinode-demo/setup.sh
### Drone
In order for the validators and clients to work, we'll need to spin up a drone to give out some test tokens. The drone delivers Milton Friedman-style "air drops" \(free tokens to requesting clients\) to be used in test transactions.
In order for the fullnodes and clients to work, we'll need to spin up a drone to give out some test tokens. The drone delivers Milton Friedman-style "air drops" \(free tokens to requesting clients\) to be used in test transactions.
Start the drone with:
@@ -69,7 +69,7 @@ To run a multinode testnet, after starting a leader node, spin up some additiona
$ ./multinode-demo/validator-x.sh
```
To run a performance-enhanced validator on Linux, [CUDA 10.0](https://developer.nvidia.com/cuda-downloads) must be installed on your system:
To run a performance-enhanced full node on Linux, [CUDA 10.0](https://developer.nvidia.com/cuda-downloads) must be installed on your system:
```bash
$ ./fetch-perf-libs.sh
@@ -84,7 +84,7 @@ Now that your singlenode or multinode testnet is up and running let's send it so
In a separate shell start the client:
```bash
$ ./multinode-demo/bench-tps.sh # runs against localhost by default
$ ./multinode-demo/client.sh # runs against localhost by default
```
What just happened? The client demo spins up several threads to send 500,000 transactions to the testnet as quickly as it can. The client then pings the testnet periodically to see how many transactions it processed in that time. Take note that the demo intentionally floods the network with UDP packets, such that the network will almost certainly drop a bunch of them. This ensures the testnet has an opportunity to reach 710k TPS. The client demo completes after it has convinced itself the testnet won't process any additional transactions. You should see several TPS measurements printed to the screen. In the multinode variation, you'll see TPS measurements for each validator node as well.
@@ -125,7 +125,7 @@ This will dump all the threads stack traces into gdb.txt
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`.
```bash
$ ./multinode-demo/bench-tps.sh --entrypoint testnet.solana.com:8001 --drone testnet.solana.com:9900 --duration 60 --tx_count 50
$ ./multinode-demo/client.sh --entrypoint testnet.solana.com:8001 --drone testnet.solana.com:9900 --duration 60 --tx_count 50
```
You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet)

View File

@@ -3,5 +3,5 @@
Participate in our testnet:
* [Running a Validator](../running-validator/)
* [Running an Archiver](../running-archiver.md)
* [Running a Replicator](../running-replicator.md)

128
book/src/gossip.md Normal file
View File

@@ -0,0 +1,128 @@
# Gossip Service
The Gossip Service acts as a gateway to nodes in the control plane. Validators
use the service to ensure information is available to all other nodes in a cluster.
The service broadcasts information using a gossip protocol.
## Gossip Overview
Nodes continuously share signed data objects among themselves in order to
manage a cluster. For example, they share their contact information, ledger
height, and votes.
Every tenth of a second, each node sends a "push" message and/or a "pull"
message. Push and pull messages may elicit responses, and push messages may be
forwarded on to others in the cluster.
Gossip runs on a well-known UDP/IP port or a port in a well-known range. Once
a cluster is bootstrapped, nodes advertise to each other where to find their
gossip endpoint (a socket address).
## Gossip Records
Records shared over gossip are arbitrary, but signed and versioned (with a
timestamp) as needed to make sense to the node receiving them. If a node
receives two records from the same source, it updates its own copy with the
record with the most recent timestamp.
## Gossip Service Interface
### Push Message
A node sends a push message to tells the cluster it has information to share.
Nodes send push messages to `PUSH_FANOUT` push peers.
Upon receiving a push message, a node examines the message for:
1. Duplication: if the message has been seen before, the node drops the message
and may respond with `PushMessagePrune` if forwarded from a low staked node
2. New data: if the message is new to the node
* Stores the new information with an updated version in its cluster info and
purges any previous older value
* Stores the message in `pushed_once` (used for detecting duplicates,
purged after `PUSH_MSG_TIMEOUT * 5` ms)
* Retransmits the messages to its own push peers
3. Expiration: nodes drop push messages that are older than `PUSH_MSG_TIMEOUT`
### Push Peers, Prune Message
A nodes selects its push peers at random from the active set of known peers.
The node keeps this selection for a relatively long time. When a prune message
is received, the node drops the push peer that sent the prune. Prune is an
indication that there is another, higher stake weighted path to that node than direct push.
The set of push peers is kept fresh by rotating a new node into the set every
`PUSH_MSG_TIMEOUT/2` milliseconds.
### Pull Message
A node sends a pull message to ask the cluster if there is any new information.
A pull message is sent to a single peer at random and comprises a Bloom filter
that represents things it already has. A node receiving a pull message
iterates over its values and constructs a pull response of things that miss the
filter and would fit in a message.
A node constructs the pull Bloom filter by iterating over current values and
recently purged values.
A node handles items in a pull response the same way it handles new data in a
push message.
## Purging
Nodes retain prior versions of values (those updated by a pull or push) and
expired values (those older than `GOSSIP_PULL_CRDS_TIMEOUT_MS`) in
`purged_values` (things I recently had). Nodes purge `purged_values` that are
older than `5 * GOSSIP_PULL_CRDS_TIMEOUT_MS`.
## Eclipse Attacks
An eclipse attack is an attempt to take over the set of node connections with
adversarial endpoints.
This is relevant to our implementation in the following ways.
* Pull messages select a random node from the network. An eclipse attack on
*pull* would require an attacker to influence the random selection in such a way
that only adversarial nodes are selected for pull.
* Push messages maintain an active set of nodes and select a random fanout for
every push message. An eclipse attack on *push* would influence the active set
selection, or the random fanout selection.
### Time and Stake based weights
Weights are calculated based on `time since last picked` and the `natural log` of the `stake weight`.
Taking the `ln` of the stake weight allows giving all nodes a fairer chance of network
coverage in a reasonable amount of time. It helps normalize the large possible `stake weight` differences between nodes.
This way a node with low `stake weight`, compared to a node with large `stake weight` will only have to wait a
few multiples of ln(`stake`) seconds before it gets picked.
There is no way for an adversary to influence these parameters.
### Pull Message
A node is selected as a pull target based on the weights described above.
### Push Message
A prune message can only remove an adversary from a potential connection.
Just like *pull message*, nodes are selected into the active set based on weights.
## Notable differences from PlumTree
The active push protocol described here is based on [Plum
Tree](https://haslab.uminho.pt/jop/files/lpr07a.pdf). The main differences are:
* Push messages have a wallclock that is signed by the originator. Once the
wallclock expires the message is dropped. A hop limit is difficult to implement
in an adversarial setting.
* Lazy Push is not implemented because its not obvious how to prevent an
adversary from forging the message fingerprint. A naive approach would allow an
adversary to be prioritized for pull based on their input.

View File

@@ -0,0 +1,3 @@
# Implemented Design Proposals
The following design proposals are fully implemented.

View File

@@ -2,11 +2,11 @@
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](../cluster/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.
The blocktree allows a validator to record every blob it observes on the network, in any order, as long as the blob 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.
Blobs are moved to a fork-able key space the tuple of `leader slot` + `blob 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.
Repair requests for recent blobs are served out of RAM or recent files and out of deeper storage for less recent blobs, as implemented by the store backing Blocktree.
## Functionalities of Blocktree
@@ -14,17 +14,17 @@ Repair requests for recent shreds are served out of RAM or recent files and out
pipeline, right behind network receive and signature verification. If the
shred received is consistent with the leader schedule \(i.e. was signed by the
blob 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,
blob that's been received. Blocktree stores blobs with signatures,
preserving the chain of origination.
3. Forks: Blocktree supports random access of shreds, so can support a
3. Forks: Blocktree supports random access of blobs, so can support a
validator's need to rollback and replay from a Bank checkpoint.
@@ -38,21 +38,21 @@ Repair requests for recent shreds are served out of RAM or recent files and out
## 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\).
1. Entries in the Blocktree are stored as key-value pairs, where the key is the concatenated slot index and blob index for an entry, and the value is the entry data. Note blob 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
* `consumed` - The highest blob index `n`, such that for all `m < n`, there exists a blob in this slot with blob index equal to `n` \(i.e. the highest consecutive blob index\).
* `received` - The highest received blob 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.
* `last_index` - The index of the blob that is flagged as the last blob for this slot. This flag on a blob will be set by the leader for a slot when they are transmitting the last blob 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`.
3. Chaining - When a blob for a new slot `x` arrives, we check the number of blocks \(`num_blocks`\) for that new slot \(this information is encoded in the blob\). 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`.
@@ -86,5 +86,5 @@ Replay stage uses Blocktree APIs to find the longest chain of entries it can han
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.
Archiver nodes will be responsible for storing really old ledger contents, and validators need only persist their bank periodically.
Replicator nodes will be responsible for storing really old ledger contents, and validators need only persist their bank periodically.

View File

@@ -1,89 +0,0 @@
# Commitment
The commitment metric aims to give clients a measure of the network confirmation
and stake levels on a particular block. Clients can then use this information to
derive their own measures of confidence.
# Calculation RPC
Clients can request commitment metrics from a validator for a signature `s`
through `get_block_commitment(s: Signature) -> BlockCommitment` over RPC. The
`BlockCommitment` struct contains an array of u64 `[u64, MAX_CONFIRMATIONS]`. This
array represents the commitment metric for the particular block `N` that
contains the signature `s` as of the last block `M` that the validator voted on.
An entry `s` at index `i` in the `BlockCommitment` array implies that the
validator observed `s` total stake in the cluster reaching `i` confirmations on
block `N` as observed in some block `M`. There will be `MAX_CONFIRMATIONS` elements in
this array, representing all the possible number of confirmations from 1 to
`MAX_CONFIRMATIONS`.
# Computation of commitment metric
Building this `BlockCommitment` struct leverages the computations already being
performed for building consensus. The `collect_vote_lockouts` function in
`consensus.rs` builds a HashMap, where each entry is of the form `(b, s)`
where `s` is a `StakeLockout` struct representing the amount of stake and
lockout on a bank `b`.
This computation is performed on a votable candidate bank `b` as follows.
```
let output: HashMap<b, StakeLockout> = HashMap::new();
for vote_account in b.vote_accounts {
for v in vote_account.vote_stack {
for a in ancestors(v) {
f(*output.get_mut(a), vote_account, v);
}
}
}
```
where `f` is some accumulation function that modifies the `StakeLockout` entry
for slot `a` with some data derivable from vote `v` and `vote_account`
(stake, lockout, etc.). Note here that the `ancestors` here only includes
slots that are present in the current status cache. Signatures for banks earlier
than those present in the status cache would not be queryable anyway, so those
banks are not included in the commitment calculations here.
Now we can naturally augment the above computation to also build a
`BlockCommitment` array for every bank `b` by:
1) Adding a `ForkCommitmentCache` to collect the `BlockCommitment` structs
2) Replacing `f` with `f'` such that the above computation also builds this
`BlockCommitment` for every bank `b`.
We will proceed with the details of 2) as 1) is trivial.
Before continuing, it is noteworthy that for some validator's vote account `a`,
the number of local confirmations for that validator on slot `s` is
`v.num_confirmations`, where `v` is the smallest vote in the stack of votes
`a.votes` such that `v.slot >= s` (i.e. there is no need to look at any
votes > v as the number of confirmations will be lower).
Now more specifically, we augment the above computation to:
```
let output: HashMap<b, StakeLockout> = HashMap::new();
let fork_commitment_cache = ForkCommitmentCache::default();
for vote_account in b.vote_accounts {
// vote stack is sorted from oldest vote to newest vote
for (v1, v2) in vote_account.vote_stack.windows(2) {
for a in ancestors(v1).difference(ancestors(v2)) {
f'(*output.get_mut(a), *fork_commitment_cache.get_mut(a), vote_account, v);
}
}
}
```
where `f'` is defined as:
```
fn f`(
stake_lockout: &mut StakeLockout,
some_ancestor: &mut BlockCommitment,
vote_account: VoteAccount,
v: Vote, total_stake: u64
){
f(stake_lockout, vote_account, v);
*some_ancestor.commitment[v.num_confirmations] += vote_account.stake;
}
```

View File

@@ -1,14 +0,0 @@
# Attack Vectors
**Subject to change.**
## Colluding validation and replication clients
A colluding validation-client, may take the strategy to mark PoReps from non-colluding archiver nodes as invalid as an attempt to maximize the rewards for the colluding archiver nodes. In this case, it isnt feasible for the offended-against archiver nodes to petition the network for resolution as this would result in a network-wide vote on each offending PoRep and create too much overhead for the network to progress adequately. Also, this mitigation attempt would still be vulnerable to a &gt;= 51% staked colluder.
Alternatively, transaction fees from submitted PoReps are pooled and distributed across validation-clients in proportion to the number of valid PoReps discounted by the number of invalid PoReps as voted by each validator-client. Thus invalid votes are directly dis-incentivized through this reward channel. Invalid votes that are revealed by archiver nodes as fishing PoReps, will not be discounted from the payout PoRep count.
Another collusion attack involves a validator-client who may take the strategy to ignore invalid PoReps from colluding archiver and vote them as valid. In this case, colluding archiver-clients would not have to store the data while still receiving rewards for validated PoReps. Additionally, colluding validator nodes would also receive rewards for validating these PoReps. To mitigate this attack, validators must randomly sample PoReps corresponding to the ledger block they are validating and because of this, there will be multiple validators that will receive the colluding archivers invalid submissions. These non-colluding validators will be incentivized to mark these PoReps as invalid as they have no way to determine whether the proposed invalid PoRep is actually a fishing PoRep, for which a confirmation vote would result in the validators stake being slashed.
In this case, the proportion of time a colluding pair will be successful has an upper limit determined by the % of stake of the network claimed by the colluding validator. This also sets bounds to the value of such an attack. For example, if a colluding validator controls 10% of the total validator stake, transaction fees will be lost \(likely sent to mining pool\) by the colluding archiver 90% of the time and so the attack vector is only profitable if the per-PoRep reward at least 90% higher than the average PoRep transaction fee. While, probabilistically, some colluding archiver-client PoReps will find their way to colluding validation-clients, the network can also monitor rates of paired \(validator + archiver\) discrepancies in voting patterns and censor identified colluders in these cases.

View File

@@ -1,14 +0,0 @@
# Economic Sustainability
**Subject to change.**
Long term economic sustainability is one of the guiding principles of Solanas economic design. While it is impossible to predict how decentralized economies will develop over time, especially economies with flexible decentralized governances, we can arrange economic components such that, under certain conditions, a sustainable economy may take shape in the long term. In the case of Solanas network, these components take the form of token issuance \(via inflation\) and token burning.
The dominant remittances from the Solana mining pool are validator and archiver rewards. The disinflationary mechanism is a flat, protocol-specified and adjusted, % of each transaction fee.
The Archiver rewards are to be delivered to archivers as a portion of the network inflation after successful PoRep validation. The per-PoRep reward amount is determined as a function of the total network storage redundancy at the time of the PoRep validation and the network goal redundancy. This function is likely to take the form of a discount from a base reward to be delivered when the network has achieved and maintained its goal redundancy. An example of such a reward function is shown in **Figure 3**
**Figure 3**: Example PoRep reward design as a function of global network storage redundancy.
In the example shown in Figure 1, multiple per PoRep base rewards are explored \(as a % of Tx Fee\) to be delivered when the global ledger replication redundancy meets 10X. When the global ledger replication redundancy is less than 10X, the base reward is discounted as a function of the square of the ratio of the actual ledger replication redundancy to the goal redundancy \(i.e. 10X\).

View File

@@ -1,6 +0,0 @@
# Replication-client Economics
**Subject to change.**
Replication-clients should be rewarded for providing the network with storage space. Incentivization of the set of archivers provides data security through redundancy of the historical ledger. Replication nodes are rewarded in proportion to the amount of ledger data storage provided, as proved by successfully submitting Proofs-of-Replication to the cluster.. These rewards are captured by generating and entering Proofs of Replication \(PoReps\) into the PoH stream which can be validated by Validation nodes as described above in the [Replication-validation Transaction Fees](../ed_validation_client_economics/ed_vce_replication_validation_transaction_fees.md) chapter.

View File

@@ -1,8 +0,0 @@
# Storage-replication Rewards
**Subject to change.**
Archiver-clients download, encrypt and submit PoReps for ledger block sections.3 PoReps submitted to the PoH stream, and subsequently validated, function as evidence that the submitting archiver client is indeed storing the assigned ledger block sections on local hard drive space as a service to the network. Therefore, archiver clients should earn protocol rewards proportional to the amount of storage, and the number of successfully validated PoReps, that they are verifiably providing to the network.
Additionally, archiver clients have the opportunity to capture a portion of slashed bounties \[TBD\] of dishonest validator clients. This can be accomplished by an archiver client submitting a verifiably false PoRep for which a dishonest validator client receives and signs as a valid PoRep. This reward incentive is to prevent lazy validators and minimize validator-archiver collusion attacks, more on this below.

View File

@@ -1,8 +0,0 @@
# Validation-client Economics
**Subject to change.**
Validator-clients are eligible to receive protocol-based \(i.e. inflation-based\) rewards issued via stake-based annual interest rates \(calculated per epoch\) by providing compute \(CPU+GPU\) resources to validate and vote on a given PoH state. These protocol-based rewards are determined through an algorithmic disinflationary schedule as a function of total amount of circulating tokens. The network is expected to launch with an annual inflation rate around 15%, set to decrease by 15% per year until a long-term stable rate of 1-2% is reached. These issuances are to be split and distributed to participating validators and archivers, with around 90% of the issued tokens allocated for validator rewards. Because the network will be distributing a fixed amount of inflation rewards across the stake-weighted valdiator set, any individual validator's interest rate will be a function of the amount of staked SOL in relation to the circulating SOL.
Additionally, validator clients may earn revenue through fees via state-validation transactions and Proof-of-Replication \(PoRep\) transactions. For clarity, we separately describe the design and motivation of these revenue distriubutions for validation-clients below: state-validation protocol-based rewards, state-validation transaction fees and rent, and PoRep-validation transaction fees.

View File

@@ -1,31 +0,0 @@
# State-validation Protocol-based Rewards
**Subject to change.**
Validator-clients have two functional roles in the Solana network:
* Validate \(vote\) the current global state of that PoH along with any Proofs-of-Replication \(see [Replication Client Economics](../ed_replication_client_economics/)\) that they are eligible to validate.
* Be elected as leader on a stake-weighted round-robin schedule during which time they are responsible for collecting outstanding transactions and Proofs-of-Replication and incorporating them into the PoH, thus updating the global state of the network and providing chain continuity.
Validator-client rewards for these services are to be distributed at the end of each Solana epoch. As previously discussed, compensation for validator-clients is provided via a protocol-based annual inflation rate dispersed in proportion to the stake-weight of each validator \(see below\) along with leader-claimed transaction fees available during each leader rotation. I.e. during the time a given validator-client is elected as leader, it has the opportunity to keep a portion of each transaction fee, less a protocol-specified amount that is destroyed \(see [Validation-client State Transaction Fees](ed_vce_state_validation_transaction_fees.md)\). PoRep transaction fees are also collected by the leader client and validator PoRep rewards are distributed in proportion to the number of validated PoReps less the number of PoReps that mismatch an archiver's challenge. \(see [Replication-client Transaction Fees](ed_vce_replication_validation_transaction_fees.md)\)
The effective protocol-based annual interest rate \(%\) per epoch received by validation-clients is to be a function of:
* the current global inflation rate, derived from the pre-determined dis-inflationary issuance schedule \(see [Validation-client Economics](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/ed_validartion_client_economics.md)\)
* the fraction of staked SOLs out of the current total circulating supply,
* the up-time/participation \[% of available slots that validator had opportunity to vote on\] of a given validator over the previous epoch.
The first factor is a function of protocol parameters only \(i.e. independent of validator behavior in a given epoch\) and results in a global validation reward schedule designed to incentivize early participation, provide clear montetary stability and provide optimal security in the network.
At any given point in time, a specific validator's interest rate can be determined based on the porportion of circulating supply that is staked by the network and the validator's uptime/activity in the previous epoch. For example, consider a hypothetical instance of the network with an initial circulating token supply of 250MM tokens with an additional 250MM vesting over 3 years. Additionally an inflation rate is specified at network launch of 7.5%, and a disinflationary schedule of 20% decrease in inflation rate per year \(the actual rates to be implemented are to be worked out during the testnet experimentation phase of mainnet launch\). With these broad assumptions, the 10-year inflation rate \(adjusted daily for this example\) is shown in **Figure 2**, while the total circulating token supply is illustrated in **Figure 3**. Neglected in this toy-model is the inflation supression due to the portion of each transaction fee that is to be destroyed.
![drawing](../../../.gitbook/assets/p_ex_schedule-3.png) \*\*Figure 2:\*\* In this example schedule, the annual inflation rate \[%\] reduces at around 20% per year, until it reaches the long-term, fixed, 1.5% rate.
![drawing](../../../.gitbook/assets/p_ex_supply-1%20%281%29.png) \*\*Figure 3:\*\* The total token supply over a 10-year period, based on an initial 250MM tokens with the disinflationary inflation schedule as shown in \*\*Figure 2\*\* Over time, the interest rate, at a fixed network staked percentage, will reduce concordant with network inflation. Validation-client interest rates are designed to be higher in the early days of the network to incentivize participation and jumpstart the network economy. As previously mentioned, the inflation rate is expected to stabalize near 1-2% which also results in a fixed, long-term, interest rate to be provided to validator-clients. This value does not represent the total interest available to validator-clients as transaction fees for state-validation and ledger storage replication \(PoReps\) are not accounted for here. Given these example parameters, annualized validator-specific interest rates can be determined based on the global fraction of tokens bonded as stake, as well as their uptime/activity in the previous epoch. For the purpose of this example, we assume 100% uptime for all validators and a split in interest-based rewards between validators and archiver nodes of 80%/20%. Additionally, the fraction of staked circulating supply is assummed to be constant. Based on these assumptions, an annualized validation-client interest rate schedule as a function of % circulating token supply that is staked is shown in\*\* Figure 4\*\*.
![drawing](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/.gitbook/assets/p_ex_interest.png)
**Figure 4:** Shown here are example validator interest rates over time, neglecting transaction fees, segmented by fraction of total circulating supply bonded as stake.
This epoch-specific protocol-defined interest rate sets an upper limit of _protocol-generated_ annual interest rate \(not absolute total interest rate\) possible to be delivered to any validator-client per epoch. The distributed interest rate per epoch is then discounted from this value based on the participation of the validator-client during the previous epoch.

View File

@@ -11,7 +11,7 @@ This document proposes an easy to use software install and updater that can be u
The easiest install method for supported platforms:
```bash
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.20.1/install/solana-install-init.sh | sh
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.0/install/solana-install-init.sh | sh
```
This script will check github for the latest tagged release and download and run the `solana-install-init` binary from there.
@@ -20,7 +20,7 @@ If additional arguments need to be specified during the installation, the follow
```bash
$ init_args=.... # arguments for `solana-install-init ...`
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.20.1/install/solana-install-init.sh | sh -s - ${init_args}
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.0/install/solana-install-init.sh | sh -s - ${init_args}
```
### Fetch and run a pre-built installer from a Github release
@@ -28,7 +28,7 @@ $ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.20.1/install
With a well-known release URL, a pre-built binary can be obtained for supported platforms:
```bash
$ curl -o solana-install-init https://github.com/solana-labs/solana/releases/download/v0.20.1/solana-install-init-x86_64-apple-darwin
$ curl -o solana-install-init https://github.com/solana-labs/solana/releases/download/v0.18.0/solana-install-init-x86_64-apple-darwin
$ chmod +x ./solana-install-init
$ ./solana-install-init --help
```

View File

@@ -1,6 +1,6 @@
# Leader-to-Validator Transition
A validator typically spends its time validating blocks. If, however, a staker delegates its stake to a validator, it will occasionally be selected as a _slot leader_. As a slot leader, the validator is responsible for producing blocks during an assigned _slot_. A slot has a duration of some number of preconfigured _ticks_. The duration of those ticks are estimated with a _PoH Recorder_ described later in this document.
A fullnode typically operates as a validator. If, however, a staker delegates its stake to a fullnode, it will occasionally be selected as a _slot leader_. As a slot leader, the fullnode is responsible for producing blocks during an assigned _slot_. A slot has a duration of some number of preconfigured _ticks_. The duration of those ticks are estimated with a _PoH Recorder_ described later in this document.
## BankFork

View File

@@ -0,0 +1,152 @@
# Passive Stake Delegation and Rewards
This design proposal focuses on the software architecture for the on-chain voting and staking programs. Incentives for staking is covered in [staking rewards](../proposals/staking-rewards.md).
The current architecture requires a vote for each delegated stake from the validator, and therefore does not scale to allow replicator clients to automatically delegate their rewards.
The design proposes a new set of programs for voting and stake delegation, The proposed programs allow many stake accounts to passively earn rewards with a single validator vote without permission or active involvement from the validator.
## Current Design Problems
In the current design each staker creates their own VoteState, and assigns a **delegate** in the VoteState that can submit votes. Since the validator has to actively vote for each stake delegated to it, validators can censor stakes by not voting for them.
The number of votes is equal to the number of stakers, and not the number of validators. Replicator clients are expected to delegate their replication rewards as they are earned, and therefore the number of stakes is expected to be large compared to the number of validators in a long running cluster.
## Proposed changes to the current design.
The general idea is that instead of the staker, the validator will own the VoteState program. In this proposal the VoteState program is there to track validator votes, count validator generated credits and to provide any additional validator specific state. The VoteState program is not aware of any stakes delegated to it, and has no staking weight.
The rewards generated are proportional to the amount of lamports staked. In this proposal stake state is stored as part of the StakeState program. This program is owned by the staker only. Lamports stored in this program are the stake. Unlike the current design, this program contains a new field to indicate which VoteState program the stake is delegated to.
### VoteState
VoteState is the current state of all the votes the **delegate** has submitted to the bank. VoteState contains the following state information:
* votes - The submitted votes data structure.
* credits - The total number of rewards this vote program has generated over its lifetime.
* 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 StakeState accounts. This is the percentage ceiling of the reward.
* Account::lamports - The accumulated lamports from the commission. These do not count as stakes.
* `authorized_vote_signer` - Only this identity is authorized to submit votes, and this field can only modified by this entity
### VoteInstruction::Initialize
* `account[0]` - RW - The VoteState
`VoteState::authorized_vote_signer` is initialized to `account[0]`
other VoteState members defaulted
### VoteInstruction::AuthorizeVoteSigner\(Pubkey\)
* `account[0]` - RW - The VoteState
`VoteState::authorized_vote_signer` is set to to `Pubkey`, instruction must by
signed by Pubkey
### StakeState
A StakeState takes one of two forms, StakeState::Stake and StakeState::MiningPool.
### StakeState::Stake
Stake is the current delegation preference of the **staker**. Stake contains the following state information:
* `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.
* `stake` - The actual activated stake.
* Account::lamports - Lamports available for staking, including any earned as rewards.
### StakeState::MiningPool
There are two approaches to the mining pool. The bank could allow the StakeState program to bypass the token balance check, or a program representing the mining pool could run on the network. To avoid a single network wide lock, the pool can be split into several mining pools. This design focuses on using a StakeState::MiningPool as the cluster wide mining pools.
* 256 StakeState::MiningPool are initialized, each with 1/256 number of mining pool
tokens stored as `Account::lamports`.
The stakes and the MiningPool are accounts that are owned by the same `Stake` program.
### StakeInstruction::DelegateStake\(stake\)
* `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 `stake`, as long as it's less than account\[0\].lamports
* `account[1]` - R - The VoteState instance.
### StakeInstruction::RedeemVoteCredits
The VoteState program and the StakeState programs maintain a lifetime counter of total rewards generated and claimed. Therefore an explicit `Clear` instruction is not necessary. When claiming rewards, the total lamports deposited into the StakeState and as validator commission is proportional to `VoteState::credits - StakeState::credits_observed`.
* `account[0]` - RW - The StakeState::MiningPool instance that will fulfill the
reward.
* `account[1]` - RW - The StakeState::Stake instance that is redeeming votes
credits.
* `account[2]` - R - The VoteState instance, must be the same as
`StakeState::voter_pubkey`
Reward is payed out for the difference between `VoteState::credits` to `StakeState::Delgate.credits_observed`, and `credits_observed` is updated to `VoteState::credits`. The commission is deposited into the `VoteState` token balance, and the reward is deposited to the `StakeState::Stake` token balance. The reward and the commission is weighted by the `StakeState::lamports` divided by total lamports staked.
The Staker or the owner of the Stake program sends a transaction with this instruction to claim the reward.
Any random MiningPool can be used to redeem the credits.
```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.
### Collecting network fees into the MiningPool
At the end of the block, before the bank is frozen, but after it processed all the transactions for the block, a virtual instruction is executed to collect the transaction fees.
* A portion of the fees are deposited into the leader's account.
* A portion of the fees are deposited into the smallest StakeState::MiningPool
account.
### Benefits
* Single vote for all the stakers.
* Clearing of the credit variable is not necessary for claiming rewards.
* Each delegated stake can claim its rewards independently.
* Commission for the work is deposited when a reward is claimed by the delegated stake.
This proposal would benefit from the `read-only` accounts proposal to allow for many rewards to be claimed concurrently.
## Passive Delegation
Any number of instances of StakeState::Stake programs can delegate to a single VoteState program without an interactive action from the identity controlling the VoteState program or submitting votes to the program.
The total stake allocated to a VoteState program can be calculated by the sum of all the StakeState programs that have the VoteState pubkey as the `StakeState::Stake::voter_pubkey`.
## Example Callflow
![Passive Staking Callflow](../.gitbook/assets/passive-staking-callflow%20%284%29.svg)
## Future work
Validators may want to split the stake delegated to them amongst many validator nodes since stake is used as weight in the network control and data planes. One way to implement this would be for the StakeState to delegate to a pool of validators instead of a single one.
Instead of a single `vote_pubkey` and `credits_observed` entry in the StakeState program, the program can be initialized with a vector of tuples.
```text
Voter {
voter_pubkey: Pubkey,
credits_observed: u64,
weight: u8,
}
```
* voters: Vec - Array of VoteState accounts that are voting rewards with
this stake.
A StakeState program would claim a fraction of the reward from each voter in the `voters` array, and each voter would be delegated a fraction of the stake.

View File

@@ -2,7 +2,7 @@
## Persistent Account Storage
The set of Accounts represent the current computed state of all the transactions that have been processed by a validator. Each validator needs to maintain this entire set. Each block that is proposed by the network represents a change to this set, and since each block is a potential rollback point the changes need to be reversible.
The set of Accounts represent the current computed state of all the transactions that have been processed by a fullnode. Each fullnode needs to maintain this entire set. Each block that is proposed by the network represents a change to this set, and since each block is a potential rollback point the changes need to be reversible.
Persistent storage like NVMEs are 20 to 40 times cheaper than DDR. The problem with persistent storage is that write and read performance is much slower than DDR and care must be taken in how data is read or written to. Both reads and writes can be split between multiple storage drives and accessed in parallel. This design proposes a data structure that allows for concurrent reads and concurrent writes of storage. Writes are optimized by using an AppendVec data structure, which allows a single writer to append while allowing access to many concurrent readers. The accounts index maintains a pointer to a spot where the account was appended to every fork, thus removing the need for explicit checkpointing of state.

View File

@@ -2,15 +2,15 @@
## Repair Service
The RepairService is in charge of retrieving missing shreds that failed to be delivered by primary communication protocols like Avalanche. It is in charge of managing the protocols described below in the `Repair Protocols` section below.
The RepairService is in charge of retrieving missing blobs that failed to be delivered by primary communication protocols like Avalanche. It is in charge of managing the protocols described below in the `Repair Protocols` section below.
## Challenges:
1\) Validators can fail to receive particular shreds due to network failures
1\) Validators can fail to receive particular blobs due to network failures
2\) Consider a scenario where blocktree contains the set of slots {1, 3, 5}. Then Blocktree receives shreds for some slot 7, where for each of the shreds b, b.parent == 6, so then the parent-child relation 6 -&gt; 7 is stored in blocktree. However, there is no way to chain these slots to any of the existing banks in Blocktree, and thus the `Shred Repair` protocol will not repair these slots. If these slots happen to be part of the main chain, this will halt replay progress on this node.
2\) Consider a scenario where blocktree contains the set of slots {1, 3, 5}. Then Blocktree receives blobs for some slot 7, where for each of the blobs b, b.parent == 6, so then the parent-child relation 6 -&gt; 7 is stored in blocktree. However, there is no way to chain these slots to any of the existing banks in Blocktree, and thus the `Blob Repair` protocol will not repair these slots. If these slots happen to be part of the main chain, this will halt replay progress on this node.
3\) Validators that find themselves behind the cluster by an entire epoch struggle/fail to catch up because they do not have a leader schedule for future epochs. If nodes were to blindly accept repair shreds in these future epochs, this exposes nodes to spam.
3\) Validators that find themselves behind the cluster by an entire epoch struggle/fail to catch up because they do not have a leader schedule for future epochs. If nodes were to blindly accept repair blobs in these future epochs, this exposes nodes to spam.
## Repair Protocols
@@ -18,32 +18,32 @@ The repair protocol makes best attempts to progress the forking structure of Blo
The different protocol strategies to address the above challenges:
1. Shred Repair \(Addresses Challenge \#1\): This is the most basic repair protocol, with the purpose of detecting and filling "holes" in the ledger. Blocktree tracks the latest root slot. RepairService will then periodically iterate every fork in blocktree starting from the root slot, sending repair requests to validators for any missing shreds. It will send at most some `N` repair reqeusts per iteration.
1. Blob Repair \(Addresses Challenge \#1\): This is the most basic repair protocol, with the purpose of detecting and filling "holes" in the ledger. Blocktree tracks the latest root slot. RepairService will then periodically iterate every fork in blocktree starting from the root slot, sending repair requests to validators for any missing blobs. It will send at most some `N` repair reqeusts per iteration.
Note: Validators will only accept shreds within the current verifiable epoch \(epoch the validator has a leader schedule for\).
Note: Validators will only accept blobs within the current verifiable epoch \(epoch the validator has a leader schedule for\).
2. Preemptive Slot Repair \(Addresses Challenge \#2\): The goal of this protocol is to discover the chaining relationship of "orphan" slots that do not currently chain to any known fork.
* Blocktree will track the set of "orphan" slots in a separate column family.
* RepairService will periodically make `RequestOrphan` requests for each of the orphans in blocktree.
`RequestOrphan(orphan)` request - `orphan` is the orphan slot that the requestor wants to know the parents of `RequestOrphan(orphan)` response - The highest shreds for each of the first `N` parents of the requested `orphan`
`RequestOrphan(orphan)` request - `orphan` is the orphan slot that the requestor wants to know the parents of `RequestOrphan(orphan)` response - The highest blobs for each of the first `N` parents of the requested `orphan`
On receiving the responses `p`, where `p` is some shred in a parent slot, validators will:
On receiving the responses `p`, where `p` is some blob in a parent slot, validators will:
* Insert an empty `SlotMeta` in blocktree for `p.slot` if it doesn't already exist.
* If `p.slot` does exist, update the parent of `p` based on `parents`
Note: that once these empty slots are added to blocktree, the `Shred Repair` protocol should attempt to fill those slots.
Note: that once these empty slots are added to blocktree, the `Blob Repair` protocol should attempt to fill those slots.
Note: Validators will only accept responses containing shreds within the current verifiable epoch \(epoch the validator has a leader schedule for\).
3. Repairmen \(Addresses Challenge \#3\): This part of the repair protocol is the primary mechanism by which new nodes joining the cluster catch up after loading a snapshot. This protocol works in a "forward" fashion, so validators can verify every shred that they receive against a known leader schedule.
Note: Validators will only accept responses containing blobs within the current verifiable epoch \(epoch the validator has a leader schedule for\).
3. Repairmen \(Addresses Challenge \#3\): This part of the repair protocol is the primary mechanism by which new nodes joining the cluster catch up after loading a snapshot. This protocol works in a "forward" fashion, so validators can verify every blob that they receive against a known leader schedule.
Each validator advertises in gossip:
* Current root
* The set of all completed slots in the confirmed epochs \(an epoch that was calculated based on a bank &lt;= current root\) past the current root
Observers of this gossip message with higher epochs \(repairmen\) send shreds to catch the lagging node up with the rest of the cluster. The repairmen are responsible for sending the slots within the epochs that are confrimed by the advertised `root` in gossip. The repairmen divide the responsibility of sending each of the missing slots in these epochs based on a random seed \(simple shred.index iteration by N, seeded with the repairman's node\_pubkey\). Ideally, each repairman in an N node cluster \(N nodes whose epochs are higher than that of the repairee\) sends 1/N of the missing shreds. Both data and coding shreds for missing slots are sent. Repairmen do not send shreds again to the same validator until they see the message in gossip updated, at which point they perform another iteration of this protocol.
Observers of this gossip message with higher epochs \(repairmen\) send blobs to catch the lagging node up with the rest of the cluster. The repairmen are responsible for sending the slots within the epochs that are confrimed by the advertised `root` in gossip. The repairmen divide the responsibility of sending each of the missing slots in these epochs based on a random seed \(simple blob.index iteration by N, seeded with the repairman's node\_pubkey\). Ideally, each repairman in an N node cluster \(N nodes whose epochs are higher than that of the repairee\) sends 1/N of the missing blobs. Both data and coding blobs for missing slots are sent. Repairmen do not send blobs again to the same validator until they see the message in gossip updated, at which point they perform another iteration of this protocol.
Gossip messages are updated every time a validator receives a complete slot within the epoch. Completed slots are detected by blocktree and sent over a channel to RepairService. It is important to note that we know that by the time a slot X is complete, the epoch schedule must exist for the epoch that contains slot X because WindowService will reject shreds for unconfirmed epochs. When a newly completed slot is detected, we also update the current root if it has changed since the last update. The root is made available to RepairService through Blocktree, which holds the latest root.
Gossip messages are updated every time a validator receives a complete slot within the epoch. Completed slots are detected by blocktree and sent over a channel to RepairService. It is important to note that we know that by the time a slot X is complete, the epoch schedule must exist for the epoch that contains slot X because WindowService will reject blobs for unconfirmed epochs. When a newly completed slot is detected, we also update the current root if it has changed since the last update. The root is made available to RepairService through Blocktree, which holds the latest root.

View File

@@ -1,55 +0,0 @@
# Snapshot Verification
## Problem
When a validator boots up from a snapshot, it needs a way to verify the account set matches what the rest of the network sees quickly. A potential
attacker could give the validator an incorrect state, and then try to convince it to accept a transaction that would otherwise be rejected.
## Solution
Currently the bank hash is derived from hashing the delta state of the accounts in a slot which is then combined with the previous bank hash value.
The problem with this is that the list of hashes will grow on the order of the number of slots processed by the chain and become a burden to both
transmit and verify successfully.
Another naive method could be to create a merkle tree of the account state. This has the downside that with each account update, the merkle tree
would have to be recomputed from the entire account state of all live accounts in the system.
To verify the snapshot, we do the following:
On account store of non-zero lamport accounts, we hash the following data:
* Account owner
* Account data
* Account pubkey
* Account lamports balance
* Fork the account is stored on
Use this resulting hash value as input to an expansion function which expands the hash value into an image value.
The function will create a 440 byte block of data where the first 32 bytes are the hash value, and the next 440 - 32 bytes are
generated from a Chacha RNG with the hash as the seed.
The account images are then combined with xor. The previous account value will be xored into the state and the new account value also xored into the state.
Voting and sysvar hash values occur with the hash of the resulting full image value.
On validator boot, when it loads from a snapshot, it would verify the hash value with the accounts set. It would then
use SPV to display the percentage of the network that voted for the hash value given.
The resulting value can be verified by a validator to be the result of xoring all current account states together.
A snapshot must be purged of zero lamport accounts before creation and during verify since the zero lamport accounts do not affect the hash value but may cause
a validator bank to read that an account is not present when it really should be.
An attack on the xor state could be made to influence its value:
Thus the 440 byte image size comes from this paper, avoiding xor collision with 0 \(or thus any other given bit pattern\): \[[https://link.springer.com/content/pdf/10.1007%2F3-540-45708-9\_19.pdf](https://link.springer.com/content/pdf/10.1007%2F3-540-45708-9_19.pdf)\]
The math provides 128 bit security in this case:
```text
O(k * 2^(n/(1+lg(k)))
k=2^40 accounts
n=440
2^(40) * 2^(448 * 8 / 41) ~= O(2^(128))
```

View File

@@ -12,7 +12,7 @@ For brevity this design assumes that a single voter with a stake is deployed as
## Time
The Solana cluster generates a source of time via a Verifiable Delay Function we are calling [Proof of History](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/book/src/synchronization.md).
The Solana cluster generates a source of time via a Verifiable Delay Function we are calling [Proof of History](https://github.com/solana-labs/solana/tree/6b18db969dd1616eff07de35e7b823c75339fea8/book/src/book/src/synchronization.md).
Proof of History is used to create a deterministic round robin schedule for all the active leaders. At any given time only 1 leader, which can be computed from the ledger itself, can propose a fork. For more details, see [fork generation](../cluster/fork-generation.md) and [leader rotation](../cluster/leader-rotation.md).
@@ -109,7 +109,7 @@ When evaluating multiple forks, each validator should use the following rules:
3. Pick the fork that has the greatest amount of cluster transaction fees.
4. Pick the latest fork in terms of PoH.
Cluster transaction fees are fees that are deposited to the mining pool as described in the [Staking Rewards](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/book/src/staking-rewards.md) section.
Cluster transaction fees are fees that are deposited to the mining pool as described in the [Staking Rewards](https://github.com/solana-labs/solana/tree/6b18db969dd1616eff07de35e7b823c75339fea8/book/src/book/src/staking-rewards.md) section.
## PoH ASIC Resistance

213
book/src/installer.md Normal file
View File

@@ -0,0 +1,213 @@
## Cluster Software Installation and Updates
Currently users are required to build the solana cluster software themselves
from the git repository and manually update it, which is error prone and
inconvenient.
This document proposes an easy to use software install and updater that can be
used to deploy pre-built binaries for supported platforms. Users may elect to
use binaries supplied by Solana or any other party they trust. Deployment of
updates is managed using an on-chain update manifest program.
### Motivating Examples
#### Fetch and run a pre-built installer using a bootstrap curl/shell script
The easiest install method for supported platforms:
```bash
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.0/install/solana-install-init.sh | sh
```
This script will check github for the latest tagged release and download and run the
`solana-install-init` binary from there.
If additional arguments need to be specified during the installation, the
following shell syntax is used:
```bash
$ init_args=.... # arguments for `solana-install-init ...`
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.0/install/solana-install-init.sh | sh -s - ${init_args}
```
#### Fetch and run a pre-built installer from a Github release
With a well-known release URL, a pre-built binary can be obtained for supported
platforms:
```bash
$ curl -o solana-install-init https://github.com/solana-labs/solana/releases/download/v0.18.0/solana-install-init-x86_64-apple-darwin
$ chmod +x ./solana-install-init
$ ./solana-install-init --help
```
#### Build and run the installer from source
If a pre-built binary is not available for a given platform, building the
installer from source is always an option:
```bash
$ git clone https://github.com/solana-labs/solana.git
$ cd solana/install
$ cargo run -- --help
```
#### Deploy a new update to a cluster
Given a solana release tarball (as created by `ci/publish-tarball.sh`) that has already been uploaded to a publicly accessible URL,
the following commands will deploy the update:
```bash
$ solana-keygen new -o update-manifest.json # <-- only generated once, the public key is shared with users
$ solana-install deploy http://example.com/path/to/solana-release.tar.bz2 update-manifest.json
```
#### Run a validator node that auto updates itself
```bash
$ solana-install init --pubkey 92DMonmBYXwEMHJ99c9ceRSpAmk9v6i3RdvDdXaVcrfj # <-- pubkey is obtained from whoever is deploying the updates
$ export PATH=~/.local/share/solana-install/bin:$PATH
$ solana-keygen ... # <-- runs the latest solana-keygen
$ solana-install run solana-validator ... # <-- runs a validator, restarting it as necesary when an update is applied
```
### On-chain Update Manifest
An update manifest is used to advertise the deployment of new release tarballs
on a solana cluster. The update manifest is stored using the `config` program,
and each update manifest account describes a logical update channel for a given
target triple (eg, `x86_64-apple-darwin`). The account public key is well-known
between the entity deploying new updates and users consuming those updates.
The update tarball itself is hosted elsewhere, off-chain and can be fetched from
the specified `download_url`.
```rust,ignore
use solana_sdk::signature::Signature;
/// Information required to download and apply a given update
pub struct UpdateManifest {
pub timestamp_secs: u64, // When the release was deployed in seconds since UNIX EPOCH
pub download_url: String, // Download URL to the release tar.bz2
pub download_sha256: String, // SHA256 digest of the release tar.bz2 file
}
/// Userdata of an Update Manifest program Account.
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct SignedUpdateManifest {
pub manifest: UpdateManifest,
pub manifest_signature: Signature,
}
```
Note that the `manifest` field itself contains a corresponding signature
(`manifest_signature`) to guard against man-in-the-middle attacks between the
`solana-install` tool and the solana cluster RPC API.
To guard against rollback attacks, `solana-install` will refuse to install an
update with an older `timestamp_secs` than what is currently installed.
### Release Archive Contents
A release archive is expected to be a tar file compressed with
bzip2 with the following internal structure:
* `/version.yml` - a simple YAML file containing the field `"target"` - the
target tuple. Any additional fields are ignored.
* `/bin/` -- directory containing available programs in the release.
`solana-install` will symlink this directory to
`~/.local/share/solana-install/bin` for use by the `PATH` environment
variable.
* `...` -- any additional files and directories are permitted
### solana-install Tool
The `solana-install` tool is used by the user to install and update their cluster software.
It manages the following files and directories in the user's home directory:
* `~/.config/solana/install/config.yml` - user configuration and information about currently installed software version
* `~/.local/share/solana/install/bin` - a symlink to the current release. eg, `~/.local/share/solana-update/<update-pubkey>-<manifest_signature>/bin`
* `~/.local/share/solana/install/releases/<download_sha256>/` - contents of a release
#### Command-line Interface
```manpage
solana-install 0.16.0
The solana cluster software installer
USAGE:
solana-install [OPTIONS] <SUBCOMMAND>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-c, --config <PATH> Configuration file to use [default: .../Library/Preferences/solana/install.yml]
SUBCOMMANDS:
deploy deploys a new update
help Prints this message or the help of the given subcommand(s)
info displays information about the current installation
init initializes a new installation
run Runs a program while periodically checking and applying software updates
update checks for an update, and if available downloads and applies it
```
```manpage
solana-install-init
initializes a new installation
USAGE:
solana-install init [OPTIONS]
FLAGS:
-h, --help Prints help information
OPTIONS:
-d, --data_dir <PATH> Directory to store install data [default: .../Library/Application Support/solana]
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://testnet.solana.com:8899]
-p, --pubkey <PUBKEY> Public key of the update manifest [default: 9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp]
```
```manpage
solana-install-info
displays information about the current installation
USAGE:
solana-install info [FLAGS]
FLAGS:
-h, --help Prints help information
-l, --local only display local information, don't check the cluster for new updates
```
```manpage
solana-install-deploy
deploys a new update
USAGE:
solana-install deploy <download_url> <update_manifest_keypair>
FLAGS:
-h, --help Prints help information
ARGS:
<download_url> URL to the solana release archive
<update_manifest_keypair> Keypair file for the update manifest (/path/to/keypair.json)
```
```manpage
solana-install-update
checks for an update, and if available downloads and applies it
USAGE:
solana-install update
FLAGS:
-h, --help Prints help information
```
```manpage
solana-install-run
Runs a program while periodically checking and applying software updates
USAGE:
solana-install run <program_name> [program_arguments]...
FLAGS:
-h, --help Prints help information
ARGS:
<program_name> program to run
<program_arguments>... arguments to supply to the program
The program will be restarted upon a successful software update
```

View File

@@ -0,0 +1,25 @@
# Instructions
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

View File

@@ -0,0 +1,204 @@
# Inter-chain Transaction Verification Program
## Problem
Inter-chain applications are not new to the digital asset ecosystem; in fact, even
the smaller centralized exchanges still categorically dwarf all single chain dapps
put together in terms of users and volume. They command massive valuations and
have spent years effectively optimizing their core products for a broad range of
end users. However, their basic operations center around mechanisms that require
their users to unilaterally trust them, typically with little to no recourse
or protection from accidental loss. This has led to the broader digital asset
ecosystem being fractured along network lines because interoperability solutions typically:
* Are technically complex to fully implement
* Create unstable network scale incentive structures
* Require consistent and high level cooperation between stakeholders
## Proposed Solution
Simple Payment Verification (SPV) is a generic term for a range of different
methodologies used by light clients on most major blockchain networks to verify
aspects of the network state without the burden of fully storing and maintaining
the chain itself. In most cases, this means relying on a form of hash tree to
supply a proof of the presence of a given transaction in a certain block by
comparing against a root hash in that blocks header or equivalent. This allows
a light client or wallet to reach a probabilistic level of certainty about
on-chain events by itself with a minimum of trust required with regard to network nodes.
Traditionally the process of assembling and validating these proofs is carried
out off chain by nodes, wallets, or other clients, but it also offers a potential
mechanism for inter-chain state verification. However, by moving the capability
to validate SPV proofs on-chain as a smart contract while leveraging the archival
properties inherent to the blockchain, it is possible to construct a system for
programmatically detecting and verifying transactions on other networks without
the involvement of any type of trusted oracle or complex multi-stage consensus
mechanism. This concept is broadly generalisable to any network with an SPV
mechanism and can even be operated bilaterally on other smart contract platforms,
opening up the possibility of cheap, fast, inter-chain transfer of value without
relying on collateral, hashlocks, or trusted intermediaries.
Opting to take advantage of well established and developmentally stable mechanisms
already common to all major blockchains allows SPV based interoperability solutions
to be dramatically simpler than orchestrated multi-stage approaches. As part of
this, they dispense with the need for widely agreed upon cross chain communication
standards and the large multi-party organizations that write them in favor of a
set of discrete contract-based services that can be easily utilized by caller
contracts through a common abstraction format. This will set the groundwork for
a broad range of dapps and contracts able to interoperate across the variegated
and every growing platform ecosystem.
## Terminology
SPV Program - Client-facing interface for the inter-chain SPV system, manages participant roles.
SPV Engine - Validates transaction proofs, subset of the SPV Program.
Client - The caller to the SPV Program, typically another solana contract.
Prover - Party who generates proofs for transactions and submits them to the SPV Program.
Transaction Proof - Created by Provers, contains a merkle proof, transaction, and blockheader reference.
Merkle Proof - Basic SPV proof that validates the presence of a transaction in a certain block.
Block Header - Represents the basic parameters and relative position of a given block.
Proof Request - An order placed by a client for verification of transaction(s) by provers.
Header Store - A data structure for storing and referencing ranges of block headers in proofs.
Client Request - Transaction from the client to the SPV Program to trigger creation of a Proof Request.
Sub-account - A Solana account owned by another contract account, without its own private key.
## Service
SPV Programs run as contracts deployed on the Solana network and maintain a type
of public marketplace for SPV proofs that allows any party to submit both requests
for proofs as well as proofs themselves for verification in response to requests.
There will be multiple SPV Program instances active at any given time, at least
one for each connected external network and potentially multiple instances per
network. SPV program instances will be relatively consistent in their high level
API and feature sets with some variation between currency platforms (Bitcoin,
Litecoin) and smart contract platforms owing to the potential for verification of
network state changes beyond simply transactions. In every case regardless of
network, the SPV Program relies on an internal component called an SPV engine to
provide stateless verification of the actual SPV proofs upon which the higher
level client facing features and api are built. The SPV engine requires a
network specific implementation, but allows easy extension of the larger
inter-chain ecosystem by any team who chooses to carry out that implementation
and drop it into the standard SPV program for deployment.
For purposes of Proof Requests, the requester is referred to as the program client,
which in most if not all cases will be another Solana Contract. The client can
choose to submit a request pertaining to a specific transaction or to include a
broader filter that can apply to any of a range of parameters of a transaction
including its inputs, outputs, and amount. For example, A client could submit a
request for any transaction sent from a given address A to address B with the
amount X after a certain time. This structure can be used in a range of
applications, such as verifying a specific intended payment in the case of an
atomic swap or detecting the movement of collateral assets for a loan.
Following submission of a Client Request, assuming that it is successfully
validated, a proof request account is created by the SPV program to track the
progress of the request. Provers use the account to specify the request they
intend to fill in the proofs they submit for validation, at which point the
SPV program validates those proofs and if successful, saves them to the account
data of the request account. Clients can monitor the status of their requests
and see any applicable transactions alongside their proofs by querying the
account data of the request account. In future iterations when supported by
Solana, this process will be simplified by contracts publishing events rather
than requiring a polling style process as described.
## Implementation
The Solana Inter-chain SPV mechanism consists of the following components and participants:
#### SPV engine
A contract deployed on Solana which statelessly verifies SPV proofs for the caller.
It takes as arguments for validation:
* An SPV proof in the correct format of the blockchain associated with the program
* Reference(s) to the relevant block headers to compare that proof against
* The necessary parameters of the transaction to verify
If the proof in question is successfully validated, the SPV program saves proof
of that verification to the request account, which can be saved by the caller to
its account data or otherwise handled as necessary. SPV programs also expose
utilities and structs used for representation and validation of headers,
transactions, hashes, etc. on a chain by chain basis.
#### SPV program
A contract deployed on Solana which coordinates and intermediates the interaction
between Clients and Provers and manages the validation of requests, headers,
proofs, etc. It is the primary point of access for Client contracts to access the
inter-chain. SPV mechanism. It offers the following core features:
* Submit Proof Request - allows client to place a request for a specific proof or set of proofs
* Cancel Proof Request - allows client to invalidate a pending request
* Fill Proof Request - used by Provers to submit for validation a proof corresponding to a given Proof Request
The SPV program maintains a publicly available listing of valid pending Proof
Requests in its account data for the benefit of the Provers, who monitor it and
enclose references to target requests with their submitted proofs.
#### Proof Request
A message sent by the Client to the SPV engine denoting a request for a proof of
a specific transaction or set of transactions. Proof Requests can either manually
specify a certain transaction by its hash or can elect to submit a filter that
matches multiple transactions or classes of transactions. For example, a filter
matching “any transaction from address xxx to address yyy” could be used to detect
payment of a debt or settlement of an inter-chain swap. Likewise, a filter matching
“any transaction from address xxx” could be used by a lending or synthetic token
minting contract to monitor and react to changes in collateralization. Proof
Requests are sent with a fee, which is disbursed by the SPV engine contract to
the appropriate Prover once a proof matching that request is validated.
#### Request Book
The public listing of valid, open Proof Requests available to provers to fill or
for clients to cancel. Roughly analogous to an orderbook in an exchange, but with
a single type of listing rather than two separate sides. It is stored in the
account data of the SPV program.
#### Proof
A proof of the presence of a given transaction in the blockchain in question.
Proofs encompass both the actual merkle proof and reference(s) to a chain of valid
sequential block headers. They are constructed and submitted by Provers in
accordance with the specifications of the publicly available Proof Requests
hosted on the request book by the SPV program. Upon Validation, they are saved
to the account data of the relevant Proof Request, which can be used by the
Client to monitor the state of the request.
#### Client
The originator of a request for a transaction proof. Clients will most often be
other contracts as parts of dapps or specific financial products like loans,
swaps, escrow, etc. The client in any given verification process cycle initially
submits a ClientRequest which communicates the parameters and fee and if
successfully validated, results in the creation of a Proof Request account by
the SPV program. The Client may also submit a CancelRequest referencing an active
Proof Request in order to denote it as invalid for purposes of proof submission.
#### Prover
The submitter of a proof that fills a Proof Request. Provers monitor the request
book of the SPV program for outstanding Proof Requests and generate matching
proofs, which they submit to the SPV program for validation. If the proof is
accepted, the fee associated with the Proof Request in question is disbursed to
the Prover. Provers typically operate as Solana Blockstreamer nodes that also
have access to a Bitcoin node, which they use for purposes of constructing proofs
and accessing block headers.
#### Header Store
An account-based data structure used to maintain block headers for the purpose
of inclusion in submitted proofs by reference to the header store account.
header stores can be maintained by independent entities, since header chain
validation is a component of the SPV program proof validation mechanism. Fees
that are paid out by Proof Requests to Provers are split between the submitter
of the merkle proof itself and the header store that is referenced in the
submitted proof. Due to the current inability to grow already allocated account
data capacity, the use case necessitates a data structure that can grow
indefinitely without rebalancing. Sub-accounts are accounts owned by the SPV
program without their own private keys that are used for storage by allocating
blockheaders to their account data. Multiple potential approaches to the
implementation of the header store system are feasible:
Store Headers in program sub-accounts indexed by Public address:
* Each sub-account holds one header and has a public key matching the blockhash
* Requires same number of account data lookups as confirmations per verification
* Limit on number of confirmations (15-20) via max transaction data ceiling
* No network-wide duplication of individual headers
Linked List of multiple sub-accounts storing headers:
* Maintain sequential index of storage accounts, many headers per storage account
* Max 2 account data lookups for >99.9% of verifications (1 for most)
* Compact sequential data address format allows any number of confirmations and fast lookups
* Facilitates network-wide header duplication inefficiencies

View File

@@ -32,7 +32,7 @@ In June of 2018, the team scaled up the technology to run on cloud-based network
A cluster is a set of computers that work together and can be viewed from the outside as a single system. A Solana cluster is a set of independently owned computers working together \(and sometimes against each other\) to verify the output of untrusted, user-submitted programs. A Solana cluster can be utilized any time a user wants to preserve an immutable record of events in time or programmatic interpretations of those events. One use is to track which of the computers did meaningful work to keep the cluster running. Another use might be to track the possession of real-world assets. In each case, the cluster produces a record of events called the ledger. It will be preserved for the lifetime of the cluster. As long as someone somewhere in the world maintains a copy of the ledger, the output of its programs \(which may contain a record of who possesses what\) will forever be reproducible, independent of the organization that launched it.
## What are SOLs?
## What are Sols?
A SOL is the name of Solana's native token, which can be passed to nodes in a Solana cluster in exchange for running an on-chain program or validating its output. The system may perform micropayments of fractional SOLs and a SOL may be split as many as 34 times. The fractional sol is called a _lamport_. It is named in honor of Solana's biggest technical influence, [Leslie Lamport](https://en.wikipedia.org/wiki/Leslie_Lamport). A lamport has a value of approximately 0.0000000000582 sol \(2^-34\).
A sol is the name of Solana's native token, which can be passed to nodes in a Solana cluster in exchange for running an on-chain program or validating its output. The Solana protocol defines that only 1 billion sols will ever exist, but that the system may perform micropayments of fractional sols, and that a sol may be split as many as 34 times. The fractional sol is called a _lamport_. It is named in honor of Solana's biggest technical influence, [Leslie Lamport](https://en.wikipedia.org/wiki/Leslie_Lamport). A lamport has a value of approximately 0.0000000000582 sol \(2^-34\).

View File

@@ -0,0 +1,3 @@
# JavaScript API
See [solana-web3](https://solana-labs.github.io/solana-web3.js/).

728
book/src/jsonrpc-api.md Normal file
View File

@@ -0,0 +1,728 @@
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://192.168.1.88:8899
RPC PubSub WebSocket Endpoint
---
**Default port:** 8900
eg. ws://localhost:8900, http://192.168.1.88:8900
Methods
---
* [confirmTransaction](#confirmtransaction)
* [getAccountInfo](#getaccountinfo)
* [getBalance](#getbalance)
* [getClusterNodes](#getclusternodes)
* [getEpochInfo](#getepochinfo)
* [getGenesisBlockhash](#getgenesisblockhash)
* [getLeaderSchedule](#getleaderschedule)
* [getProgramAccounts](#getprogramaccounts)
* [getRecentBlockhash](#getrecentblockhash)
* [getSignatureStatus](#getsignaturestatus)
* [getSlot](#getslot)
* [getSlotLeader](#getslotleader)
* [getSlotsPerSegment](#getslotspersegment)
* [getStorageTurn](#getstorageturn)
* [getStorageTurnRate](#getstorageturnrate)
* [getNumBlocksSinceSignatureConfirmation](#getnumblockssincesignatureconfirmation)
* [getTransactionCount](#gettransactioncount)
* [getTotalSupply](#gettotalsupply)
* [getVersion](#getversion)
* [getVoteAccounts](#getvoteaccounts)
* [requestAirdrop](#requestairdrop)
* [sendTransaction](#sendtransaction)
* [startSubscriptionChannel](#startsubscriptionchannel)
* [Subscription Websocket](#subscription-websocket)
* [accountSubscribe](#accountsubscribe)
* [accountUnsubscribe](#accountunsubscribe)
* [programSubscribe](#programsubscribe)
* [programUnsubscribe](#programunsubscribe)
* [signatureSubscribe](#signaturesubscribe)
* [signatureUnsubscribe](#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](#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 <ERR> [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](#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}
```

View File

@@ -0,0 +1,101 @@
# Leader to Leader Transition
This design describes how leaders transition production of the PoH ledger
between each other as each leader generates its own slot.
## Challenges
Current leader and the next leader are both racing to generate the final tick
for the current slot. The next leader may arrive at that slot while still
processing the current leader's entries.
The ideal scenario would be that the next leader generated its own slot right
after it was able to vote for the current leader. It is very likely that the
next leader will arrive at their PoH slot height before the current leader
finishes broadcasting the entire block.
The next leader has to make the decision of attaching its own block to the last
completed block, or wait to finalize the pending block. It is possible that the
next leader will produce a block that proposes that the current leader failed,
even though the rest of the network observes that block succeeding.
The current leader has incentives to start its slot as early as possible to
capture economic rewards. Those incentives need to be balanced by the leader's
need to attach its block to a block that has the most commitment from the rest
of the network.
## Leader timeout
While a leader is actively receiving entries for the previous slot, the leader
can delay broadcasting the start of its block in real time. The delay is
locally configurable by each leader, and can be dynamically based on the
previous leader's behavior. If the previous leader's block is confirmed by the
leader's TVU before the timeout, the PoH is reset to the start of the slot and
this leader produces its block immediately.
The downsides:
* Leader delays its own slot, potentially allowing the next leader more time to
catch up.
The upsides compared to guards:
* All the space in a block is used for entries.
* The timeout is not fixed.
* The timeout is local to the leader, and therefore can be clever. The leader's
heuristic can take into account turbine performance.
* This design doesn't require a ledger hard fork to update.
* The previous leader can redundantly transmit the last entry in the block to
the next leader, and the next leader can speculatively decide to trust it to
generate its block without verification of the previous block.
* The leader can speculatively generate the last tick from the last received
entry.
* The leader can speculatively process transactions and guess which ones are not
going to be encoded by the previous leader. This is also a censorship attack
vector. The current leader may withhold transactions that it receives from the
clients so it can encode them into its own slot. Once processed, entries can be
replayed into PoH quickly.
## Alternative design options
### Guard tick at the end of the slot
A leader does not produce entries in its block after the *penultimate tick*,
which is the last tick before the first tick of the next slot. The network
votes on the *last tick*, so the time difference between the *penultimate tick*
and the *last tick* is the forced delay for the entire network, as well as the
next leader before a new slot can be generated. The network can produce the
*last tick* from the *penultimate tick*.
If the next leader receives the *penultimate tick* before it produces its own
*first tick*, it will reset its PoH and produce the *first tick* from the
previous leader's *penultimate tick*. The rest of the network will also reset
its PoH to produce the *last tick* as the id to vote on.
The downsides:
* Every vote, and therefore confirmation, is delayed by a fixed timeout. 1 tick,
or around 100ms.
* Average case confirmation time for a transaction would be at least 50ms worse.
* It is part of the ledger definition, so to change this behavior would require
a hard fork.
* Not all the available space is used for entries.
The upsides compared to leader timeout:
* The next leader has received all the previous entries, so it can start
processing transactions without recording them into PoH.
* The previous leader can redundantly transmit the last entry containing the
*penultimate tick* to the next leader. The next leader can speculatively
generate the *last tick* as soon as it receives the *penultimate tick*, even
before verifying it.

167
book/src/leader-rotation.md Normal file
View File

@@ -0,0 +1,167 @@
# Leader Rotation
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*.
## Leader Schedule Rotation
A validator rejects blocks that are not signed by the *slot leader*. The list
of identities of all slot leaders is called a *leader schedule*. The leader
schedule is recomputed locally and periodically. It assigns slot leaders for a
duration of time called an _epoch_. The schedule must be computed far in advance
of the slots it assigns, such that the ledger state it uses to compute the
schedule is finalized. That duration is called the *leader schedule offset*.
Solana sets the offset to the duration of slots until the next epoch. That is,
the leader schedule for an epoch is calculated from the ledger state at the
start of the previous epoch. The offset of one epoch is fairly arbitrary and
assumed to be sufficiently long such that all validators will have finalized
their ledger state before the next schedule is generated. A cluster may choose
to shorten the offset to reduce the time between stake changes and leader
schedule updates.
While operating without partitions lasting longer than an epoch, the schedule
only needs to be generated when the root fork crosses the epoch boundary. Since
the schedule is for the next epoch, any new stakes committed to the root fork
will not be active until the next epoch. The block used for generating the
leader schedule is the first block to cross the epoch boundary.
Without a partition lasting longer than an epoch, the cluster will work as
follows:
1. A validator continuously updates its own root fork as it votes.
2. The validator updates its leader schedule each time the slot height crosses
an epoch boundary.
For example:
The epoch duration is 100 slots. The root fork is updated from fork computed at
slot height 99 to a fork computed at slot height 102. Forks with slots at height
100,101 were skipped because of failures. The new leader schedule is computed
using fork at slot height 102. It is active from slot 200 until it is updated
again.
No inconsistency can exist because every validator that is voting with the
cluster has skipped 100 and 101 when its root passes 102. All validators,
regardless of voting pattern, would be committing to a root that is either 102,
or a descendant of 102.
### Leader Schedule Rotation with Epoch Sized Partitions.
The duration of the leader schedule offset has a direct relationship to the
likelihood of a cluster having an inconsistent view of the correct leader
schedule.
Consider the following scenario:
Two partitions that are generating half of the blocks each. Neither is coming
to a definitive supermajority fork. Both will cross epoch 100 and 200 without
actually committing to a root and therefore a cluster wide commitment to a new
leader schedule.
In this unstable scenario, multiple valid leader schedules exist.
* A leader schedule is generated for every fork whose direct parent is in the
previous epoch.
* The leader schedule is valid after the start of the next epoch for descendant
forks until it is updated.
Each partition's schedule will diverge after the partition lasts more than an
epoch. For this reason, the epoch duration should be selected to be much much
larger then slot time and the expected length for a fork to be committed to
root.
After observing the cluster for a sufficient amount of time, the leader schedule
offset can be selected based on the median partition duration and its standard
deviation. For example, an offset longer then the median partition duration
plus six standard deviations would reduce the likelihood of an inconsistent
ledger schedule in the cluster to 1 in 1 million.
## Leader Schedule Generation at Genesis
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](tower-bft.md).
## Leader Schedule Generation Algorithm
Leader schedule is generated using a predefined seed. The process is as follows:
1. Periodically use the PoH tick height (a monotonically increasing counter) to
seed a stable pseudo-random algorithm.
2. At that height, sample the bank for all the staked accounts with leader
identities that have voted within a cluster-configured number of ticks. The
sample is called the *active set*.
3. Sort the active set by stake weight.
4. Use the random seed to select nodes weighted by stake to create a
stake-weighted ordering.
5. This ordering becomes valid after a cluster-configured number of ticks.
## Schedule Attack Vectors
### Seed
The seed that is selected is predictable but unbiasable. There is no grinding
attack to influence its outcome.
### Active Set
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
* 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.
### Staking
Leaders can censor new staking transactions or refuse to validate blocks with
new stakes. This attack is similar to censorship of validator votes.
### Validator operational key loss
Leaders and validators are expected to use ephemeral keys for operation, and
stake owners authorize the validators to do work with their stake via
delegation.
The cluster should be able to recover from the loss of all the ephemeral keys
used by leaders and validators, which could occur through a common software
vulnerability shared by all the nodes. Stake owners should be able to vote
directly co-sign a validator vote even though the stake is currently delegated
to a validator.
## Appending Entries
The lifetime of a leader schedule is called an *epoch*. The epoch is split into
*slots*, where each slot has a duration of `T` PoH ticks.
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.

View File

@@ -0,0 +1,84 @@
# Leader-to-Validator Transition
A fullnode typically operates as a validator. If, however, a staker delegates
its stake to a fullnode, it will occasionally be selected as a *slot leader*.
As a slot leader, the fullnode is responsible for producing blocks during an
assigned *slot*. A slot has a duration of some number of preconfigured *ticks*.
The duration of those ticks are estimated with a *PoH Recorder* described later
in this document.
## BankFork
BankFork tracks changes to the bank state over a specific slot. Once the final
tick has been registered the state is frozen. Any attempts to write to are
rejected.
## Validator
A validator operates on many different concurrent forks of the bank state until
it generates a PoH hash with a height within its leader slot.
## Slot Leader
A slot leader builds blocks on top of only one fork, the one it last voted on.
## PoH Recorder
Slot leaders and validators use a PoH Recorder for both estimating slot height
and for recording transactions.
### PoH Recorder when Validating
The PoH Recorder acts as a simple VDF when validating. It tells the validator
when it needs to switch to the slot leader role. Every time the validator votes
on a fork, it should use the fork's latest block id to re-seed the VDF.
Re-seeding solves two problems. First, it synchronizes its VDF to the leader's,
allowing it to more accurately determine when its leader slot begins. Second,
if the previous leader goes down, all wallclock time is accounted for in the
next leader's PoH stream. For example, if one block is missing when the leader
starts, the block it produces should have a PoH duration of two blocks. The
longer duration ensures the following leader isn't attempting to snip all the
transactions from the previous leader's slot.
### PoH Recorder when Leading
A slot leader use the PoH Recorder to record transactions, locking their
positions in time. The PoH hash must be derived from a previous leader's last
block. If it isn't, its block will fail PoH verification and be rejected by
the cluster.
The PoH Recorder also serves to inform the slot leader when its slot is over.
The leader needs to take care not to modify its bank if recording the
transaction would generate a PoH height outside its designated slot. The
leader, therefore, should not commit account changes until after it generates
the entry's PoH hash. When the PoH height falls outside its slot any
transactions in its pipeline may be dropped or forwarded to the next leader.
Forwarding is preferred, as it would minimize network congestion, allowing the
cluster to advertise higher TPS capacity.
## Validator Loop
The PoH Recorder manages the transition between modes. Once a ledger is
replayed, the validator can run until the recorder indicates it should be
the slot leader. As a slot leader, the node can then execute and record
transactions.
The loop is synchronized to PoH and does a synchronous start and stop of the
slot leader functionality. After stopping, the validator's TVU should find
itself in the same state as if a different leader had sent it the same block.
The following is pseudocode for the loop:
1. Query the LeaderScheduler for the next assigned slot.
2. Run the TVU over all the forks.
1. TVU will send votes to what it believes is the "best" fork.
2. After each vote, restart the PoH Recorder to run until the next assigned
slot.
3. When time to be a slot leader, start the TPU. Point it to the last fork the
TVU voted on.
4. Produce entries until the end of the slot.
1. For the duration of the slot, the TVU must not vote on other forks.
2. After the slot ends, the TPU freezes its BankFork. After freezing,
the TVU may resume voting.
5. Goto 1.

View File

@@ -0,0 +1,142 @@
# Ledger Replication
Replication behavior yet to be implemented.
### Storage epoch
The storage epoch should be the number of slots which results in around 100GB-1TB of
ledger to be generated for replicators to store. Replicators will start storing ledger
when a given fork has a high probability of not being rolled back.
### Validator behavior
3. Every NUM\_KEY\_ROTATION\_TICKS it also validates samples received from
replicators. It signs the PoH hash at that point and uses the following
algorithm with the signature as the input:
- The low 5 bits of the first byte of the signature creates an index into
another starting byte of the signature.
- The validator then looks at the set of storage proofs where the byte of
the proof's sha state vector starting from the low byte matches exactly
with the chosen byte(s) of the signature.
- If the set of proofs is larger than the validator can handle, then it
increases to matching 2 bytes in the signature.
- Validator continues to increase the number of matching bytes until a
workable set is found.
- It then creates a mask of valid proofs and fake proofs and sends it to
the leader. This is a storage proof confirmation transaction.
5. After a lockout period of NUM\_SECONDS\_STORAGE\_LOCKOUT seconds, the
validator then submits a storage proof claim transaction which then causes the
distribution of the storage reward if no challenges were seen for the proof to
the validators and replicators party to the proofs.
### Replicator behavior
9. The replicator then generates another set of offsets which it submits a fake
proof with an incorrect sha state. It can be proven to be fake by providing the
seed for the hash result.
- A fake proof should consist of a replicator hash of a signature of a PoH
value. That way when the replicator reveals the fake proof, it can be
verified on chain.
10. The replicator monitors the ledger, if it sees a fake proof integrated, it
creates a challenge transaction and submits it to the current leader. The
transacation proves the validator incorrectly validated a fake storage proof.
The replicator is rewarded and the validator's staking balance is slashed or
frozen.
### Storage proof contract logic
Each replicator and validator will have their own storage account. The validator's
account would be separate from their gossip id similiar to their vote account.
These should be implemented as two programs one which handles the validator as the keysigner
and one for the replicator. In that way when the programs reference other accounts, they
can check the program id to ensure it is a validator or replicator account they are
referencing.
#### SubmitMiningProof
```rust,ignore
SubmitMiningProof {
slot: u64,
sha_state: Hash,
signature: Signature,
};
keys = [replicator_keypair]
```
Replicators create these after mining their stored ledger data for a certain hash value.
The slot is the end slot of the segment of ledger they are storing, the sha\_state
the result of the replicator using the hash function to sample their encrypted ledger segment.
The signature is the signature that was created when they signed a PoH value for the
current storage epoch. The list of proofs from the current storage epoch should be saved
in the account state, and then transfered to a list of proofs for the previous epoch when
the epoch passes. In a given storage epoch a given replicator should only submit proofs
for one segment.
The program should have a list of slots which are valid storage mining slots.
This list should be maintained by keeping track of slots which are rooted slots in which a significant
portion of the network has voted on with a high lockout value, maybe 32-votes old. Every SLOTS\_PER\_SEGMENT
number of slots would be added to this set. The program should check that the slot is in this set. The set can
be maintained by receiving a AdvertiseStorageRecentBlockHash and checking with its bank/Tower BFT state.
The program should do a signature verify check on the signature, public key from the transaction submitter and the message of
the previous storage epoch PoH value.
#### ProofValidation
```rust,ignore
ProofValidation {
proof_mask: Vec<ProofStatus>,
}
keys = [validator_keypair, replicator_keypair(s) (unsigned)]
```
A validator will submit this transaction to indicate that a set of proofs for a given
segment are valid/not-valid or skipped where the validator did not look at it. The
keypairs for the replicators that it looked at should be referenced in the keys so the program
logic can go to those accounts and see that the proofs are generated in the previous epoch. The
sampling of the storage proofs should be verified ensuring that the correct proofs are skipped by
the validator according to the logic outlined in the validator behavior of sampling.
The included replicator keys will indicate the the storage samples which are being referenced; the
length of the proof\_mask should be verified against the set of storage proofs in the referenced
replicator account(s), and should match with the number of proofs submitted in the previous storage
epoch in the state of said replicator account.
#### ClaimStorageReward
```rust,ignore
ClaimStorageReward {
}
keys = [validator_keypair or replicator_keypair, validator/replicator_keypairs (unsigned)]
```
Replicators and validators will use this transaction to get paid tokens from a program state
where SubmitStorageProof, ProofValidation and ChallengeProofValidations are in a state where
proofs have been submitted and validated and there are no ChallengeProofValidations referencing
those proofs. For a validator, it should reference the replicator keypairs to which it has validated
proofs in the relevant epoch. And for a replicator it should reference validator keypairs for which it
has validated and wants to be rewarded.
#### ChallengeProofValidation
```rust,ignore
ChallengeProofValidation {
proof_index: u64,
hash_seed_value: Vec<u8>,
}
keys = [replicator_keypair, validator_keypair]
```
This transaction is for catching lazy validators who are not doing the work to validate proofs.
A replicator will submit this transaction when it sees a validator has approved a fake SubmitMiningProof
transaction. Since the replicator is a light client not looking at the full chain, it will have to ask
a validator or some set of validators for this information maybe via RPC call to obtain all ProofValidations for
a certain segment in the previous storage epoch. The program will look in the validator account
state see that a ProofValidation is submitted in the previous storage epoch and hash the hash\_seed\_value and
see that the hash matches the SubmitMiningProof transaction and that the validator marked it as valid. If so,
then it will save the challenge to the list of challenges that it has in its state.
#### AdvertiseStorageRecentBlockhash
```rust,ignore
AdvertiseStorageRecentBlockhash {
hash: Hash,
slot: u64,
}
```
Validators and replicators will submit this to indicate that a new storage epoch has passed and that the
storage proofs which are current proofs should now be for the previous epoch. Other transactions should
check to see that the epoch that they are referencing is accurate according to current chain state.

View File

@@ -0,0 +1,219 @@
# Ledger Replication
At full capacity on a 1gbps network solana will generate 4 petabytes of data
per year. To prevent the network from centralizing around validators that have
to store the full data set this protocol proposes a way for mining nodes to
provide storage capacity for pieces of the data.
The basic idea to Proof of Replication is encrypting a dataset with a public
symmetric key using CBC encryption, then hash the encrypted dataset. The main
problem with the naive approach is that a dishonest storage node can stream the
encryption and delete the data as it's hashed. The simple solution is to periodically
regenerate the hash based on a signed PoH value. This ensures that all the data is present
during the generation of the proof and it also requires validators to have the
entirety of the encrypted data present for verification of every proof of every identity.
So the space required to validate is `number_of_proofs * data_size`
## Optimization with PoH
Our improvement on this approach is to randomly sample the encrypted segments
faster than it takes to encrypt, and record the hash of those samples into the
PoH ledger. Thus the segments stay in the exact same order for every PoRep and
verification can stream the data and verify all the proofs in a single batch.
This way we can verify multiple proofs concurrently, each one on its own CUDA
core. The total space required for verification is `1_ledger_segment +
2_cbc_blocks * number_of_identities` with core count equal to
`number_of_identities`. We use a 64-byte chacha CBC block size.
## Network
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.
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
We have the following constraints:
* Verification requires generating the CBC blocks. That requires space of 2
blocks per identity, and 1 CUDA core per identity for the same dataset. So as
many identities at once should be batched with as many proofs for those
identities verified concurrently for the same dataset.
* Validators will randomly sample the set of storage proofs to the set that
they can handle, and only the creators of those chosen proofs will be
rewarded. The validator can run a benchmark whenever its hardware configuration
changes to determine what rate it can validate storage proofs.
## Validation and Replication Protocol
### Constants
1. SLOTS\_PER\_SEGMENT: Number of slots in a segment of ledger data. The
unit of storage for a replicator.
2. NUM\_KEY\_ROTATION\_SEGMENTS: Number of segments after which replicators
regenerate their encryption keys and select a new dataset to store.
3. NUM\_STORAGE\_PROOFS: Number of storage proofs required for a storage proof
claim to be successfully rewarded.
4. RATIO\_OF\_FAKE\_PROOFS: Ratio of fake proofs to real proofs that a storage
mining proof claim has to contain to be valid for a reward.
5. NUM\_STORAGE\_SAMPLES: Number of samples required for a storage mining
proof.
6. NUM\_CHACHA\_ROUNDS: Number of encryption rounds performed to generate
encrypted state.
7. NUM\_SLOTS\_PER\_TURN: Number of slots that define a single storage epoch or
a "turn" of the PoRep game.
### Validator behavior
1. Validators join the network and begin looking for replicator accounts at each
storage epoch/turn boundary.
2. Every turn, Validators sign the PoH value at the boundary and use that signature
to randomly pick proofs to verify from each storage account found in the turn boundary.
This signed value is also submitted to the validator's storage account and will be used by
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 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).
At which point all validations during that turn are available for reward collection.
5. Any incorrect validations will be marked during the turn in between.
### Replicator behavior
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 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
replicator do extra wasted work. For many of the operations there are a number of options
depending on how paranoid a replicator is:
- (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) replicator can subscribe to an abbreviated transaction stream to
generate the information itself (assuming the slot is recent enough)
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
replicator mods the signature with the slot to get which segment to
replicate.
4. The replicator retrives the ledger by asking peer validators and
replicators. See 6.5.
5. The replicator then encrypts that segment with the key with chacha algorithm
in CBC mode with `NUM_CHACHA_ROUNDS` of encryption.
6. The replicator initializes a chacha rng with the a signed recent PoH value as
the seed.
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
offset value. Sampling the state should be faster than generating the encrypted
segment.
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 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 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 replicators and validators.
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.
### The PoRep Game
The Proof of Replication game has 4 primary stages. For each "turn" multiple PoRep
games can be in progress but each in a different stage.
The 4 stages of the PoRep Game are as follows:
1. Proof submission stage
- Replicators: submit as many proofs as possible during this stage
- Validators: No-op
2. Proof verification stage
- Replicators: No-op
- Validators: Select replicators and verify their proofs from the previous turn
3. Proof challenge stage
- Replicators: Submit the proof mask with justifications (for fake proofs submitted 2 turns ago)
- Validators: No-op
4. Reward collection stage
- Replicators: Collect rewards for 3 turns ago
- Validators: Collect rewards for 3 turns ago
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
1. Validators monitor the turns in the PoRep game and look at the rooted bank
at turn boundaries for any proofs.
2. Validators maintain a map of ledger segments and corresponding replicator public keys.
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 a replicator's network address (correlating it via cluster_info table).
The clients can then send repair requests to the replicator to retrieve segments.
3. Validators would need to invalidate this list every N turns.
## Sybil attacks
For any random seed, we force everyone to use a signature that is derived from
a PoH hash at the turn boundary. Everyone uses the same count, so the same PoH
hash is signed by every participant. The signatures are then each cryptographically
tied to the keypair, which prevents a leader from grinding on the resulting
value for more than 1 identity.
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 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, replicator can easily out them by
showing the initial state for the hash.
- If a validator marks real proofs as fake, no on-chain computation can be done
to distinguish who is correct. Rewards would have to rely on the results from
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 a replicator, since the validator does not know the
private key used to generate the encryption key, it cannot be the generator of
the proof.
## Reward incentives
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.
## Notes
* We can reduce the costs of verification of PoRep by using PoH, and actually
make it feasible to verify a large number of proofs for a global dataset.
* We can eliminate grinding by forcing everyone to sign the same PoH hash and
use the signatures as the seed
* 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.
* Replicator clients fish for lazy validators by submitting fake proofs that
they can prove are fake.
* To defend against Sybil client identities that try to store the same block we
force the clients to store for multiple rounds before receiving a reward.
* Validators should also get rewarded for validating submitted storage proofs
as incentive for storing the ledger. They can only validate proofs if they
are storing that slice of the ledger.

View File

@@ -0,0 +1,63 @@
# Managing Forks in the Ledger
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 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:
<img alt="Forks" src=".gitbook/assets/forks.svg" class="center"/>
The following sequences are *active forks*:
* {4, 2, 1}
* {5, 2, 1}
* {6, 3, 1}
* {7, 3, 1}
## Pruning and Squashing
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:
<img alt="Forks after pruning" src=".gitbook/assets/forks-pruned.svg" class="center"/>
The new root is 2, and any active forks that are not descendants from 2 are
pruned.
Alternatively, a vote on 6:
<img alt="Forks" src=".gitbook/assets/forks-pruned2.svg" class="center"/>
The tree remains with a root of 1, since the active fork starting at 6 is only
2 checkpoints from the root.

View File

@@ -0,0 +1,31 @@
# Performance Metrics
Solana cluster performance is measured as average number of transactions per second
that the network can sustain (TPS). And, how long it takes for a transaction to be
confirmed by super majority of the cluster (Confirmation Time).
Each cluster node maintains various counters that are incremented on certain events.
These counters are periodically uploaded to a cloud based database. Solana's metrics
dashboard fetches these counters, and computes the performance metrics and displays
it on the dashboard.
## TPS
Each node's bank runtime maintains a count of transactions that it has processed.
The dashboard first calculates the median count of transactions across all metrics
enabled nodes in the cluster. The median cluster transaction count is then averaged
over a 2 second period and displayed in the TPS time series graph. The dashboard also
shows the Mean TPS, Max TPS and Total Transaction Count stats which are all calculated from
the median transaction count.
## Confirmation Time
Each validator node maintains a list of active ledger forks that are visible to the node.
A fork is considered to be frozen when the node has received and processed all entries
corresponding to the fork. A fork is considered to be confirmed when it receives cumulative
super majority vote, and when one of its children forks is frozen.
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.

View File

@@ -0,0 +1,153 @@
# Persistent Account Storage
The set of Accounts represent the current computed state of all the transactions
that have been processed by a fullnode. Each fullnode needs to maintain this
entire set. Each block that is proposed by the network represents a change to
this set, and since each block is a potential rollback point the changes need to
be reversible.
Persistent storage like NVMEs are 20 to 40 times cheaper than DDR. The problem
with persistent storage is that write and read performance is much slower than
DDR and care must be taken in how data is read or written to. Both reads and
writes can be split between multiple storage drives and accessed in parallel.
This design proposes a data structure that allows for concurrent reads and
concurrent writes of storage. Writes are optimized by using an AppendVec data
structure, which allows a single writer to append while allowing access to many
concurrent readers. The accounts index maintains a pointer to a spot where the
account was appended to every fork, thus removing the need for explicit
checkpointing of state.
# AppendVec
AppendVec is a data structure that allows for random reads concurrent with a
single append-only writer. Growing or resizing the capacity of the AppendVec
requires exclusive access. This is implemented with an atomic `offset`, which
is updated at the end of a completed append.
The underlying memory for an AppendVec is a memory-mapped file. Memory-mapped
files allow for fast random access and paging is handled by the OS.
# Account Index
The account index is designed to support a single index for all the currently
forked Accounts.
```rust,ignore
type AppendVecId = usize;
type Fork = u64;
struct AccountMap(Hashmap<Fork, (AppendVecId, u64)>);
type AccountIndex = HashMap<Pubkey, AccountMap>;
```
The index is a map of account Pubkeys to a map of Forks and the location of the
Account data in an AppendVec. To get the version of an account for a specific Fork:
```rust,ignore
/// Load the account for the pubkey.
/// This function will load the account from the specified fork, falling back to the fork's parents
/// * fork - a virtual Accounts instance, keyed by Fork. Accounts keep track of their parents with Forks,
/// the persistent store
/// * pubkey - The Account's public key.
pub fn load_slow(&self, id: Fork, pubkey: &Pubkey) -> Option<&Account>
```
The read is satisfied by pointing to a memory-mapped location in the
`AppendVecId` at the stored offset. A reference can be returned without a copy.
## Root Forks
[Tower BFT](tower-bft.md) eventually selects a fork as a
root fork and the fork is squashed. A squashed/root fork cannot be rolled back.
When a fork is squashed, all accounts in its parents not already present in the
fork are pulled up into the fork by updating the indexes. Accounts with zero
balance in the squashed fork are removed from fork by updating the indexes.
An account can be *garbage-collected* when squashing makes it unreachable.
Three possible options exist:
* Maintain a HashSet<u64> of root forks. One is expected to be created every
second. The entire tree can be garbage-collected later. Alternatively, if
every fork keeps a reference count of accounts, garbage collection could occur
any time an index location is updated.
* Remove any pruned forks from the index. Any remaining forks lower in number
than the root are can be considered root.
* Scan the index, migrate any old roots into the new one. Any remaining forks
lower than the new root can be deleted later.
# Append-only Writes
All the updates to Accounts occur as append-only updates. For every account
update, a new version is stored in the AppendVec.
It is possible to optimize updates within a single fork by returning a mutable
reference to an already stored account in a fork. The Bank already tracks
concurrent access of accounts and guarantees that a write to a specific account
fork will not be concurrent with a read to an account at that fork. To support
this operation, AppendVec should implement this function:
```rust,ignore
fn get_mut(&self, index: u64) -> &mut T;
```
This API allows for concurrent mutable access to a memory region at `index`. It
relies on the Bank to guarantee exclusive access to that index.
# Garbage collection
As accounts get updated, they move to the end of the AppendVec. Once capacity
has run out, a new AppendVec can be created and updates can be stored there.
Eventually references to an older AppendVec will disappear because all the
accounts have been updated, and the old AppendVec can be deleted.
To speed up this process, it's possible to move Accounts that have not been
recently updated to the front of a new AppendVec. This form of garbage
collection can be done without requiring exclusive locks to any of the data
structures except for the index update.
The initial implementation for garbage collection is that once all the accounts in
an AppendVec become stale versions, it gets reused. The accounts are not updated
or moved around once appended.
# Index Recovery
Each bank thread has exclusive access to the accounts during append, since the
accounts locks cannot be released until the data is committed. But there is no
explicit order of writes between the separate AppendVec files. To create an
ordering, the index maintains an atomic write version counter. Each append to
the AppendVec records the index write version number for that append in the
entry for the Account in the AppendVec.
To recover the index, all the AppendVec files can be read in any order, and the
latest write version for every fork should be stored in the index.
# Snapshots
To snapshot, the underlying memory-mapped files in the AppendVec need to be
flushed to disk. The index can be written out to disk as well.
# Performance
* Append-only writes are fast. SSDs and NVMEs, as well as all the OS level
kernel data structures, allow for appends to run as fast as PCI or NVMe bandwidth
will allow (2,700 MB/s).
* Each replay and banking thread writes concurrently to its own AppendVec.
* Each AppendVec could potentially be hosted on a separate NVMe.
* Each replay and banking thread has concurrent read access to all the
AppendVecs without blocking writes.
* Index requires an exclusive write lock for writes. Single-thread performance
for HashMap updates is on the order of 10m per second.
* Banking and Replay stages should use 32 threads per NVMe. NVMes have
optimal performance with 32 concurrent readers or writers.

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