Compare commits

..

85 Commits

Author SHA1 Message Date
Dan Albert
f9d9c1fcbf Update book to SOLANA_RELEASE=v0.16.5 (#5154) 2019-07-17 14:20:49 -06:00
mergify[bot]
7c59c105cf Add weighted shuffle support for values upto u64::MAX (#5151) (#5152)
automerge

(cherry picked from commit 10d85f8366)
2019-07-17 13:12:05 -07:00
Tyera Eulberg
a8ea9f2738 Fix bench-tps funding math; make generate_keypairs() and fund_keys() algorithms consistent (#4841) (#5145)
* Fix funding math; make generate_keypairs and fund_keys consistent

* Add test, and fix inconsistencies it exposes

* De-pow math, and use assert_eq in tests for better failure msgs
2019-07-17 09:01:13 -07:00
Michael Vines
651f87a937 Show stake pubkey 2019-07-16 20:10:58 -07:00
mergify[bot]
88f8e2f332 Check harder on crates.io for recently published crates (#5136) (#5138)
automerge
2019-07-16 19:54:17 -07:00
mergify[bot]
a2cb289503 clear-config.sh now works with a secondary disk (#5135) (#5137)
automerge
2019-07-16 19:47:40 -07:00
Michael Vines
89bd9d5b72 Bump blockexplorer version 2019-07-16 19:44:42 -07:00
mergify[bot]
7edaaeb2a1 Improve validator-info CLI (#5121) (#5125)
automerge
2019-07-16 09:08:35 -07:00
Michael Vines
1c3ade80c2 Add missing dash 2019-07-16 07:28:26 -07:00
Dan Albert
3606d51507 Increment toml and cargo.ock to 0.16.5 (#5119) 2019-07-15 17:32:12 -06:00
Dan Albert
281fd88ea7 Update testnet doc to use latest release (#5118) 2019-07-15 17:11:42 -06:00
mergify[bot]
ee6b625c13 fix transaction_count (bp #5110) (#5111)
automerge
2019-07-15 14:46:11 -07:00
mergify[bot]
4cc1b85376 Boot remote native loads, take 2 (#5106) (#5109)
automerge
2019-07-15 12:54:24 -07:00
mergify[bot]
f8312ce125 Keybase pubkey file instructions and verification for validators (#5090) (#5102)
automerge
2019-07-14 23:25:50 -07:00
mergify[bot]
6a4cd02f64 Add node zone and count to ENV (#5100) (#5101)
automerge
2019-07-14 22:13:50 -07:00
mergify[bot]
50f238d900 Pull testnet vars up to buildkite env (#5098) (#5099)
automerge
2019-07-14 19:56:59 -07:00
mergify[bot]
23e3f4e8a2 Plumb --no-snapshot in from CI (#5077) (#5095)
* Plumb --no-snapshot in from CI

(cherry picked from commit 440d006ec1)
2019-07-14 13:22:28 -06:00
mergify[bot]
27f70dfa49 Correctly decode update manifest (#5086) (#5087)
automerge
2019-07-12 23:25:15 -07:00
Michael Vines
72d366a84e Stop trying to publish crates that are unpublishable 2019-07-12 21:53:33 -07:00
Michael Vines
2da9de8861 Avoid trying to republish crates already on crates.io 2019-07-12 21:36:07 -07:00
Michael Vines
f4288961d5 Add epoch voting history to show-vote-account (#5080) 2019-07-12 21:23:15 -07:00
Michael Vines
143ad436cf Give publish-crate more time 2019-07-12 20:28:23 -07:00
mergify[bot]
0a9fbc3e4c Facility to generate a blocktree prune list using ledger tool (#5041) (#5081)
automerge
2019-07-12 17:49:29 -07:00
Jack May
7aa091bf8c Add rewards to is_syscall_id() (#5035) 2019-07-12 16:10:28 -07:00
Dan Albert
91d8bfa828 Increment cargo tomls to 0.16.4 (#5078) 2019-07-12 16:30:55 -06:00
mergify[bot]
c501c19750 Add a version field to blobs (bp #5057) (#5068)
automerge
2019-07-12 14:38:32 -07:00
Dan Albert
acd55660da Add --no-snapshot to disable booting a validator from a snapshot (#5050) (#5073)
automerge
2019-07-12 15:35:42 -06:00
mergify[bot]
855bd7d3b8 apt-get update before installing certbot (#5054) (#5056)
* apt-get update before installing certbot

(cherry picked from commit f093377805)
2019-07-12 11:54:56 -06:00
Dan Albert
a2e9d8e0bf Enable GPUs and secondary disks for TdS net, pull external account file (#5031) (#5053) 2019-07-12 10:17:46 -06:00
mergify[bot]
81dbe3c49b Add support for additional disks for config-local (#5030) (#5040)
* Add support for additional disks for config-local

(cherry picked from commit e4861f52e0)
2019-07-12 10:01:07 -06:00
mergify[bot]
086e20f6c7 Restore ledger-tool print and json commands (#5048) (#5049)
automerge
2019-07-11 21:14:37 -07:00
Tyera Eulberg
d08a810c08 v0.16: Expand Config program; implement Validator Info CLI (#5045)
* Update config program to accommodate multiple signers (#4946)

* Update config program to accommodate multiple signers

* Update install CLI

* Remove account_type u32; add handling for unsigned keys in list

* ConfigKeys doc

* Make config_api more robust (#4980)

* Make config_api more robust

* Add test and update store instruction

* Improve signature checks in config_api (#5001)

automerge

* Add validator-info CLI (#4970)

* Add validator-info CLI

* Add GetProgramAccounts method to solana-client

* Update validator-info args, and add get subcommand

* Update ValidatorInfo lengths

* Add account filter for get --all

* Update testnet participation doc to reflect validator-info

* Flesh out tests

* Review comments
2019-07-11 18:28:49 -06:00
Tyera Eulberg
400610bf6a v0.16: AccountsDB updates and getProgramAccounts RPC fix (#5044)
* reduce replicode in accounts, fix cast to i64 (#5025)

* add accounts_index_scan_accounts (#5020)

* Plumb scan_accounts into accounts_db, adding load from storage (#5029)

* Fix getProgramAccounts RPC (#5024)

* Use scan_accounts to load accounts by program_id

* Add bank test

* Use get_program_accounts in RPC

* Rebase for v0.16
2019-07-11 17:57:56 -06:00
mergify[bot]
f759ac3a8d add node_pubkey to vote warning (#5033) (#5034)
(cherry picked from commit a191f3fd90)
2019-07-11 13:58:49 -07:00
mergify[bot]
558411364e Pass SOLANA_METRICS_CONFIG along to oom-monitor.sh (#5021) (#5026)
(cherry picked from commit 8781aebe06)
2019-07-11 12:43:38 -07:00
mergify[bot]
d0b5be3051 Rename tds-testnet to tds (#5008) (#5009)
(cherry picked from commit e563a4dda3)
2019-07-10 11:39:57 -06:00
Michael Vines
dc6da6fcca Bump @solana/blockexplorer to v1.17.2 2019-07-10 09:33:10 -07:00
Michael Vines
8ae11a74fa Move letsencrypt arg to create_args 2019-07-09 21:26:56 -07:00
Michael Vines
11f0333728 Include --letsencrypt ($1) 2019-07-09 20:55:41 -07:00
mergify[bot]
aac74d2357 Fund solana-install deployments from the mint keypair to avoid airdrops (#4997) (#5000)
automerge
2019-07-09 17:29:43 -07:00
mergify[bot]
508abcf4ed net/ plumbing to manage LetsEncrypt TLS certificates (#4985) (#4996)
automerge
2019-07-09 16:29:45 -07:00
mergify[bot]
6dbb6c7fe2 Fix always passing in remote filename, even if no accounts file (#4993) (#4994)
* Fix always passing in remote filename, even if no accounts file

* typo

(cherry picked from commit d111223085)
2019-07-09 15:44:04 -07:00
mergify[bot]
2f58658f61 Add testnet-tds support to testnet manager (#4762) (#4987)
automerge
2019-07-09 14:16:13 -07:00
mergify[bot]
0ec7ff5e2f Add pubkey (#4971) (#4977)
automerge
2019-07-09 01:28:48 -07:00
Sagar Dhawan
4d49820188 Handle replicator errors without panicking (#4957)
* Handle replicator errors without panicking

* Typo

* Handle error with match instead of if-let

* Discard error
2019-07-08 11:23:21 -07:00
Michael Vines
6e51babff9 Reduce default commission from 100% to 50% (#4929) 2019-07-05 08:00:39 -07:00
dependabot-preview[bot]
872cf100d7 [Security] Bump smallvec from 0.6.9 to 0.6.10 (#4921)
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 0.6.9 to 0.6.10. **This update includes security fixes.**
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-07-03 18:12:11 -07:00
Michael Vines
78cc4e644c Cargo.lock 2019-07-03 18:10:31 -07:00
Michael Vines
81c0152187 install: more little window fixes (#4930)
* Only add .exe extension if no extension was given

* Switch to ctrlc crate for freebie Windows ^C handling
2019-07-03 18:00:27 -07:00
mergify[bot]
4779625f23 change vote commission to u8 (from u32) (#4887) (#4918)
automerge
2019-07-02 14:52:53 -07:00
Dan Albert
3c0b03ba84 Update cargo.toml and cargo.lock files for 0.16.3 (#4917) 2019-07-02 14:33:04 -06:00
Tyera Eulberg
c53f163ef5 Add RPC api to return program accounts (#4876) (#4912)
automerge
2019-07-02 11:48:00 -07:00
mergify[bot]
ca35854417 Add .exe extension before checking for a program file on windows (#4902) (#4903)
automerge
2019-07-02 08:48:35 -07:00
Michael Vines
ab1fda2a54 Add curl retries 2019-07-02 08:38:18 -07:00
Michael Vines
a6ec77c230 Update Cargo.toml 2019-07-01 23:18:15 -07:00
Michael Vines
1d7894f1be Avoid signal-hook crate on windows (#4901) 2019-07-01 22:52:49 -07:00
Michael Vines
4866a1fc39 run command now kills child process on SIGTERM to cleanly exit (#4896) (#4899)
automerge
2019-07-01 19:28:12 -07:00
mergify[bot]
60c5e59a5e Always send pull responses to the origin addr (#4894) (#4897)
automerge
2019-07-01 17:34:51 -07:00
mergify[bot]
fd93bdadf6 Try to gracefully terminal child process before using SIGKILL (#4890) (#4892)
automerge
2019-07-01 14:43:15 -07:00
mergify[bot]
6089db2a07 Rework fullnode.sh to recover better from genesis block resets (#4884) (#4888)
automerge
2019-07-01 12:31:53 -07:00
mergify[bot]
462d0cfc6c Disable Enter prompt when stdin is not a tty (#4874) (#4877)
(cherry picked from commit 41bda18046)
2019-06-28 18:18:39 -07:00
mergify[bot]
e6d6fc4391 Don't prompt the user to update their PATH if --no-modify-path was supplied (#4872) (#4875)
automerge
2019-06-28 17:39:21 -07:00
mergify[bot]
092556ae5e Lower warn to info, fetch from validator root instead of root + 1 (#4870) (#4873)
automerge
2019-06-28 16:55:46 -07:00
mergify[bot]
ad9fa54a47 Ensure validator process is killed when fullnode.sh is killed (#4869) (#4871)
automerge
2019-06-28 15:03:46 -07:00
Dan Albert
2d68170747 Update cargo.toml files to version 0.16.2 (#4854) 2019-06-27 11:08:02 -06:00
mergify[bot]
20f3d18458 Save snapshots followed by accounts to avoid stale account data (#4847) (#4849)
automerge
2019-06-26 23:53:14 -07:00
mergify[bot]
be79efe9b7 rsync of ledger/ and state.tgz now works on both macOS and Linux (#4845) (#4846)
automerge
2019-06-26 22:45:44 -07:00
Michael Vines
5db377f743 Use default pubkey for solana-install sanity check 2019-06-26 21:51:10 -07:00
Sagar Dhawan
9c2f45a1e0 Fix possible subtract with overflow if mining pool is not setup (#4836)
automerge
2019-06-26 15:28:37 -07:00
Michael Vines
8646918d00 Upload all artifacts 2019-06-26 14:38:55 -07:00
Michael Vines
7c44fc3561 Set CI_REPO_SLUG correctly for the solana-secondary pipeline 2019-06-26 14:38:55 -07:00
mergify[bot]
686403eb1d Setup reward pools in genesis (#4831) (#4834)
automerge
2019-06-26 14:29:27 -07:00
mergify[bot]
6cf9b60a9c Airdrop more token in wallet sanity due to fee (#4830) (#4832)
automerge
2019-06-26 14:05:17 -07:00
Dan Albert
aca142df16 Update cargo.toml files to 0.16.1 (#4829) 2019-06-26 13:25:13 -06:00
mergify[bot]
b2582196db Create snapshots sparsely (#4815) (#4816)
(cherry picked from commit c5e6ebb496)
2019-06-25 12:13:05 -07:00
mergify[bot]
85a77bec5f Set proper count value for account stores (#4797) (#4813)
* set count values for store accounts

* Use AppendVecId type

(cherry picked from commit 9e7f618cff)
2019-06-25 07:57:40 -07:00
mergify[bot]
e781cbf4ba Ignore flaky test_two_unbalanced_stakes (#4794) (#4796)
automerge
2019-06-23 21:30:02 -07:00
mergify[bot]
59956e4543 Remove --storage-mining-pool-lamports (#4792) (#4793)
automerge
2019-06-23 20:53:31 -07:00
Michael Vines
303417f981 Lock blockexplorer version 2019-06-22 22:23:16 -07:00
mergify[bot]
fea03fdf33 Add storage reward pools (#4779) (#4789)
automerge
2019-06-22 21:26:11 -07:00
mergify[bot]
e8160efc46 Prevent Travis/Appveyor from trying to build mergify branches (#4786) (#4787)
(cherry picked from commit 23b6b85bf0)
2019-06-22 08:58:13 -07:00
Sathish
e0ba0d581c Update authorized public key (#4783) 2019-06-22 08:44:00 -07:00
Pankaj Garg
36eda29fc9 Add instructions and processor for stake deactivation (#4781)
automerge
2019-06-22 08:43:17 -07:00
Michael Vines
2ec73db6bd Program instruction to withdraw un-staked lamports from stake account (#4780) (#4782) 2019-06-22 00:11:15 -07:00
Michael Vines
ef6ce2765e Remove storage-mining-pool-keypair arg 2019-06-21 21:38:43 -07:00
536 changed files with 12479 additions and 32069 deletions

View File

@@ -1,3 +1,4 @@
os: Visual Studio 2017
version: '{build}'
branches:
@@ -9,15 +10,13 @@ cache:
- '%USERPROFILE%\.cargo'
- '%APPVEYOR_BUILD_FOLDER%\target'
clone_folder: d:\projects\solana
build_script:
- bash ci/publish-tarball.sh
notifications:
- provider: Slack
incoming_webhook:
secure: GJsBey+F5apAtUm86MHVJ68Uqa6WN1SImcuIc4TsTZrDhA8K1QWUNw9FFQPybUWDyOcS5dly3kubnUqlGt9ux6Ad2efsfRIQYWv0tOVXKeY=
secure: 6HTXVh+FBz29LGJb+taFOo9dqoADfo9xyAszeyXZF5Ub9t5NERytKAR35B2wb+uIOOCBF8+JhmH4437Cgf/ti4IqvURzW1QReXK7eQhn1EI=
channel: ci-status
on_build_success: false
on_build_failure: true
@@ -26,16 +25,16 @@ notifications:
deploy:
- provider: S3
access_key_id:
secure: fTbJl6JpFebR40J7cOWZ2mXBa3kIvEiXgzxAj6L3N7A=
secure: ptvqM/yvgeTeA12XOzybH1KYNh95AdfEvqoH9mvP2ic=
secret_access_key:
secure: vItsBXb2rEFLvkWtVn/Rcxu5a5+2EwC+b7GsA0waJy9hXh6XuBAD0lnHd9re3g/4
secure: IkrgBlz5hdxvwcJdMXyyHUrpWhKa6fXLOD/8rm/rjKqYCdrba9B8V1nLZVrzXGGy
bucket: release.solana.com
region: us-west-1
set_public: true
- provider: GitHub
auth_token:
secure: 81fEmPZ0cV1wLtNuUrcmtgxKF6ROQF1+/ft5m+fHX21z6PoeCbaNo8cTyLioWBj7
secure: vQ3jMl5LQrit6+TQONA3ZgQjZ/Ej62BN2ReVb2NSOwjITHMu1131hjc3dOrMEZL6
draft: false
prerelease: false
on:

View File

@@ -33,10 +33,3 @@ source ci/env.sh
kill -9 "$victim" || true
done
)
# HACK: These are in our docker images, need to be removed from CARGO_HOME
# because we try to cache downloads across builds with CARGO_HOME
# cargo lacks a facility for "system" tooling, always tries CARGO_HOME first
cargo uninstall cargo-audit || true
cargo uninstall svgbob_cli || true
cargo uninstall mdbook || true

View File

@@ -10,13 +10,7 @@
set -e
cd "$(dirname "$0")"/..
if [[ -n $BUILDKITE_TAG ]]; then
buildkite-agent annotate --style info --context release-tag \
"https://github.com/solana-labs/solana/releases/$BUILDKITE_TAG"
buildkite-agent pipeline upload ci/buildkite-release.yml
else
buildkite-agent pipeline upload ci/buildkite.yml
fi
buildkite-agent pipeline upload ci/buildkite.yml
if [[ $BUILDKITE_BRANCH =~ ^pull ]]; then
# Add helpful link back to the corresponding Github Pull Request

24
.github/stale.yml vendored
View File

@@ -1,24 +0,0 @@
only: pulls
# Number of days of inactivity before a pull request becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale pull request is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- security
# Label to use when marking a pull request as stale
staleLabel: stale
# Comment to post when marking a pull request as stale. Set to `false` to disable
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
# Comment to post when closing a stale pull request. Set to `false` to disable
closeComment: >
This stale pull request has been automatically closed.
Thank you for your contributions.

4
.gitignore vendored
View File

@@ -11,7 +11,10 @@
**/*.rs.bk
.cargo
# node config that is rsynced
/config/
# node config that remains local
/config-local/
# log files
*.log
@@ -20,4 +23,3 @@ log-*.txt
# intellij files
/.idea/
/solana.iml
/.vscode/

View File

@@ -1,77 +0,0 @@
# Validate your changes with:
#
# $ curl -F 'data=@.mergify.yml' https://gh.mergify.io/validate
#
# https://doc.mergify.io/
pull_request_rules:
- name: remove outdated reviews
conditions:
- base=master
actions:
dismiss_reviews:
changes_requested: true
- name: set automerge label on mergify backport PRs
conditions:
- author=mergify[bot]
- head~=^mergify/bp/
- "#status-failure=0"
actions:
label:
add:
- automerge
- name: v0.16 backport
conditions:
- base=master
- label=v0.16
actions:
backport:
branches:
- v0.16
- name: v0.17 backport
conditions:
- base=master
- label=v0.17
actions:
backport:
branches:
- v0.17
- name: v0.18 backport
conditions:
- base=master
- label=v0.18
actions:
backport:
branches:
- v0.18
- name: v0.19 backport
conditions:
- base=master
- label=v0.19
actions:
backport:
branches:
- v0.19
- name: v0.20 backport
conditions:
- base=master
- label=v0.20
actions:
backport:
branches:
- v0.20
- name: v0.21 backport
conditions:
- base=master
- label=v0.21
actions:
backport:
branches:
- v0.21
- name: v0.22 backport
conditions:
- base=master
- label=v0.22
actions:
backport:
branches:
- v0.22

View File

@@ -2,8 +2,9 @@ os:
- osx
language: rust
cache: cargo
rust:
- 1.37.0
- 1.35.0
install:
- source ci/rust-version.sh

4095
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,17 +9,15 @@ members = [
"drone",
"validator",
"genesis",
"genesis_programs",
"gossip",
"install",
"keygen",
"kvstore",
"ledger-tool",
"local_cluster",
"logger",
"merkle-tree",
"measure",
"metrics",
"netutil",
"programs/bpf",
"programs/bpf_loader_api",
"programs/bpf_loader_program",
@@ -27,17 +25,12 @@ members = [
"programs/budget_program",
"programs/config_api",
"programs/config_program",
"programs/config_tests",
"programs/exchange_api",
"programs/exchange_program",
"programs/failure_program",
"programs/move_loader_api",
"programs/move_loader_program",
"programs/librapay_api",
"programs/noop_program",
"programs/stake_api",
"programs/stake_program",
"programs/stake_tests",
"programs/storage_api",
"programs/storage_program",
"programs/token_api",
@@ -47,15 +40,9 @@ members = [
"replicator",
"runtime",
"sdk",
"sdk-c",
"upload-perf",
"validator-info",
"utils/netutil",
"utils/fixed_buf",
"vote-signer",
"cli",
]
exclude = [
"programs/bpf/rust/noop",
"wallet",
]
exclude = ["programs/bpf/rust/noop"]

View File

@@ -78,7 +78,7 @@ $ source $HOME/.cargo/env
$ rustup component add rustfmt
```
If your rustc version is lower than 1.37.0, please update it:
If your rustc version is lower than 1.34.0, please update it:
```bash
$ rustup update
@@ -127,9 +127,12 @@ Remote Testnets
We maintain several testnets:
* `testnet` - public stable testnet accessible via testnet.solana.com. Runs 24/7
* `testnet` - public stable testnet accessible via testnet.solana.com, with an https proxy for web apps at api.testnet.solana.com. Runs 24/7
* `testnet-beta` - public beta channel testnet accessible via beta.testnet.solana.com. Runs 24/7
* `testnet-edge` - public edge channel testnet accessible via edge.testnet.solana.com. Runs 24/7
* `testnet-perf` - permissioned stable testnet running a 24/7 soak test
* `testnet-beta-perf` - permissioned beta channel testnet running a multi-hour soak test weekday mornings
* `testnet-edge-perf` - permissioned edge channel testnet running a multi-hour soak test weekday mornings
## Deploy process
@@ -240,3 +243,5 @@ problem is solved by this code?" On the other hand, if a test does fail and you
better way to solve the same problem, a Pull Request with your solution would most certainly be
welcome! Likewise, if rewriting a test can better communicate what code it's protecting, please
send us that patch!

View File

@@ -61,7 +61,7 @@ There are three release channels that map to branches as follows:
## Release Steps
### Creating a new branch from master
### Advance the Channels
#### Create the new branch
1. Pick your branch point for release on master.
@@ -84,12 +84,6 @@ There are three release channels that map to branches as follows:
At this point, `ci/channel-info.sh` should show your freshly cut release branch as
"BETA_CHANNEL" and the previous release branch as "STABLE_CHANNEL".
### Update documentation
Document the new recommended version by updating
```export SOLANA_RELEASE=[new scheduled TESTNET_TAG value]```
in book/src/testnet-participation.md on the release (beta) branch.
### Make the Release
We use [github's Releases UI](https://github.com/solana-labs/solana/releases) for tagging a release.
@@ -112,25 +106,6 @@ We use [github's Releases UI](https://github.com/solana-labs/solana/releases) fo
1. Push your Cargo.toml change and the autogenerated Cargo.lock changes to the
release branch.
### Publish updated Book
We maintain three copies of the "book" as official documentation:
1) "Book" is the documentation for the latest official release. This should get manually updated whenever a new release is made. It is published here:
https://solana-labs.github.io/book/
2) "Book-edge" tracks the tip of the master branch and updates automatically.
https://solana-labs.github.io/book-edge/
3) "Book-beta" tracks the tip of the beta branch and updates automatically.
https://solana-labs.github.io/book-beta/
To manually trigger an update of the "Book", create a new job of the manual-update-book pipeline.
Set the tag of the latest release as the PUBLISH_BOOK_TAG environment variable.
```bash
PUBLISH_BOOK_TAG=v0.16.6
```
https://buildkite.com/solana-labs/manual-update-book
### Update software on testnet.solana.com
The testnet running on testnet.solana.com is set to use a fixed release tag
@@ -170,6 +145,12 @@ TESTNET_TAG=[same value as used in TESTNET_TAG in the schedules]
TESTNET_OP=create-and-start
```
#### Update documentation
Document the new recommended version by updating
```export SOLANA_RELEASE=[new scheduled TESTNET_TAG value]```
in book/src/testnet-participation.md for both edge and beta channel branches.
### Alert the community
Notify Discord users on #validator-support that a new release for

View File

@@ -1,4 +1,3 @@
/target/
/config/
/config-local/
/farf/

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-exchange"
version = "0.18.1"
version = "0.16.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -10,34 +10,33 @@ publish = false
[dependencies]
bincode = "1.1.4"
bs58 = "0.2.4"
bs58 = "0.2.0"
clap = "2.32.0"
env_logger = "0.6.2"
env_logger = "0.6.0"
itertools = "0.8.0"
log = "0.4.8"
log = "0.4.6"
num-derive = "0.2"
num-traits = "0.2"
rand = "0.6.5"
rayon = "1.1.0"
serde = "1.0.99"
serde_derive = "1.0.99"
serde_json = "1.0.40"
serde = "1.0.92"
serde_derive = "1.0.92"
serde_json = "1.0.39"
serde_yaml = "0.8.9"
# solana-runtime = { path = "../solana/runtime"}
solana-core = { path = "../core", version = "0.18.1" }
solana-local-cluster = { path = "../local_cluster", version = "0.18.1" }
solana-client = { path = "../client", version = "0.18.1" }
solana-drone = { path = "../drone", version = "0.18.1" }
solana-exchange-api = { path = "../programs/exchange_api", version = "0.18.1" }
solana-exchange-program = { path = "../programs/exchange_program", version = "0.18.1" }
solana-logger = { path = "../logger", version = "0.18.1" }
solana-metrics = { path = "../metrics", version = "0.18.1" }
solana-netutil = { path = "../utils/netutil", version = "0.18.1" }
solana-runtime = { path = "../runtime", version = "0.18.1" }
solana-sdk = { path = "../sdk", version = "0.18.1" }
untrusted = "0.7.0"
ws = "0.9.0"
solana = { path = "../core", version = "0.16.5" }
solana-client = { path = "../client", version = "0.16.5" }
solana-drone = { path = "../drone", version = "0.16.5" }
solana-exchange-api = { path = "../programs/exchange_api", version = "0.16.5" }
solana-exchange-program = { path = "../programs/exchange_program", version = "0.16.5" }
solana-logger = { path = "../logger", version = "0.16.5" }
solana-metrics = { path = "../metrics", version = "0.16.5" }
solana-netutil = { path = "../netutil", version = "0.16.5" }
solana-runtime = { path = "../runtime", version = "0.16.5" }
solana-sdk = { path = "../sdk", version = "0.16.5" }
untrusted = "0.6.2"
ws = "0.8.1"
[features]
cuda = ["solana-core/cuda"]
cuda = ["solana/cuda"]

View File

@@ -6,10 +6,10 @@ learn how to start and interact with the exchange.
### Table of Contents
[Overview](#Overview)<br>
[Premise](#Premise)<br>
[Premiss](#Premiss)<br>
[Exchange startup](#Exchange-startup)<br>
[Order Requests](#Trade-requests)<br>
[Order Cancellations](#Trade-cancellations)<br>
[Trade requests](#Trade-requests)<br>
[Trade cancellations](#Trade-cancellations)<br>
[Trade swap](#Trade-swap)<br>
[Exchange program operations](#Exchange-program-operations)<br>
[Quotes and OHLCV](#Quotes-and-OHLCV)<br>
@@ -22,9 +22,9 @@ An exchange is a marketplace where one asset can be traded for another. This
demo demonstrates one way to host an exchange on the Solana blockchain by
emulating a currency exchange.
The assets are virtual tokens held by investors who may post order requests to
the exchange. A Matcher monitors the exchange and posts swap requests for
matching orders. All the transactions can execute concurrently.
The assets are virtual tokens held by investors who may post trade requests to
the exchange. A Swapper monitors the exchange and posts swap requests for
matching trade orders. All the transactions can execute concurrently.
## Premise
@@ -42,91 +42,92 @@ matching orders. All the transactions can execute concurrently.
- A request to create a token account
- Token request
- A request to deposit tokens of a particular type into a token account.
- Asset pair
- A struct with fields Base and Quote, representing the two assets which make up a
trading pair, which themselves are Tokens. The Base or 'primary' asset is the
numerator and the Quote is the denominator for pricing purposes.
- Order side
- Describes which side of the market an investor wants to place a trade on. Options
are "Bid" or "Ask", where a bid represents an offer to purchase the Base asset of
the AssetPair for a sum of the Quote Asset and an Ask is an offer to sell Base asset
for the Quote asset.
- Token pair
- A unique ordered list of two tokens. For the four types of tokens used in
this demo, the valid pairs are AB, AC, AD, BC, BD, CD.
- Direction of trade
- Describes which token in the pair the investor wants to sell and buy and can
be either "To" or "From". For example, if an investor issues a "To" trade
for "AB" then they which to exchange A tokens to B tokens. A "From" order
would read the other way, A tokens from B tokens.
- Price ratio
- An expression of the relative prices of two tokens. Calculated with the Base
Asset as the numerator and the Quote Asset as the denominator. Ratios are
represented as fixed point numbers. The fixed point scaler is defined in
- An expression of the relative prices of two tokens. They consist of the
price of the primary token and the price of the secondary token. For
simplicity sake, the primary token's price is always 1, which forces the
secondary to be the common denominator. For example, if token A was worth
2 and token B was worth 6, the price ratio would be 1:3 or just 3. Price
ratios are represented as fixed point numbers. The fixed point scaler is
defined in
[exchange_state.rs](https://github.com/solana-labs/solana/blob/c2fdd1362a029dcf89c8907c562d2079d977df11/programs/exchange_api/src/exchange_state.rs#L7)
- Order request
- A Solana transaction sent by a trader to the exchange to submit an order.
Order requests are made up of the token pair, the order side (bid or ask),
quantity of the primary token, the price ratio, and the two token accounts
to be credited/deducted. An example trade request looks like "T AB 5 2"
which reads "Exchange 5 A tokens to B tokens at a price ratio of 1:2" A fulfilled trade would result in 5 A tokens
- Trade request
- A Solana transaction executed by the exchange requesting the trade of one
type of token for another. Trade requests are made up of the token pair,
the direction of the trade, quantity of the primary token, the price ratio,
and the two token accounts to be credited/deducted. An example trade
request looks like "T AB 5 2" which reads "Exchange 5 A tokens to B tokens
at a price ratio of 1:2" A fulfilled trade would result in 5 A tokens
deducted and 10 B tokens credited to the trade initiator's token accounts.
Successful order requests result in an order.
- Order
- The result of a successful order request. orders are stored in
accounts owned by the submitter of the order request. They can only be
Successful trade requests result in a trade order.
- Trade order
- The result of a successful trade request. Trade orders are stored in
accounts owned by the submitter of the trade request. They can only be
canceled by their owner but can be used by anyone in a trade swap. They
contain the same information as the order request.
contain the same information as the trade request.
- Price spread
- The difference between the two matching orders. The spread is the
profit of the Matcher initiating the swap request.
- Match requirements
- The difference between the two matching trade orders. The spread is the
profit of the Swapper initiating the swap request.
- Swap requirements
- Policies that result in a successful trade swap.
- Match request
- A request to fill two complementary orders (bid/ask), resulting if successful,
in a trade being created.
- Trade
- A successful trade is created from two matching orders that meet
swap requirements which are submitted in a Match Request by a Matcher and
executed by the exchange. A trade may not wholly satisfy one or both of the
orders in which case the orders are adjusted appropriately. Upon execution,
tokens are distributed to the traders' accounts and any overlap or
"negative spread" between orders is deposited into the Matcher's profit
account. All successful trades are recorded in the data of a new solana
account for posterity.
- Swap request
- A request to exchange tokens between to trade orders
- Trade swap
- A successful trade. A swap consists of two matching trade orders that meet
swap requirements. A trade swap may not wholly satisfy one or both of the
trade orders in which case the trade orders are adjusted appropriately. As
long as the swap requirements are met there will be an exchange of tokens
between accounts. Any price spread is deposited into the Swapper's profit
account. All trade swaps are recorded in a new account for posterity.
- Investor
- Individual investors who hold a number of tokens and wish to trade them on
the exchange. Investors operate as Solana thin clients who own a set of
accounts containing tokens and/or order requests. Investors post
accounts containing tokens and/or trade requests. Investors post
transactions to the exchange in order to request tokens and post or cancel
order requests.
- Matcher
- An agent who facilitates trading between investors. Matchers operate as
Solana thin clients who monitor all the orders looking for a trade
match. Once found, the Matcher issues a swap request to the exchange.
Matchers are the engine of the exchange and are rewarded for their efforts by
accumulating the price spreads of the swaps they initiate. Matchers also
trade requests.
- Swapper
- An agent who facilitates trading between investors. Swappers operate as
Solana thin clients who monitor all the trade orders looking for a trade
match. Once found, the Swapper issues a swap request to the exchange.
Swappers are the engine of the exchange and are rewarded for their efforts by
accumulating the price spreads of the swaps they initiate. Swappers also
provide current bid/ask price and OHLCV (Open, High, Low, Close, Volume)
information on demand via a public network port.
- Transaction fees
- Solana transaction fees are paid for by the transaction submitters who are
the Investors and Matchers.
the Investors and Swappers.
## Exchange startup
The exchange is up and running when it reaches a state where it can take
investors' trades and Matchers' match requests. To achieve this state the
investor's trades and Swapper's swap requests. To achieve this state the
following must occur in order:
- Start the Solana blockchain
- Start the thin-client
- The Matcher subscribes to change notifications for all the accounts owned by
- Start the Swapper thin-client
- The Swapper subscribes to change notifications for all the accounts owned by
the exchange program id. The subscription is managed via Solana's JSON RPC
interface.
- The Matcher starts responding to queries for bid/ask price and OHLCV
- The Swapper starts responding to queries for bid/ask price and OHLCV
The Matcher responding successfully to price and OHLCV requests is the signal to
The Swapper responding successfully to price and OHLCV requests is the signal to
the investors that trades submitted after that point will be analyzed. <!--This
is not ideal, and instead investors should be able to submit trades at any time,
and the Matcher could come and go without missing a trade. One way to achieve
this is for the Matcher to read the current state of all accounts looking for all
open orders.-->
and the Swapper could come and go without missing a trade. One way to achieve
this is for the Swapper to read the current state of all accounts looking for all
open trade orders.-->
Investors will initially query the exchange to discover their current balance
for each type of token. If the investor does not already have an account for
each type of token, they will submit account requests. Matcher as well will
each type of token, they will submit account requests. Swappers as well will
request accounts to hold the tokens they earn by initiating trade swaps.
```rust
@@ -164,7 +165,7 @@ pub struct TokenAccountInfo {
}
```
For this demo investors or Matcher can request more tokens from the exchange at
For this demo investors or Swappers can request more tokens from the exchange at
any time by submitting token requests. In non-demos, an exchange of this type
would provide another way to exchange a 3rd party asset into tokens.
@@ -180,19 +181,19 @@ pub enum ExchangeInstruction {
}
```
## Order Requests
## Trade requests
When an investor decides to exchange a token of one type for another, they
submit a transaction to the Solana Blockchain containing an order request, which,
if successful, is turned into an order. orders do not expire but are
cancellable. <!-- orders should have a timestamp to enable trade
expiration --> When an order is created, tokens are deducted from a token
account and the order acts as an escrow. The tokens are held until the
order is fulfilled or canceled. If the direction is `To`, then the number
submit a transaction to the Solana Blockchain containing a trade request, which,
if successful, is turned into a trade order. Trade orders do not expire but are
cancellable. <!-- Trade orders should have a timestamp to enable trade
expiration --> When a trade order is created, tokens are deducted from a token
account and the trade order acts as an escrow. The tokens are held until the
trade order is fulfilled or canceled. If the direction is `To`, then the number
of `tokens` are deducted from the primary account, if `From` then `tokens`
multiplied by `price` are deducted from the secondary account. orders are
multiplied by `price` are deducted from the secondary account. Trade orders are
no longer valid when the number of `tokens` goes to zero, at which point they
can no longer be used. <!-- Could support refilling orders, so order
can no longer be used. <!-- Could support refilling trade orders, so trade order
accounts are refilled rather than accumulating -->
```rust
@@ -204,7 +205,7 @@ pub enum Direction {
From,
}
pub struct OrderRequestInfo {
pub struct TradeRequestInfo {
/// Direction of trade
pub direction: Direction,
@@ -223,7 +224,7 @@ pub struct OrderRequestInfo {
}
pub enum ExchangeInstruction {
/// order request
/// Trade request
/// key 0 - Signer
/// key 1 - Account in which to record the swap
/// key 2 - Token account associated with this trade
@@ -232,7 +233,7 @@ pub enum ExchangeInstruction {
/// Trade accounts are populated with this structure
pub struct TradeOrderInfo {
/// Owner of the order
/// Owner of the trade order
pub owner: Pubkey,
/// Direction of the exchange
pub direction: Direction,
@@ -251,7 +252,7 @@ pub struct TradeOrderInfo {
}
```
## Order cancellations
## Trade cancellations
An investor may cancel a trade at anytime, but only trades they own. If the
cancellation is successful, any tokens held in escrow are returned to the
@@ -259,24 +260,24 @@ account from which they came.
```rust
pub enum ExchangeInstruction {
/// order cancellation
/// Trade cancellation
/// key 0 - Signer
/// key 1 -order to cancel
/// key 1 -Trade order to cancel
TradeCancellation,
}
```
## Trade swaps
The Matcher is monitoring the accounts assigned to the exchange program and
building a trade-order table. The order table is used to identify
matching orders which could be fulfilled. When a match is found the
Matcher should issue a swap request. Swap requests may not satisfy the entirety
The Swapper is monitoring the accounts assigned to the exchange program and
building a trade-order table. The trade order table is used to identify
matching trade orders which could be fulfilled. When a match is found the
Swapper should issue a swap request. Swap requests may not satisfy the entirety
of either order, but the exchange will greedily fulfill it. Any leftover tokens
in either account will keep the order valid for further swap requests in
in either account will keep the trade order valid for further swap requests in
the future.
Matching orders are defined by the following swap requirements:
Matching trade orders are defined by the following swap requirements:
- Opposite polarity (one `To` and one `From`)
- Operate on the same token pair
@@ -309,14 +310,14 @@ whole for clarity.
| 5 | 1 T AB 2 10 | 2 F AB 1 5 |
As part of a successful swap request, the exchange will credit tokens to the
Matcher's account equal to the difference in the price ratios or the two orders.
These tokens are considered the Matcher's profit for initiating the trade.
Swapper's account equal to the difference in the price ratios or the two orders.
These tokens are considered the Swapper's profit for initiating the trade.
The Matcher would initiate the following swap on the order table above:
The Swapper would initiate the following swap on the order table above:
- Row 1, To: Investor 1 trades 2 A tokens to 8 B tokens
- Row 1, From: Investor 2 trades 2 A tokens from 8 B tokens
- Matcher takes 8 B tokens as profit
- Swapper takes 8 B tokens as profit
Both row 1 trades are fully realized, table becomes:
@@ -327,11 +328,11 @@ Both row 1 trades are fully realized, table becomes:
| 3 | 1 T AB 2 8 | 2 F AB 3 6 |
| 4 | 1 T AB 2 10 | 2 F AB 1 5 |
The Matcher would initiate the following swap:
The Swapper would initiate the following swap:
- Row 1, To: Investor 1 trades 1 A token to 4 B tokens
- Row 1, From: Investor 2 trades 1 A token from 4 B tokens
- Matcher takes 4 B tokens as profit
- Swapper takes 4 B tokens as profit
Row 1 From is not fully realized, table becomes:
@@ -342,11 +343,11 @@ Row 1 From is not fully realized, table becomes:
| 3 | 1 T AB 2 10 | 2 F AB 3 6 |
| 4 | | 2 F AB 1 5 |
The Matcher would initiate the following swap:
The Swapper would initiate the following swap:
- Row 1, To: Investor 1 trades 1 A token to 6 B tokens
- Row 1, From: Investor 2 trades 1 A token from 6 B tokens
- Matcher takes 2 B tokens as profit
- Swapper takes 2 B tokens as profit
Row 1 To is now fully realized, table becomes:
@@ -356,11 +357,11 @@ Row 1 To is now fully realized, table becomes:
| 2 | 1 T AB 2 8 | 2 F AB 3 5 |
| 3 | 1 T AB 2 10 | 2 F AB 1 5 |
The Matcher would initiate the following last swap:
The Swapper would initiate the following last swap:
- Row 1, To: Investor 1 trades 2 A token to 12 B tokens
- Row 1, From: Investor 2 trades 2 A token from 12 B tokens
- Matcher takes 4 B tokens as profit
- Swapper takes 4 B tokens as profit
Table becomes:
@@ -378,11 +379,11 @@ pub enum ExchangeInstruction {
/// Trade swap request
/// key 0 - Signer
/// key 1 - Account in which to record the swap
/// key 2 - 'To' order
/// key 3 - `From` order
/// key 2 - 'To' trade order
/// key 3 - `From` trade order
/// key 4 - Token account associated with the To Trade
/// key 5 - Token account associated with From trade
/// key 6 - Token account in which to deposit the Matcher profit from the swap.
/// key 6 - Token account in which to deposit the Swappers profit from the swap.
SwapRequest,
}
@@ -390,9 +391,9 @@ pub enum ExchangeInstruction {
pub struct TradeSwapInfo {
/// Pair swapped
pub pair: TokenPair,
/// `To` order
/// `To` trade order
pub to_trade_order: Pubkey,
/// `From` order
/// `From` trade order
pub from_trade_order: Pubkey,
/// Number of primary tokens exchanged
pub primary_tokens: u64,
@@ -423,32 +424,32 @@ pub enum ExchangeInstruction {
/// the exchange has a limitless number of tokens it can transfer.
TransferRequest(Token, u64),
/// order request
/// Trade request
/// key 0 - Signer
/// key 1 - Account in which to record the swap
/// key 2 - Token account associated with this trade
TradeRequest(TradeRequestInfo),
/// order cancellation
/// Trade cancellation
/// key 0 - Signer
/// key 1 -order to cancel
/// key 1 -Trade order to cancel
TradeCancellation,
/// Trade swap request
/// key 0 - Signer
/// key 1 - Account in which to record the swap
/// key 2 - 'To' order
/// key 3 - `From` order
/// key 2 - 'To' trade order
/// key 3 - `From` trade order
/// key 4 - Token account associated with the To Trade
/// key 5 - Token account associated with From trade
/// key 6 - Token account in which to deposit the Matcher profit from the swap.
/// key 6 - Token account in which to deposit the Swappers profit from the swap.
SwapRequest,
}
```
## Quotes and OHLCV
The Matcher will provide current bid/ask price quotes based on trade actively and
The Swapper will provide current bid/ask price quotes based on trade actively and
also provide OHLCV based on some time window. The details of how the bid/ask
price quotes are calculated are yet to be decided.
@@ -477,3 +478,6 @@ To also see the cluster messages:
```bash
$ RUST_LOG=solana_bench_exchange=info,solana=info cargo test --release -- --nocapture test_exchange_local_cluster
```

View File

@@ -5,8 +5,8 @@ use itertools::izip;
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana::gen_keys::GenKeys;
use solana_client::perf_utils::{sample_txs, SampleStats};
use solana_core::gen_keys::GenKeys;
use solana_drone::drone::request_airdrop_transaction;
use solana_exchange_api::exchange_instruction;
use solana_exchange_api::exchange_state::*;
@@ -332,7 +332,7 @@ fn do_tx_transfers<T>(
struct TradeInfo {
trade_account: Pubkey,
order_info: OrderInfo,
order_info: TradeOrderInfo,
}
#[allow(clippy::too_many_arguments)]
fn swapper<T>(
@@ -509,7 +509,7 @@ fn trader<T>(
T: Client,
{
// TODO Hard coded for now
let pair = AssetPair::default();
let pair = TokenPair::AB;
let tokens = 1;
let price = 1000;
let mut account_group: usize = 0;
@@ -527,21 +527,21 @@ fn trader<T>(
let mut trade_infos = vec![];
let start = account_group * batch_size as usize;
let end = account_group * batch_size as usize + batch_size as usize;
let mut side = OrderSide::Ask;
let mut direction = Direction::To;
for (signer, trade, src) in izip!(
signers[start..end].iter(),
trade_keys,
srcs[start..end].iter(),
) {
side = if side == OrderSide::Ask {
OrderSide::Bid
direction = if direction == Direction::To {
Direction::From
} else {
OrderSide::Ask
Direction::To
};
let order_info = OrderInfo {
let order_info = TradeOrderInfo {
/// Owner of the trade order
owner: Pubkey::default(), // don't care
side,
direction,
pair,
tokens,
price,
@@ -551,7 +551,7 @@ fn trader<T>(
trade_account: trade.pubkey(),
order_info,
});
trades.push((signer, trade.pubkey(), side, src));
trades.push((signer, trade.pubkey(), direction, src));
}
account_group = (account_group + 1) % account_groups as usize;
@@ -562,7 +562,7 @@ fn trader<T>(
trades.chunks(chunk_size).for_each(|chunk| {
let trades_txs: Vec<_> = chunk
.par_iter()
.map(|(signer, trade, side, src)| {
.map(|(signer, trade, direction, src)| {
let s: &Keypair = &signer;
let owner = &signer.pubkey();
let space = mem::size_of::<ExchangeState>() as u64;
@@ -571,7 +571,7 @@ fn trader<T>(
vec![
system_instruction::create_account(owner, trade, 1, space, &id()),
exchange_instruction::trade_request(
owner, trade, *side, pair, tokens, price, src,
owner, trade, *direction, pair, tokens, price, src,
),
],
blockhash,
@@ -646,21 +646,7 @@ where
false
}
fn verify_funding_transfer<T: SyncClient + ?Sized>(
client: &T,
tx: &Transaction,
amount: u64,
) -> bool {
for a in &tx.message().account_keys[1..] {
if client.get_balance(a).unwrap_or(0) >= amount {
return true;
}
}
false
}
pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
pub fn fund_keys(client: &Client, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
let total = lamports * (dests.len() as u64 + 1);
let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
@@ -717,7 +703,6 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>],
.collect();
let mut retries = 0;
let amount = chunk[0].1[0].1;
while !to_fund_txs.is_empty() {
let receivers = to_fund_txs
.iter()
@@ -746,7 +731,7 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>],
let mut waits = 0;
loop {
sleep(Duration::from_millis(200));
to_fund_txs.retain(|(_, tx)| !verify_funding_transfer(client, &tx, amount));
to_fund_txs.retain(|(_, tx)| !verify_transfer(client, &tx));
if to_fund_txs.is_empty() {
break;
}
@@ -778,7 +763,7 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>],
}
}
pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], accounts: &[Pubkey]) {
pub fn create_token_accounts(client: &Client, signers: &[Arc<Keypair>], accounts: &[Pubkey]) {
let mut notfunded: Vec<(&Arc<Keypair>, &Pubkey)> = signers.iter().zip(accounts).collect();
while !notfunded.is_empty() {
@@ -908,7 +893,7 @@ fn generate_keypairs(num: u64) -> Vec<Keypair> {
rnd.gen_n_keypairs(num)
}
pub fn airdrop_lamports(client: &dyn Client, drone_addr: &SocketAddr, id: &Keypair, amount: u64) {
pub fn airdrop_lamports(client: &Client, drone_addr: &SocketAddr, id: &Keypair, amount: u64) {
let balance = client.get_balance(&id.pubkey());
let balance = balance.unwrap_or(0);
if balance >= amount {
@@ -963,11 +948,11 @@ pub fn airdrop_lamports(client: &dyn Client, drone_addr: &SocketAddr, id: &Keypa
#[cfg(test)]
mod tests {
use super::*;
use solana_core::gossip_service::{discover_cluster, get_multi_client};
use solana_core::validator::ValidatorConfig;
use solana::gossip_service::{discover_cluster, get_multi_client};
use solana::local_cluster::{ClusterConfig, LocalCluster};
use solana::validator::ValidatorConfig;
use solana_drone::drone::run_local_drone;
use solana_exchange_api::exchange_processor::process_instruction;
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
use solana_runtime::bank::Bank;
use solana_runtime::bank_client::BankClient;
use solana_sdk::genesis_block::create_genesis_block;

View File

@@ -1,5 +1,5 @@
use clap::{crate_description, crate_name, crate_version, value_t, App, Arg, ArgMatches};
use solana_core::gen_keys::GenKeys;
use solana::gen_keys::GenKeys;
use solana_drone::drone::DRONE_PORT;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use std::net::SocketAddr;

View File

@@ -8,7 +8,7 @@ extern crate solana_exchange_program;
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
use log::*;
use solana_core::gossip_service::{discover_cluster, get_multi_client};
use solana::gossip_service::{discover_cluster, get_multi_client};
use solana_sdk::signature::KeypairUtil;
fn main() {

View File

@@ -10,7 +10,7 @@ use std::{error, fmt};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ToOrder {
pub pubkey: Pubkey,
pub info: OrderInfo,
pub info: TradeOrderInfo,
}
impl Ord for ToOrder {
@@ -26,7 +26,7 @@ impl PartialOrd for ToOrder {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FromOrder {
pub pubkey: Pubkey,
pub info: OrderInfo,
pub info: TradeOrderInfo,
}
impl Ord for FromOrder {
@@ -95,13 +95,17 @@ impl OrderBook {
// pub fn cancel(&mut self, pubkey: Pubkey) -> Result<(), Box<dyn error::Error>> {
// Ok(())
// }
pub fn push(&mut self, pubkey: Pubkey, info: OrderInfo) -> Result<(), Box<dyn error::Error>> {
check_trade(info.side, info.tokens, info.price)?;
match info.side {
OrderSide::Ask => {
pub fn push(
&mut self,
pubkey: Pubkey,
info: TradeOrderInfo,
) -> Result<(), Box<dyn error::Error>> {
check_trade(info.direction, info.tokens, info.price)?;
match info.direction {
Direction::To => {
self.to_ab.push(ToOrder { pubkey, info });
}
OrderSide::Bid => {
Direction::From => {
self.from_ab.push(FromOrder { pubkey, info });
}
}

View File

@@ -1,2 +1 @@
/target/
/farf/

View File

@@ -2,17 +2,17 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-streamer"
version = "0.18.1"
version = "0.16.5"
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.18.1" }
solana-logger = { path = "../logger", version = "0.18.1" }
solana-netutil = { path = "../utils/netutil", version = "0.18.1" }
solana = { path = "../core", version = "0.16.5" }
solana-logger = { path = "../logger", version = "0.16.5" }
solana-netutil = { path = "../netutil", version = "0.16.5" }
[features]
cuda = ["solana-core/cuda"]
cuda = ["solana/cuda"]

View File

@@ -1,8 +1,7 @@
use clap::{crate_description, crate_name, crate_version, App, Arg};
use solana_core::packet::PacketsRecycler;
use solana_core::packet::{Packet, Packets, BLOB_SIZE, PACKET_DATA_SIZE};
use solana_core::result::Result;
use solana_core::streamer::{receiver, PacketReceiver};
use solana::packet::{Packet, Packets, BLOB_SIZE, PACKET_DATA_SIZE};
use solana::result::Result;
use solana::streamer::{receiver, PacketReceiver};
use std::cmp::max;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
@@ -17,7 +16,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
let send = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut msgs = Packets::default();
msgs.packets.resize(10, Packet::default());
for w in msgs.packets.iter_mut() {
for w in &mut msgs.packets {
w.meta.size = PACKET_DATA_SIZE;
w.meta.set_addr(&addr);
}
@@ -75,7 +74,6 @@ fn main() -> Result<()> {
let mut read_channels = Vec::new();
let mut read_threads = Vec::new();
let recycler = PacketsRecycler::default();
for _ in 0..num_sockets {
let read = solana_netutil::bind_to(port, false).unwrap();
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
@@ -85,13 +83,7 @@ fn main() -> Result<()> {
let (s_reader, r_reader) = channel();
read_channels.push(r_reader);
read_threads.push(receiver(
Arc::new(read),
&exit,
s_reader,
recycler.clone(),
"bench-streamer-test",
));
read_threads.push(receiver(Arc::new(read), &exit, s_reader));
}
let t_producer1 = producer(&addr, exit.clone());

View File

@@ -1,4 +1,3 @@
/target/
/config/
/config-local/
/farf/

View File

@@ -2,34 +2,28 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-tps"
version = "0.18.1"
version = "0.16.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
bincode = "1.1.4"
clap = "2.33.0"
log = "0.4.8"
log = "0.4.6"
rayon = "1.1.0"
serde = "1.0.99"
serde_derive = "1.0.99"
serde_json = "1.0.40"
serde = "1.0.92"
serde_derive = "1.0.92"
serde_json = "1.0.39"
serde_yaml = "0.8.9"
solana-core = { path = "../core", version = "0.18.1" }
solana-local-cluster = { path = "../local_cluster", version = "0.18.1" }
solana-client = { path = "../client", version = "0.18.1" }
solana-drone = { path = "../drone", version = "0.18.1" }
solana-librapay-api = { path = "../programs/librapay_api", version = "0.18.1" }
solana-logger = { path = "../logger", version = "0.18.1" }
solana-metrics = { path = "../metrics", version = "0.18.1" }
solana-measure = { path = "../measure", version = "0.18.1" }
solana-netutil = { path = "../utils/netutil", version = "0.18.1" }
solana-runtime = { path = "../runtime", version = "0.18.1" }
solana-sdk = { path = "../sdk", version = "0.18.1" }
solana-move-loader-program = { path = "../programs/move_loader_program", version = "0.18.1" }
solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.18.1" }
solana = { path = "../core", version = "0.16.5" }
solana-client = { path = "../client", version = "0.16.5" }
solana-drone = { path = "../drone", version = "0.16.5" }
solana-logger = { path = "../logger", version = "0.16.5" }
solana-metrics = { path = "../metrics", version = "0.16.5" }
solana-netutil = { path = "../netutil", version = "0.16.5" }
solana-runtime = { path = "../runtime", version = "0.16.5" }
solana-sdk = { path = "../sdk", version = "0.16.5" }
[features]
cuda = ["solana-core/cuda"]
cuda = ["solana/cuda"]

View File

@@ -1,17 +1,13 @@
use solana_metrics;
use bincode;
use log::*;
use rayon::prelude::*;
use solana::gen_keys::GenKeys;
use solana_client::perf_utils::{sample_txs, SampleStats};
use solana_core::gen_keys::GenKeys;
use solana_drone::drone::request_airdrop_transaction;
use solana_librapay_api::{create_genesis, upload_mint_program, upload_payment_program};
use solana_measure::measure::Measure;
use solana_metrics::datapoint_info;
use solana_sdk::client::Client;
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::system_instruction;
use solana_sdk::system_transaction;
@@ -28,8 +24,6 @@ use std::thread::Builder;
use std::time::Duration;
use std::time::Instant;
use solana_librapay_api::librapay_transaction;
pub const MAX_SPENDS_PER_TX: u64 = 4;
pub const NUM_LAMPORTS_PER_ACCOUNT: u64 = 128;
@@ -49,7 +43,6 @@ pub struct Config {
pub duration: Duration,
pub tx_count: usize,
pub sustained: bool,
pub use_move: bool,
}
impl Default for Config {
@@ -61,19 +54,15 @@ impl Default for Config {
duration: Duration::new(std::u64::MAX, 0),
tx_count: 500_000,
sustained: false,
use_move: false,
}
}
}
type LibraKeys = (Keypair, Pubkey, Pubkey, Vec<Keypair>);
pub fn do_bench_tps<T>(
clients: Vec<T>,
config: Config,
gen_keypairs: Vec<Keypair>,
keypair0_balance: u64,
libra_args: Option<LibraKeys>,
) -> u64
where
T: 'static + Client + Send + Sync,
@@ -85,7 +74,6 @@ where
duration,
tx_count,
sustained,
..
} = config;
let clients: Vec<_> = clients.into_iter().map(Arc::new).collect();
@@ -177,7 +165,6 @@ where
&keypairs[len..],
threads,
reclaim_lamports_back_to_source_account,
&libra_args,
);
// In sustained mode overlap the transfers with generation
// this has higher average performance but lower peak performance
@@ -234,74 +221,6 @@ fn metrics_submit_lamport_balance(lamport_balance: u64) {
);
}
fn generate_move_txs(
source: &[Keypair],
dest: &[Keypair],
reclaim: bool,
move_keypairs: &[Keypair],
libra_pay_program_id: &Pubkey,
libra_mint_id: &Pubkey,
blockhash: &Hash,
) -> Vec<(Transaction, u64)> {
let count = move_keypairs.len() / 2;
let source_move = &move_keypairs[..count];
let dest_move = &move_keypairs[count..];
let pairs: Vec<_> = if !reclaim {
source_move
.iter()
.zip(dest_move.iter())
.zip(source.iter())
.collect()
} else {
dest_move
.iter()
.zip(source_move.iter())
.zip(dest.iter())
.collect()
};
pairs
.par_iter()
.map(|((from, to), payer)| {
(
librapay_transaction::transfer(
libra_pay_program_id,
libra_mint_id,
&payer,
&from,
&to.pubkey(),
1,
*blockhash,
),
timestamp(),
)
})
.collect()
}
fn generate_system_txs(
source: &[Keypair],
dest: &[Keypair],
reclaim: bool,
blockhash: &Hash,
) -> Vec<(Transaction, u64)> {
let pairs: Vec<_> = if !reclaim {
source.iter().zip(dest.iter()).collect()
} else {
dest.iter().zip(source.iter()).collect()
};
pairs
.par_iter()
.map(|(from, to)| {
(
system_transaction::create_user_account(from, &to.pubkey(), 1, *blockhash),
timestamp(),
)
})
.collect()
}
fn generate_txs(
shared_txs: &SharedTransactions,
blockhash: &Hash,
@@ -309,31 +228,25 @@ fn generate_txs(
dest: &[Keypair],
threads: usize,
reclaim: bool,
libra_args: &Option<LibraKeys>,
) {
let tx_count = source.len();
println!("Signing transactions... {} (reclaim={})", tx_count, reclaim);
let signing_start = Instant::now();
let transactions = if let Some((
libra_genesis_keypair,
libra_pay_program_id,
_libra_mint_program_id,
libra_keys,
)) = libra_args
{
generate_move_txs(
source,
dest,
reclaim,
&libra_keys,
libra_pay_program_id,
&libra_genesis_keypair.pubkey(),
blockhash,
)
let pairs: Vec<_> = if !reclaim {
source.iter().zip(dest.iter()).collect()
} else {
generate_system_txs(source, dest, reclaim, blockhash)
dest.iter().zip(source.iter()).collect()
};
let transactions: Vec<_> = pairs
.par_iter()
.map(|(id, keypair)| {
(
system_transaction::create_user_account(id, &keypair.pubkey(), 1, *blockhash),
timestamp(),
)
})
.collect();
let duration = signing_start.elapsed();
let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
@@ -440,12 +353,7 @@ pub fn fund_keys<T: Client>(
let mut notfunded: Vec<&Keypair> = dests.iter().collect();
let lamports_per_account = (total - (extra * max_fee)) / (notfunded.len() as u64 + 1);
println!(
"funding keys {} with lamports: {:?} total: {}",
dests.len(),
client.get_balance(&source.pubkey()),
total
);
println!("funding keys {}", dests.len());
while !notfunded.is_empty() {
let mut new_funded: Vec<(&Keypair, u64)> = vec![];
let mut to_fund = vec![];
@@ -484,10 +392,13 @@ pub fn fund_keys<T: Client>(
let mut to_fund_txs: Vec<_> = chunk
.par_iter()
.map(|(k, m)| {
let tx = Transaction::new_unsigned_instructions(
system_instruction::transfer_many(&k.pubkey(), &m),
);
(k.clone(), tx)
(
k.clone(),
Transaction::new_unsigned_instructions(system_instruction::transfer_many(
&k.pubkey(),
&m,
)),
)
})
.collect();
@@ -691,171 +602,15 @@ pub fn generate_keypairs(seed_keypair: &Keypair, count: u64) -> (Vec<Keypair>, u
(rnd.gen_n_keypairs(total_keys), extra)
}
fn fund_move_keys<T: Client>(
client: &T,
funding_key: &Keypair,
keypairs: &[Keypair],
total: u64,
libra_pay_program_id: &Pubkey,
libra_mint_program_id: &Pubkey,
libra_mint_key: &Keypair,
) {
let (mut blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap();
info!("creating the libra funding account..");
let libra_funding_key = Keypair::new();
let tx = librapay_transaction::create_account(
funding_key,
&libra_funding_key.pubkey(),
1,
blockhash,
);
client.send_message(&[funding_key], tx.message).unwrap();
info!("minting to funding keypair");
let tx = librapay_transaction::mint_tokens(
&libra_mint_program_id,
funding_key,
libra_mint_key,
&libra_funding_key.pubkey(),
total,
blockhash,
);
client
.send_message(&[funding_key, libra_mint_key], tx.message)
.unwrap();
info!("creating {} move accounts...", keypairs.len());
let create_len = 8;
let mut funding_time = Measure::start("funding_time");
for (i, keys) in keypairs.chunks(create_len).enumerate() {
if client.get_balance(&keys[0].pubkey()).unwrap_or(0) > 0 {
// already created these accounts.
break;
}
let pubkeys: Vec<_> = keys.iter().map(|k| k.pubkey()).collect();
let tx = librapay_transaction::create_accounts(funding_key, &pubkeys, 1, blockhash);
let ser_size = bincode::serialized_size(&tx).unwrap();
client.send_message(&[funding_key], tx.message).unwrap();
if i % 10 == 0 {
info!(
"size: {} created {} accounts of {}",
ser_size,
i,
(keypairs.len() / create_len),
);
}
}
funding_time.stop();
info!("funding accounts {}ms", funding_time.as_ms());
const NUM_FUNDING_KEYS: usize = 4;
let funding_keys: Vec<_> = (0..NUM_FUNDING_KEYS).map(|_| Keypair::new()).collect();
let pubkey_amounts: Vec<_> = funding_keys
.iter()
.map(|key| (key.pubkey(), total / NUM_FUNDING_KEYS as u64))
.collect();
let tx = Transaction::new_signed_instructions(
&[funding_key],
system_instruction::transfer_many(&funding_key.pubkey(), &pubkey_amounts),
blockhash,
);
client.send_message(&[funding_key], tx.message).unwrap();
let mut balance = 0;
for _ in 0..20 {
if let Ok(balance_) = client.get_balance(&funding_keys[0].pubkey()) {
if balance_ > 0 {
balance = balance_;
break;
}
}
sleep(Duration::from_millis(100));
}
assert!(balance > 0);
info!("funded multiple funding accounts.. {:?}", balance);
let libra_funding_keys: Vec<_> = (0..NUM_FUNDING_KEYS).map(|_| Keypair::new()).collect();
for (i, key) in libra_funding_keys.iter().enumerate() {
let tx =
librapay_transaction::create_account(&funding_keys[i], &key.pubkey(), 1, blockhash);
client
.send_message(&[&funding_keys[i]], tx.message)
.unwrap();
let tx = librapay_transaction::transfer(
libra_pay_program_id,
&libra_mint_key.pubkey(),
&funding_keys[i],
&libra_funding_key,
&key.pubkey(),
total / NUM_FUNDING_KEYS as u64,
blockhash,
);
client
.send_message(&[&funding_keys[i], &libra_funding_key], tx.message)
.unwrap();
info!("funded libra funding key {}", i);
}
let tx_count = keypairs.len();
let amount = total / (tx_count as u64);
for (i, keys) in keypairs[..tx_count].chunks(NUM_FUNDING_KEYS).enumerate() {
for (j, key) in keys.iter().enumerate() {
let tx = librapay_transaction::transfer(
libra_pay_program_id,
&libra_mint_key.pubkey(),
&funding_keys[j],
&libra_funding_keys[j],
&key.pubkey(),
amount,
blockhash,
);
let _sig = client
.async_send_transaction(tx.clone())
.expect("create_account in generate_and_fund_keypairs");
}
info!("sent... checking balance {}", i);
for (j, key) in keys.iter().enumerate() {
let mut times = 0;
loop {
let balance =
librapay_transaction::get_libra_balance(client, &key.pubkey()).unwrap();
if balance >= amount {
break;
} else if times > 20 {
info!("timed out.. {} key: {} balance: {}", i, j, balance);
break;
} else {
times += 1;
sleep(Duration::from_millis(100));
}
}
}
info!("funded: {} of {}", i, keypairs.len() / NUM_FUNDING_KEYS);
blockhash = client.get_recent_blockhash().unwrap().0;
}
info!("done funding keys..");
}
pub fn generate_and_fund_keypairs<T: Client>(
client: &T,
drone_addr: Option<SocketAddr>,
funding_key: &Keypair,
funding_pubkey: &Keypair,
tx_count: usize,
lamports_per_account: u64,
use_move: bool,
) -> Result<(Vec<Keypair>, Option<LibraKeys>, u64)> {
) -> Result<(Vec<Keypair>, u64)> {
info!("Creating {} keypairs...", tx_count * 2);
let (mut keypairs, extra) = generate_keypairs(funding_key, tx_count as u64 * 2);
let (mut keypairs, extra) = generate_keypairs(funding_pubkey, tx_count as u64 * 2);
info!("Get lamports...");
// Sample the first keypair, see if it has lamports, if so then resume.
@@ -864,60 +619,19 @@ pub fn generate_and_fund_keypairs<T: Client>(
.get_balance(&keypairs[tx_count * 2 - 1].pubkey())
.unwrap_or(0);
let mut move_keypairs_ret = None;
if lamports_per_account > last_keypair_balance {
let (_blockhash, fee_calculator) = client.get_recent_blockhash().unwrap();
let (_, fee_calculator) = client.get_recent_blockhash().unwrap();
let account_desired_balance =
lamports_per_account - last_keypair_balance + fee_calculator.max_lamports_per_signature;
let extra_fees = extra * fee_calculator.max_lamports_per_signature;
let mut total = account_desired_balance * (1 + keypairs.len() as u64) + extra_fees;
if use_move {
total *= 3;
let total = account_desired_balance * (1 + keypairs.len() as u64) + extra_fees;
if client.get_balance(&funding_pubkey.pubkey()).unwrap_or(0) < total {
airdrop_lamports(client, &drone_addr.unwrap(), funding_pubkey, total)?;
}
println!("Previous key balance: {} max_fee: {} lamports_per_account: {} extra: {} desired_balance: {} total: {}",
last_keypair_balance, fee_calculator.max_lamports_per_signature, lamports_per_account, extra,
account_desired_balance, total
);
if client.get_balance(&funding_key.pubkey()).unwrap_or(0) < total {
airdrop_lamports(client, &drone_addr.unwrap(), funding_key, total)?;
}
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);
let libra_pay_program_id = upload_payment_program(&funding_key, client);
// Generate another set of keypairs for move accounts.
// Still fund the solana ones which will be used for fees.
let seed = [0u8; 32];
let mut rnd = GenKeys::new(seed);
let move_keypairs = rnd.gen_n_keypairs(tx_count as u64 * 2);
fund_move_keys(
client,
funding_key,
&move_keypairs,
total / 3,
&libra_pay_program_id,
&libra_mint_program_id,
&libra_genesis_keypair,
);
move_keypairs_ret = Some((
libra_genesis_keypair,
libra_pay_program_id,
libra_mint_program_id,
move_keypairs,
));
// Give solana keys 1/3 and move keys 1/3 the lamports. Keep 1/3 for fees.
total /= 3;
}
info!("adding more lamports {}", account_desired_balance);
fund_keys(
client,
funding_key,
funding_pubkey,
&keypairs,
total,
fee_calculator.max_lamports_per_signature,
@@ -928,18 +642,17 @@ pub fn generate_and_fund_keypairs<T: Client>(
// 'generate_keypairs' generates extra keys to be able to have size-aligned funding batches for fund_keys.
keypairs.truncate(2 * tx_count);
Ok((keypairs, move_keypairs_ret, last_keypair_balance))
Ok((keypairs, last_keypair_balance))
}
#[cfg(test)]
mod tests {
use super::*;
use solana::cluster_info::FULLNODE_PORT_RANGE;
use solana::local_cluster::{ClusterConfig, LocalCluster};
use solana::validator::ValidatorConfig;
use solana_client::thin_client::create_client;
use solana_core::cluster_info::FULLNODE_PORT_RANGE;
use solana_core::validator::ValidatorConfig;
use solana_drone::drone::run_local_drone;
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
use solana_runtime::bank::Bank;
use solana_runtime::bank_client::BankClient;
use solana_sdk::client::SyncClient;
@@ -962,68 +675,47 @@ mod tests {
assert_eq!(should_switch_directions(20, 101), false);
}
fn test_bench_tps_local_cluster(config: Config) {
#[test]
fn test_bench_tps_local_cluster() {
solana_logger::setup();
const NUM_NODES: usize = 1;
let cluster = LocalCluster::new(&ClusterConfig {
node_stakes: vec![999_990; NUM_NODES],
cluster_lamports: 200_000_000,
cluster_lamports: 2_000_000,
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
native_instruction_processors: vec![solana_move_loader_program!()],
..ClusterConfig::default()
});
let drone_keypair = Keypair::new();
cluster.transfer(
&cluster.funding_keypair,
&drone_keypair.pubkey(),
100_000_000,
);
cluster.transfer(&cluster.funding_keypair, &drone_keypair.pubkey(), 1_000_000);
let (addr_sender, addr_receiver) = channel();
run_local_drone(drone_keypair, addr_sender, None);
let drone_addr = addr_receiver.recv_timeout(Duration::from_secs(2)).unwrap();
let mut config = Config::default();
config.tx_count = 100;
config.duration = Duration::from_secs(5);
let client = create_client(
(cluster.entry_point_info.rpc, cluster.entry_point_info.tpu),
FULLNODE_PORT_RANGE,
);
let (addr_sender, addr_receiver) = channel();
run_local_drone(drone_keypair, addr_sender, None);
let drone_addr = addr_receiver.recv_timeout(Duration::from_secs(2)).unwrap();
let lamports_per_account = 100;
let (keypairs, move_keypairs, _keypair_balance) = generate_and_fund_keypairs(
let (keypairs, _keypair_balance) = generate_and_fund_keypairs(
&client,
Some(drone_addr),
&config.id,
config.tx_count,
lamports_per_account,
config.use_move,
)
.unwrap();
let total = do_bench_tps(vec![client], config, keypairs, 0, move_keypairs);
let total = do_bench_tps(vec![client], config, keypairs, 0);
assert!(total > 100);
}
#[test]
fn test_bench_tps_local_cluster_solana() {
let mut config = Config::default();
config.tx_count = 100;
config.duration = Duration::from_secs(10);
test_bench_tps_local_cluster(config);
}
#[test]
fn test_bench_tps_local_cluster_move() {
let mut config = Config::default();
config.tx_count = 100;
config.duration = Duration::from_secs(20);
config.use_move = true;
test_bench_tps_local_cluster(config);
}
#[test]
fn test_bench_tps_bank_client() {
let (genesis_block, id) = create_genesis_block(10_000);
@@ -1035,11 +727,10 @@ mod tests {
config.tx_count = 10;
config.duration = Duration::from_secs(5);
let (keypairs, _move_keypairs, _keypair_balance) =
generate_and_fund_keypairs(&clients[0], None, &config.id, config.tx_count, 20, false)
.unwrap();
let (keypairs, _keypair_balance) =
generate_and_fund_keypairs(&clients[0], None, &config.id, config.tx_count, 20).unwrap();
do_bench_tps(clients, config, keypairs, 0, None);
do_bench_tps(clients, config, keypairs, 0);
}
#[test]
@@ -1050,8 +741,8 @@ mod tests {
let tx_count = 10;
let lamports = 20;
let (keypairs, _move_keypairs, _keypair_balance) =
generate_and_fund_keypairs(&client, None, &id, tx_count, lamports, false).unwrap();
let (keypairs, _keypair_balance) =
generate_and_fund_keypairs(&client, None, &id, tx_count, lamports).unwrap();
for kp in &keypairs {
assert_eq!(client.get_balance(&kp.pubkey()).unwrap(), lamports);
@@ -1068,8 +759,8 @@ mod tests {
let tx_count = 10;
let lamports = 20;
let (keypairs, _move_keypairs, _keypair_balance) =
generate_and_fund_keypairs(&client, None, &id, tx_count, lamports, false).unwrap();
let (keypairs, _keypair_balance) =
generate_and_fund_keypairs(&client, None, &id, tx_count, lamports).unwrap();
let max_fee = client
.get_recent_blockhash()

View File

@@ -22,7 +22,6 @@ pub struct Config {
pub write_to_client_file: bool,
pub read_from_client_file: bool,
pub target_lamports_per_signature: u64,
pub use_move: bool,
}
impl Default for Config {
@@ -41,7 +40,6 @@ impl Default for Config {
write_to_client_file: false,
read_from_client_file: false,
target_lamports_per_signature: FeeCalculator::default().target_lamports_per_signature,
use_move: false,
}
}
}
@@ -102,11 +100,6 @@ pub fn build_args<'a, 'b>() -> App<'a, 'b> {
.long("sustained")
.help("Use sustained performance mode vs. peak mode. This overlaps the tx generation with transfers."),
)
.arg(
Arg::with_name("use-move")
.long("use-move")
.help("Use Move language transactions to perform transfers."),
)
.arg(
Arg::with_name("tx_count")
.long("tx_count")
@@ -218,7 +211,5 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
args.target_lamports_per_signature = v.to_string().parse().expect("can't parse lamports");
}
args.use_move = matches.is_present("use-move");
args
}

View File

@@ -1,16 +1,12 @@
#[cfg(test)]
#[macro_use]
extern crate solana_move_loader_program;
mod bench;
mod cli;
use crate::bench::{
do_bench_tps, generate_and_fund_keypairs, generate_keypairs, Config, NUM_LAMPORTS_PER_ACCOUNT,
};
use solana_core::gossip_service::{discover_cluster, get_multi_client};
use solana::gossip_service::{discover_cluster, get_multi_client};
use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::Keypair;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
@@ -21,7 +17,7 @@ use std::process::exit;
pub const NUM_SIGNATURES_FOR_TXS: u64 = 100_000 * 60 * 60 * 24 * 7;
fn main() {
solana_logger::setup_with_filter("solana=info");
solana_logger::setup();
solana_metrics::set_panic_hook("bench-tps");
let matches = cli::build_args().get_matches();
@@ -41,7 +37,6 @@ fn main() {
write_to_client_file,
read_from_client_file,
target_lamports_per_signature,
use_move,
} = cli_config;
if write_to_client_file {
@@ -83,7 +78,7 @@ fn main() {
exit(1);
}
let (keypairs, move_keypairs, keypair_balance) = if read_from_client_file && !use_move {
let (keypairs, keypair_balance) = if read_from_client_file {
let path = Path::new(&client_ids_and_stake_file);
let file = File::open(path).unwrap();
@@ -96,11 +91,7 @@ fn main() {
keypairs.push(Keypair::from_bytes(&bytes).unwrap());
last_balance = balance;
});
// Sort keypairs so that do_bench_tps() uses the same subset of accounts for each run.
// This prevents the amount of storage needed for bench-tps accounts from creeping up
// across multiple runs.
keypairs.sort_by(|x, y| x.pubkey().to_string().cmp(&y.pubkey().to_string()));
(keypairs, None, last_balance)
(keypairs, last_balance)
} else {
generate_and_fund_keypairs(
&client,
@@ -108,7 +99,6 @@ fn main() {
&id,
tx_count,
NUM_LAMPORTS_PER_ACCOUNT,
use_move,
)
.unwrap_or_else(|e| {
eprintln!("Error could not fund keys: {:?}", e);
@@ -123,14 +113,7 @@ fn main() {
duration,
tx_count,
sustained,
use_move,
};
do_bench_tps(
vec![client],
config,
keypairs,
keypair_balance,
move_keypairs,
);
do_bench_tps(vec![client], config, keypairs, keypair_balance);
}

View File

@@ -1,18 +0,0 @@
+------------+
| Bank-Merkle|
+------------+
^ ^
/ \
+-----------------+ +-------------+
| Bank-Diff-Merkle| | Block-Merkle|
+-----------------+ +-------------+
^ ^
/ \
+------+ +--------------------------+
| Hash | | Previous Bank-Diff-Merkle|
+------+ +--------------------------+
^ ^
/ \
+---------------+ +---------------+
| Hash(Account1)| | Hash(Account2)|
+---------------+ +---------------+

View File

@@ -1,19 +0,0 @@
+---------------+
| Block-Merkle |
+---------------+
^ ^
/ \
+-------------+ +-------------+
| Entry-Merkle| | Entry-Merkle|
+-------------+ +-------------+
^ ^
/ \
+-------+ +-------+
| Hash | | Hash |
+-------+ +-------+
^ ^ ^ ^
/ | | \
+-----------------+ +-----------------+ +-----------------+ +---+
| Hash(T1, status)| | Hash(T2, status)| | Hash(T3, status)| | 0 |
+-----------------+ +-----------------+ +-----------------+ +---+

View File

@@ -3,4 +3,4 @@ set -e
cd "$(dirname "$0")"
make -j"$(nproc)" test
make -j"$(nproc)"

View File

@@ -4,14 +4,11 @@ MD_SRCS=$(wildcard src/*.md)
SVG_IMGS=$(BOB_SRCS:art/%.bob=src/img/%.svg) $(MSC_SRCS:art/%.msc=src/img/%.svg)
TARGET=html/index.html
TEST_STAMP=src/tests.ok
all: html/index.html
all: $(TARGET)
test: src/tests.ok
test: $(TEST_STAMP)
open: $(TEST_STAMP)
open: all
mdbook build --open
watch: $(SVG_IMGS)
@@ -29,11 +26,11 @@ src/%.md: %.md
@mkdir -p $(@D)
@cp $< $@
$(TEST_STAMP): $(TARGET)
src/tests.ok: $(SVG_IMGS) $(MD_SRCS)
mdbook test
touch $@
$(TARGET): $(SVG_IMGS) $(MD_SRCS)
html/index.html: src/tests.ok
mdbook build
clean:

View File

@@ -6,7 +6,8 @@
- [Getting Started](getting-started.md)
- [Testnet Participation](testnet-participation.md)
- [Example Client: Web Wallet](webwallet.md)
- [Testnet Replicator](testnet-replicator.md)
- [Example: Web Wallet](webwallet.md)
- [Programming Model](programs.md)
- [Example: Tic-Tac-Toe](tictactoe.md)
@@ -30,28 +31,11 @@
- [Gossip Service](gossip.md)
- [The Runtime](runtime.md)
- [Anatomy of a Transaction](transaction.md)
- [Running a Validator](running-validator.md)
- [Hardware Requirements](validator-hardware.md)
- [Choosing a Testnet](validator-testnet.md)
- [Installing the Validator Software](validator-software.md)
- [Starting a Validator](validator-start.md)
- [Staking](validator-stake.md)
- [Monitoring a Validator](validator-monitor.md)
- [Publishing Validator Info](validator-info.md)
- [Troubleshooting](validator-troubleshoot.md)
- [FAQ](validator-faq.md)
- [Running a Replicator](running-replicator.md)
- [API Reference](api-reference.md)
- [Transaction](transaction-api.md)
- [Instruction](instruction-api.md)
- [Blockstreamer](blockstreamer.md)
- [JSON RPC API](jsonrpc-api.md)
- [JavaScript API](javascript-api.md)
- [solana CLI](cli.md)
- [solana-wallet CLI](wallet.md)
- [Accepted Design Proposals](proposals.md)
- [Ledger Replication](ledger-replication-to-implement.md)
@@ -71,21 +55,18 @@
- [Economic Design MVP](ed_mvp.md)
- [References](ed_references.md)
- [Cluster Test Framework](cluster-test-framework.md)
- [Credit-only Accounts](credit-only-credit-debit-accounts.md)
- [Validator](validator-proposal.md)
- [Simple Payment and State Verification](simple-payment-and-state-verification.md)
- [Cross-Program Invocation](cross-program-invocation.md)
- [Implemented Design Proposals](implemented-proposals.md)
- [Blocktree](blocktree.md)
- [Cluster Software Installation and Updates](installer.md)
- [Deterministic Transaction Fees](transaction-fees.md)
- [Tower BFT](tower-bft.md)
- [Fork Selection](fork-selection.md)
- [Leader-to-Leader Transition](leader-leader-transition.md)
- [Leader-to-Validator Transition](leader-validator-transition.md)
- [Passive Stake Delegation and Rewards](passive-stake-delegation-and-rewards.md)
- [Persistent Account Storage](persistent-account-storage.md)
- [Reliable Vote Transmission](reliable-vote-transmission.md)
- [Repair Service](repair-service.md)
- [Testing Programs](testing-programs.md)
- [Credit-only Accounts](credit-only-credit-debit-accounts.md)
- [Embedding the Move Langauge](embedding-move.md)
- [Repair Service](repair-service.md)
- [Testing Programs](testing-programs.md)

View File

@@ -4,7 +4,7 @@ A validator votes on a PoH hash for two purposes. First, the vote indicates it
believes the ledger is valid up until that point in time. Second, since many
valid forks may exist at a given height, the vote also indicates exclusive
support for the fork. This document describes only the former. The latter is
described in [Tower BFT](tower-bft.md).
described in [fork selection](fork-selection.md).
## Current Design
@@ -50,11 +50,12 @@ log the time since the NewBlock transaction was submitted.
### Finality and Payouts
[Tower BFT](tower-bft.md) is the proposed fork selection algorithm. It proposes
that payment to miners be postponed until the *stack* of validator votes reaches
a certain depth, at which point rollback is not economically feasible. The vote
program may therefore implement Tower BFT. Vote instructions would need to
reference a global Tower account so that it can track cross-block state.
Locktower is the proposed [fork selection](fork-selection.md) algorithm. It
proposes that payment to miners be postponed until the *stack* of validator
votes reaches a certain depth, at which point rollback is not economically
feasible. The vote program may therefore implement locktower. Vote instructions
would need to reference a global locktower account so that it can track
cross-block state.
## Challenges

View File

@@ -1,111 +0,0 @@
# 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.

View File

@@ -2,15 +2,15 @@
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 remittance mechanisms are discussed below.
The main channels of participant remittances are referred to as protocol-based rewards and transaction fees. Protocol-based rewards are protocol-derived issuances from a protocol-defined, global inflation rate. These rewards will constitute the total reward delivered to replication clients and a portion of the total rewards for 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 join the network.
The main channels of participant remittances are referred to as protocol-based rewards and transaction fees. Protocol-based rewards are protocol-derived issuances from a network-controlled reserve of tokens (sometimes referred to as the mining pool). These rewards will constitute the total reward delivered to replication clients and a portion of the total rewards for 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 join 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.
These protocol-based rewards, to be distributed to participating validation and replication clients, are to be specified as annual interest rates calculated per, real-time, Solana epoch [DEFINITION]. As discussed further below, the issuance rates are determined as a function of total network validator staked percentage and total replication provided by replicators in each previous epoch. The choice for validator and replicator client rewards to be based on participation rates, rather than a global fixed inflation or interest rate, emphasizes a protocol priority of overall economic security, rather than monetary supply predictability. Due to Solanas hard total supply cap of 1B tokens and the bounds of client participant rates in the protocol, we believe that global interest, and supply issuance, scenarios should be able to be modeled with reasonable uncertainties.
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 continuous and long-term economic stability through partial burning of each transaction fee is also discussed below.
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 continuous and long-term funding of the mining pool through a pre-dedicated portion of transaction fees 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_rend_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.
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. 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="img/economic_design_infl_230719.png" alt="== Solana Economic Design Diagram ==" width="800"/></p>
<p style="text-align:center;"><img src="img/solana_economic_design.png" alt="== Solana Economic Design Diagram ==" width="800"/></p>
**Figure 1**: Schematic overview of Solana economic incentive design.

View File

@@ -1,3 +1,3 @@
## Validation-client Economics
Validator-clients are eligible to receive protocol-based (i.e. via inflation) 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. Additionally, these 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.
Validator-clients are eligible to receive protocol-based (i.e. via mining pool) rewards issued via stake-based annual interest rates by providing compute (CPU+GPU) resources to validate and vote on a given PoH state. These protocol-based rewards are determined through an algorithmic schedule as a function of total amount of Solana tokens staked in the system and duration since network launch (genesis block). Additionally, these clients may earn revenue through two types of transaction fees: state-validation transaction fees and pooled Proof-of-Replication (PoRep) transaction fees. The distribution of these two types of transaction fees to the participating validation set are designed independently as economic goals and attack vectors are unique between the state- generation/validation mechanism and the ledger replication/validation mechanism. For clarity, we separately describe the design and motivation of the three types of potential revenue streams for validation-clients below: state-validation protocol-based rewards, state-validation transaction fees and PoRep-validation transaction fees.

View File

@@ -2,8 +2,8 @@
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.2
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).
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 the distribution of the transaction fees associated with the submitted PoRep. 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). It was chosen not to include a distribution of these rewards to PoRep validators, and to rely only on the collection of PoRep attached transaction fees, due to the fact that the confluence of two participation incentive modes (state-validation inflation rate via global staked % and replication-validation rewards based on global redundancy factor) on the incentives of a single network participant (a validator-client) potentially opened up a significant incentive-driven attack surface area.
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.
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. However, because replication-client rewards are distributed in proportion to and only after submitted PoReps are validated, they are uniquely motivated for the inclusion and validation of their proofs. This pressure is expected to generate an adequate market economy between replication-clients and validation-clients. Additionally, transaction fees submitted with PoReps have no minimum amount pre-allocated to the mining pool, as do state-validation transaction fees.
There are various attack vectors available for colluding validation and replication clients, as 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).
There are various attack vectors available for colluding validation and replication clients, as described in detail below in [Economic Sustainability](ed_economic_sustainability). To protect against various collusion attack vectors, for a given epoch, PoRep transaction fees are pooled, and redistributed across participating validation-clients in proportion to the number of validated PoReps in the epoch less the number of invalidated PoReps [DIAGRAM]. 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

@@ -1,40 +1,46 @@
### State-validation protocol-based rewards
Validator-clients have two functional roles in the Solana network:
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.
* 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. 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))
Validator-client rewards for these services are to be distributed at the end of each Solana epoch. Compensation for validator-clients is provided via a protocol-based annual interest 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 non-PoRep transaction fee, less a protocol-specified amount that is returned to the mining pool (see [Validation-client State Transaction Fees](ed_vce_state_validation_transaction_fees.md)). PoRep transaction fees are not collected directly by the leader client but pooled and returned to the validator set in proportion to the number of successfully validated PoReps. (see [Replication-client Transaction Fees](ed_vce_replication_validation_transaction_fees.md))
The effective protocol-based annual interest rate (%) per epoch to be distributed to validation-clients is to be a function of:
The protocol-based annual interest-rate (%) per epoch to be distributed to validation-clients is to be a function of:
* the current global inflation rate, derived from the pre-determined dis-inflationary issuance schedule
* the current fraction of staked SOLs out of the current total circulating supply,
* the fraction of staked SOLs out of the current total circulating supply,
* the global time since the genesis block instantiation
* the up-time/participation [% of available slots that validator had opportunity to vote on] of a given validator over the previous epoch.
* the up-time/participation [% of available slots/blocks 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.
The first two factors are protocol parameters only (i.e. independent of validator behavior in a given epoch) and describe a global validation reward schedule designed to both incentivize early participation and optimal security in the network. This schedule sets a maximum annual validator-client interest rate per epoch.
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 an illustrative 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.
At any given point in time, this interest rate is pegged to a defined value given a specific % staked SOL out of the circulating supply (e.g. 10% interest rate when 66% of circulating SOL is staked). The interest rate adjusts as the square-root [TBD] of the % staked, leading to higher validation-client interest rates as the % staked drops below the targeted goal, thus incentivizing more participation leading to more security in the network. An example of such a schedule, for a specified point in time (e.g. network launch) is shown in **Table 1**.
<p style="text-align:center;"><img src="img/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.
| Percentage circulating supply staked [%] | Annual validator-client interest rate [%] |
| ---: | ---: |
| 5 | 13.87 |
| 15 | 13.31 |
| 25 | 12.73 |
| 35 | 12.12 |
| 45 | 11.48 |
| 55 | 10.80 |
| **66** | **10.00** |
| 75 | 9.29 |
| 85 | 8.44 |
<p style="text-align:center;"><img src="img/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**
**Table 1:** Example interest rate schedule based on % SOL staked out of circulating supply. In this case, interest rates are fixed at 10% for 66% of staked circulating supply
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 both 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**.
Over time, the interest rate, at any network staked percentage, will drop as described by an algorithmic schedule. Validation-client interest rates are designed to be higher in the early days of the network to incentivize participation and jumpstart the network economy. This mining-pool provided interest rate will reduce over time until a network-chosen baseline value is reached. This is 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 both state-validation and ledger storage replication (PoReps) are not accounted for here. A validation-client interest rate schedule as a function of % network staked and time is shown in** Figure 2**.
<!-- ![== Validation Client Interest Rates Figure ==](validation_client_interest_rates.png =250x) -->
<p style="text-align:center;"><img src="img/p_ex_interest.png" alt="drawing" width="800"/></p>
<p style="text-align:center;"><img src="img/validation_client_interest_rates.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.
**Figure 2:** In this example schedule, the annual interest rate [%] reduces at around 16.7% per year, until it reaches the long-term, fixed, 4% rate.
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.
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. Each epoch is comprised of XXX slots. The protocol-defined interest rate is then discounted by the log [TBD] of the % of slots a given validator submitted a vote on a PoH branch during that epoch, see **Figure XX**

View File

@@ -1,6 +1,6 @@
### 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:
Each message 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,
@@ -10,11 +10,11 @@ Each transaction sent through the network, to be processed by the current 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.
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 sent to the mining pool, with the resulting fee going to the current leader processing the transaction. These pooled fees, then re-enter the system through 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.
The intent of this design is to retain leader incentive to include as many transactions as possible within the leader-slot time, while providing a redistribution avenue that protects against "tax evasion" attacks (i.e. side-channel fee payments)<sup>[1](ed_referenced.md)</sup>. Constraints on the fixed portion of transaction fees going to the mining pool, to establish long-term economic sustainability, are established and discussed in detail in the [Economic Sustainability](ed_economic_sustainability.md) section.
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>.
This minimum, protocol-earmarked, 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 (e.g. 50% of a block's capacity), 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.
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.
Additionally, the minimum protocol captured fee can be a consideration in fork selection. In the case of a PoH fork with a malicious, censoring leader, we would expect the total procotol captured fee 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 fees on their fork themselves, thus potentially reducing the incentive to censor in the first place.

View File

@@ -1,66 +0,0 @@
# 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.

View File

@@ -55,7 +55,7 @@ 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).
[forks selection](fork-selection.md).
### Validator's View

View File

@@ -1,7 +1,7 @@
# Tower BFT
# Fork Selection
This design describes Solana's *Tower BFT* algorithm. It addresses the
following problems:
This design describes a *Fork Selection* algorithm. It addresses the following
problems:
* Some forks may not end up accepted by the super-majority of the cluster, and
voters need to recover from voting on such forks.

View File

@@ -22,7 +22,7 @@ gossip endpoint (a socket address).
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
recieves two records from the same source, it it updates its own copy with the
record with the most recent timestamp.
## Gossip Service Interface
@@ -34,8 +34,8 @@ 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
1. Duplication: if the message has been seen before, the node responds with
`PushMessagePrune` and drops the message
2. New data: if the message is new to the node
* Stores the new information with an updated version in its cluster info and
@@ -51,7 +51,7 @@ Upon receiving a push message, a node examines the message for:
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.
indication that there is another, faster 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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View File

@@ -12,7 +12,7 @@ updates is managed using an on-chain update manifest program.
#### 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
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.16.0/install/solana-install-init.sh | sh
```
This script will check github for the latest tagged release and download and run the
@@ -23,7 +23,7 @@ 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}
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.16.0/install/solana-install-init.sh | sh -s - ${init_args}
```
#### Fetch and run a pre-built installer from a Github release
@@ -31,7 +31,7 @@ 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
$ curl -o solana-install-init https://github.com/solana-labs/solana/releases/download/v0.16.0/solana-install-init-x86_64-apple-darwin
$ chmod +x ./solana-install-init
$ ./solana-install-init --help
```
@@ -130,7 +130,7 @@ FLAGS:
-V, --version Prints version information
OPTIONS:
-c, --config <PATH> Configuration file to use [default: .../Library/Preferences/solana/install.yml]
-c, --config <PATH> Configuration file to use [default: /Users/mvines/Library/Preferences/solana/install.yml]
SUBCOMMANDS:
deploy deploys a new update
@@ -152,8 +152,8 @@ 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]
-d, --data_dir <PATH> Directory to store install data [default: /Users/mvines/Library/Application Support/solana]
-u, --url <URL> JSON RPC URL for the solana cluster [default: https://api.testnet.solana.com/]
-p, --pubkey <PUBKEY> Public key of the update manifest [default: 9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp]
```

View File

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

@@ -1,13 +1,13 @@
# What is Solana?
Solana is an open source project implementing a new,
Solana is the name of an open source project that is implementing a new,
high-performance, permissionless blockchain. Solana is also the name of a
company headquartered in San Francisco that maintains the open source project.
# About this Book
This book describes the Solana open source project, a blockchain built from the
ground up for scale. The book covers why Solana is useful, how to use it, how it
ground up for scale. The book covers why it's useful, how to use it, how it
works, and why it will continue to work long after the company Solana closes
its doors. The goal of the Solana architecture is to demonstrate there exists a
set of software algorithms that when used in combination to implement a

View File

@@ -25,22 +25,14 @@ Methods
* [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)
* [getEpochVoteAccounts](#getepochvoteaccounts)
* [requestAirdrop](#requestairdrop)
* [sendTransaction](#sendtransaction)
* [startSubscriptionChannel](#startsubscriptionchannel)
@@ -177,69 +169,6 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "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
@@ -248,7 +177,7 @@ Returns all accounts owned by the provided program Pubkey
##### Results:
The result field will be an array of arrays. Each sub array will contain:
* `string` - the account Pubkey as base-58 encoded string
* `string` - a 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
@@ -315,25 +244,6 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "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
@@ -352,67 +262,7 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
{"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.
@@ -474,53 +324,31 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
---
### getVersion
Returns the current solana versions running on the node
### getEpochVoteAccounts
Returns the account info and associated stake for all the voting accounts in the current epoch.
##### 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:
The result field will be an array of JSON objects, each 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
* `stake` - the stake, in lamports, delegated to this vote account
* `commission`, a 32-bit integer used as a fraction (commission/MAX_U32) for rewards payout
##### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://localhost:8899
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochVoteAccounts"}' 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}
{"jsonrpc":"2.0","result":[{"commission":0,"nodePubkey":"Et2RaZJdJRTzTkodUwiHr4H6sLkVmijBFv8tkd7oSSFY","stake":42,"votePubkey":"B4CdWq3NBSoH2wYsVE1CaZSWPo2ZtopE4SJipQhZ3srF"}],"id":1}
```
---
### requestAirdrop
Requests an airdrop of lamports to a Pubkey

View File

@@ -96,7 +96,7 @@ 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).
[fork selection](fork-selection.md).
## Leader Schedule Generation Algorithm

View File

@@ -74,7 +74,7 @@ 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.
be maintained by receiving a AdvertiseStorageRecentBlockHash and checking with its bank/locktower 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.

View File

@@ -7,16 +7,14 @@ 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.
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.
The leader node's banking stage maintains a count of transactions that it recorded.
The dashboard displays the count averaged over 2 second period in the TPS time series
graph. The dashboard also shows per second mean, maximum and total TPS as a running
counter.
## Confirmation Time
@@ -28,4 +26,4 @@ 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.
as a time series graph.

View File

@@ -60,7 +60,7 @@ The read is satisfied by pointing to a memory-mapped location in the
## Root Forks
[Tower BFT](tower-bft.md) eventually selects a fork as a
The [fork selection algorithm](fork-selection.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

View File

@@ -1,35 +0,0 @@
# Running a Validator
This document describes how to participate in the Solana testnet as a
validator node.
Please note some of the information and instructions described here may change
in future releases, and documentation will be updated for mainnet participation.
## Overview
Solana currently maintains several testnets, each featuring a validator that can
serve as the entrypoint to the cluster for your validator.
Current testnet entrypoints:
- Stable, testnet.solana.com
- Beta, beta.testnet.solana.com
- Edge, edge.testnet.solana.com
Solana may launch special testnets for validator participation; we will provide
you with a specific entrypoint URL to use.
Prior to mainnet, the testnets may be running different versions of solana
software, which may feature breaking changes. For information on choosing a
testnet and finding software version info, jump to
[Choosing a Testnet](validator-testnet.md).
The testnets are configured to reset the ledger daily, or sooner,
should the hourly automated cluster sanity test fail.
There is a network explorer that shows the status of solana testnets available
at [http://explorer.solana.com/](https://explorer.solana.com/).
There is a **#validator-support** Discord channel available to reach other
testnet participants, [https://discord.gg/pquxPsq](https://discord.gg/pquxPsq).
Also we'd love it if you choose to register your validator node with us at
[https://forms.gle/LfFscZqJELbuUP139](https://forms.gle/LfFscZqJELbuUP139).

View File

@@ -1,172 +0,0 @@
# Simple Payment and State Verification
It is often useful to allow low resourced clients to participate in a Solana
cluster. Be this participation economic or contract execution, verification
that a client's activity has been accepted by the network is typically
expensive. This proposal lays out a mechanism for such clients to confirm that
their actions have been committed to the ledger state with minimal resource
expenditure and third-party trust.
## A Naive Approach
Validators store the signatures of recently confirmed transactions for a short
period of time to ensure that they are not processed more than once. Validators
provide a JSON RPC endpoint, which clients can use to query the cluster if a
transaction has been recently processed. Validators also provide a PubSub
notification, whereby a client registers to be notified when a given signature
is observed by the validator. While these two mechanisms allow a client to
verify a payment, they are not a proof and rely on completely trusting a
fullnode.
We will describe a way to minimize this trust using Merkle Proofs to anchor the
fullnode's response in the ledger, allowing the client to confirm on their own
that a sufficient number of their preferred validators have confirmed a
transaction. Requiring multiple validator attestations further reduces trust in
the fullnode, as it increases both the technical and economic difficulty of
compromising several other network participants.
## Light Clients
A 'light client' is a cluster participant that does not itself run a fullnode.
This light client would provide a level of security greater than trusting a
remote fullnode, without requiring the light client to spend a lot of resources
verifying the ledger.
Rather than providing transaction signatures directly to a light client, the
fullnode instead generates a Merkle Proof from the transaction of interest to
the root of a Merkle Tree of all transactions in the including block. This Merkle
Root is stored in a ledger entry which is voted on by validators, providing it
consensus legitimacy. The additional level of security for a light client depends
on an initial canonical set of validators the light client considers to be the
stakeholders of the cluster. As that set is changed, the client can update its
internal set of known validators with [receipts](#receipts). This may become
challenging with a large number of delegated stakes.
Fullnodes themselves may want to use light client APIs for performance reasons.
For example, during the initial launch of a fullnode, the fullnode may use a
cluster provided checkpoint of the state and verify it with a receipt.
## Receipts
A receipt is a minimal proof that; a transaction has been included in a block,
that the block has been voted on by the client's preferred set of validators and
that the votes have reached the desired confirmation depth.
The receipts for both state and payments start with a Merkle Path from the
value into a Bank-Merkle that has been voted on and included in the ledger. A
chain of PoH Entries containing subsequent validator votes, deriving from the
Bank-Merkle, is the confirmation proof.
Clients can examine this ledger data and compute the finality using Solana's fork
selection rules.
### Payment Merkle Path
A payment receipt is a data structure that contains a Merkle Path from a
transaction to the required set of validator votes.
An Entry-Merkle is a Merkle Root including all transactions in the entry, sorted
by signature.
<img alt="Block Merkle Diagram" src="img/spv-block-merkle.svg" class="center"/>
A Block-Merkle is a Merkle root of all the Entry-Merkles sequenced in the block.
Transaction status is necessary for the receipt because the state receipt is
constructed for the block. Two transactions over the same state can appear in
the block, and therefore, there is no way to infer from just the state whether a
transaction that is committed to the ledger has succeeded or failed in modifying
the intended state. It may not be necessary to encode the full status code, but
a single status bit to indicate the transaction's success.
### State Merkle Path
A state receipt provides a confirmation that a specific state is committed at the
end of the block. Inter-block state transitions do not generate a receipt.
For example:
* A sends 5 Lamports to B
* B spends 5 Lamports
* C sends 5 Lamports to A
At the end of the block, A and B are in the exact same starting state, and any
state receipt would point to the same value for A or B.
The Bank-Merkle is computed from the Merkle Tree of the new state changes, along
with the Previous Bank-Merkle, and the Block-Merkle.
<img alt="Bank Merkle Diagram" src="img/spv-bank-merkle.svg" class="center"/>
A state receipt contains only the state changes occurring in the block. A direct
Merkle Path to the current Bank-Merkle guarantees the state value at that bank
hash, but it cannot be used to generate a “current” receipt to the latest state
if the state modification occurred in some previous block. There is no guarantee
that the path provided by the validator is the latest one available out of all
the previous Bank-Merkles.
Clients that want to query the chain for a receipt of the "latest" state would
need to create a transaction that would update the Merkle Path for that account,
such as a credit of 0 Lamports.
### Validator Votes
Leaders should coalesce the validator votes by stake weight into a single entry.
This will reduce the number of entries necessary to create a receipt.
### Chain of Entries
A receipt has a PoH link from the payment or state Merkle Path root to a list of
consecutive validation votes.
It contains the following:
* State -> Bank-Merkle
or
* Transaction -> Entry-Merkle -> Block-Merkle -> Bank-Merkle
And a vector of PoH entries:
* Validator vote entries
* Ticks
* Light entries
```rust,ignore
/// This Entry definition skips over the transactions and only contains the
/// hash of the transactions used to modify PoH.
LightEntry {
/// The number of hashes since the previous Entry ID.
pub num_hashes: u64,
/// The SHA-256 hash `num_hashes` after the previous Entry ID.
hash: Hash,
/// The Merkle Root of the transactions encoded into the Entry.
entry_hash: Hash,
}
```
The light entries are reconstructed from Entries and simply show the entry Merkle
Root that was mixed in to the PoH hash, instead of the full transaction set.
Clients do not need the starting vote state. The [fork selection](book/src/fork-selection.md) algorithm is
defined such that only votes that appear after the transaction provide finality
for the transaction, and finality is independent of the starting state.
### Verification
A light client that is aware of the supermajority set validators can verify a
receipt by following the Merkle Path to the PoH chain. The Bank-Merkle is the
Merkle Root and will appear in votes included in an Entry. The light client can
simulate [fork selection](book/src/fork-selection.md) for the consecutive votes
and verify that the receipt is confirmed at the desired lockout threshold.
### Synthetic State
Synthetic state should be computed into the Bank-Merkle along with the bank
generated state.
For example:
* Epoch validator accounts and their stakes and weights.
* Computed fee rates
These values should have an entry in the Bank-Merkle. They should live under
known accounts, and therefore have an exact address in the Merkle Path.

View File

@@ -11,7 +11,7 @@ of getting its stake slashed. The economics are covered in [staking
rewards](staking-rewards.md). This chapter, on the other hand, describes the
underlying mechanics of its implementation.
## Basic Design
## Basic Besign
The general idea is that the validator owns a Vote account. The Vote account
tracks validator votes, counts validator generated credits, and provides any
@@ -20,7 +20,7 @@ stakes delegated to it and has no staking weight.
A separate Stake account (created by a staker) names a Vote account to which the
stake is delegated. Rewards generated are proportional to the amount of
lamports staked. The Stake account is owned by the staker only. Some portion of the lamports
lamports staked. The Stake account is owned by the staker only. Lamports
stored in this account are the stake.
## Passive Delegation
@@ -31,7 +31,7 @@ the Vote account or submitting votes to the account.
The total stake allocated to a Vote account can be calculated by the sum of
all the Stake accounts that have the Vote account pubkey as the
`StakeState::Stake::voter_pubkey`.
`StakeState::Delegate::voter_pubkey`.
## Vote and Stake accounts
@@ -46,15 +46,15 @@ program that its delegate has participated in validating the ledger.
VoteState is the current state of all the votes the validator has submitted to
the network. VoteState contains the following state information:
* `votes` - The submitted votes data structure.
* votes - The submitted votes data structure.
* `credits` - The total number of rewards this vote program has generated over its
* 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
* 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
* 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
@@ -71,17 +71,13 @@ count as stakes.
### VoteInstruction::AuthorizeVoteSigner(Pubkey)
* `account[0]` - RW - The VoteState
`VoteState::authorized_vote_signer` is set to to `Pubkey`, the transaction must by
signed by the Vote account's current `authorized_vote_signer`. <br>
`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.
`VoteState::authorized_vote_signer` is set to to `Pubkey`, instruction must by
signed by Pubkey
### VoteInstruction::Vote(Vec<Vote>)
* `account[0]` - RW - The VoteState
`VoteState::lockouts` and `VoteState::credits` are updated according to voting lockout rules see [Tower BFT](tower-bft.md)
`VoteState::lockouts` and `VoteState::credits` are updated according to voting lockout rules see [Fork Selection](fork-selection.md)
* `account[1]` - RO - A list of some N most recent slots and their hashes for the vote to be verified against.
@@ -89,16 +85,14 @@ the staker to be slashed.
### StakeState
A StakeState takes one of three forms, StakeState::Uninitialized, StakeState::Stake and StakeState::RewardsPool.
A StakeState takes one of two forms, StakeState::Delegate and StakeState::MiningPool.
### StakeState::Stake
### StakeState::Delegate
StakeState::Stake is the current delegation preference of the **staker** and
StakeState is the current delegation preference of the **staker**. StakeState
contains the following state information:
* Account::lamports - The lamports available for staking.
* `stake` - the staked amount (subject to warm up and cool down) for generating rewards, always less than or equal to Account::lamports
* Account::lamports - The staked lamports.
* `voter_pubkey` - The pubkey of the VoteState instance the lamports are
delegated to.
@@ -106,56 +100,56 @@ 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.
### StakeState::MiningPool
* `deactivated` - the epoch at which this stake will be completely de-activated, which is `cool down` epochs after StakeInstruction::Deactivate is issued.
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
StakeState::MiningPool instances as the cluster wide mining pools.
### StakeState::RewardsPool
* 256 StakeState::MiningPool are initialized, each with 1/256 number of mining pool
tokens stored as `Account::lamports`.
To avoid a single network wide lock or contention in redemption, 256 RewardsPools are part of genesis under pre-determined keys, each with std::u64::MAX credits to be able to satisfy redemptions according to point value.
The stakes and the MiningPool are accounts that are owned by the same `Stake`
program.
The Stakes and the RewardsPool are accounts that are owned by the same `Stake` program.
### StakeInstruction::Initialize
### StakeInstruction::DelegateStake(u64)
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. <br>
`StakeState::Stake::credits_observed` is initialized to `VoteState::credits`,<br>
`StakeState::Stake::voter_pubkey` is initialized to `account[1]`,<br>
`StakeState::Stake::stake` is initialized to the u64 passed as an argument above,<br>
`StakeState::Stake::activated` is initialized to current Bank epoch, and<br>
`StakeState::Stake::deactivated` is initialized to std::u64::MAX
* `account[0]` - RW - The StakeState::Delegate instance.
`StakeState::Delegate::credits_observed` is initialized to `VoteState::credits`.
`StakeState::Delegate::voter_pubkey` is initialized to `account[1]`
* `account[1]` - R - The VoteState instance.
* `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::RedeemVoteCredits
The staker or the owner of the Stake account sends a transaction with this
The Staker or the owner of the Stake account sends a transaction with this
instruction to claim rewards.
The Vote account and the Stake account pair maintain a lifetime counter of total
rewards generated and claimed. Rewards are paid according to a point value
supplied by the Bank from inflation. A `point` is one credit * one staked
lamport, rewards paid are proportional to the number of lamports staked.
The Vote account and the Stake account pair maintain a lifetime counter
of total rewards generated and claimed. When claiming rewards, the total lamports
deposited into the Stake account and as validator commission is proportional to
`VoteState::credits - StakeState::credits_observed`.
* `account[0]` - RW - The StakeState::Stake instance that is redeeming rewards.
* `account[1]` - R - The VoteState instance, must be the same as `StakeState::voter_pubkey`
* `account[2]` - RW - The StakeState::RewardsPool instance that will fulfill the request (picked at random).
* `account[3]` - R - sysvar::rewards account from the Bank that carries point value.
* `account[4]` - R - sysvar::stake_history account from the Bank that carries stake warmup/cooldown history
* `account[0]` - RW - The StakeState::MiningPool instance that will fulfill the
reward.
* `account[1]` - RW - The StakeState::Delegate instance that is redeeming votes
credits.
* `account[2]` - R - The VoteState instance, must be the same as
`StakeState::voter_pubkey`
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
`StakeState::Delgate.credits_observed`, and `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.
The total lamports paid is a percentage-rate of the lamports staked muiltplied by
the ratio of rewards being redeemed to rewards that could have been generated
during the rate period.
Any random MiningPool can be used to redeem the credits.
```rust,ignore
let credits_to_claim = vote_state.credits - stake_state.credits_observed;
@@ -163,28 +157,24 @@ 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
`StakeState::Delegate::credits_observed` is updated to the latest
`VoteState::credits` value.
### StakeInstruction::Deactivate
A staker may wish to withdraw from the network. To do so he must first deactivate his stake, and wait for cool down.
## Collecting network fees into the MiningPool
* `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
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.
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.
* 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.
## Authorizing a Vote Signer
### StakeInstruction::Withdraw(u64)
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, the transaction must be signed by this key.
* `account[1]` - RW - Account that should be credited with the withdrawn lamports.
* `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
`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.
## Benefits of the design
@@ -197,109 +187,9 @@ Lamports build up over time in a Stake account and any excess over activated sta
* 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.
## Example Callflow
<img alt="Passive Staking Callflow" src="img/passive-staking-callflow.svg" class="center"/>
## 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](staking-and-rewards.md).
### Basics
The network pays rewards from a portion of network [inflation](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.
At the end of each epoch, the total number of points earned during the epoch is
summed and used to divide the rewards portion of epoch inflation to arrive at a
point value. This value is recorded in the bank in a
[sysvar](terminology.md#sysvar) that maps epochs to point values.
During redemption, the stake program counts the points earned by the stake for
each epoch, multiplies that by the epoch's point value, and transfers lamports in
that amount from a rewards account into the stake and vote accounts according to
the vote account's commission setting.
### Economics
Point value for an epoch depends on aggregate network participation. If participation
in an epoch drops off, point values are higher for those that do participate.
### Earning credits
Validators earn one vote credit for every correct vote that exceeds maximum
lockout, i.e. every time the validator's vote account retires a slot from its
lockout list, making that vote a root for the node.
Stakers who have delegated to that validator earn points in proportion to their
stake. Points earned is the product of vote credits and stake.
### Stake warmup, cooldown, withdrawal
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 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.
Cooldown works the same way. Once a stake is deactivated, some part of it
is considered "effective", and also "deactivating". As the stake cools
down, it continues to earn rewards and be exposed to slashing, but it also
becomes available for withdrawal.
Bootstrap stakes are not subject to warmup.
Rewards are paid against the "effective" portion of the stake for that epoch.
#### Warmup example
Consider the situation of a single stake of 1,000 activated at epoch N, with
network warmup rate of 20%, and a quiescent total network stake at epoch N of 2,000.
At epoch N+1, the amount available to be activated for the network is 400 (20%
of 200), and at epoch N, this example stake is the only stake activating, and so
is entitled to all of the warmup room available.
|epoch | effective | activating | total effective | total activating|
|------|----------:|-----------:|----------------:|----------------:|
|N-1 | | | 2,000 | 0 |
|N | 0 | 1,000 | 2,000 | 1,000 |
|N+1 | 400 | 600 | 2,400 | 600 |
|N+2 | 880 | 120 | 2,880 | 120 |
|N+3 | 1000 | 0 | 3,000 | 0 |
Were 2 stakes (X and Y) to activate at epoch N, they would be awarded a portion of the 20%
in proportion to their stakes. At each epoch effective and activating for each stake is
a function of the previous epoch's state.
|epoch | X eff | X act | Y eff | Y act | total effective | total activating|
|------|----------:|-----------:|----------:|-----------:|----------------:|----------------:|
|N-1 | | | | | 2,000 | 0 |
|N | 0 | 1,000 | 0 | 200 | 2,000 | 1,200 |
|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
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

@@ -58,17 +58,6 @@ with a ledger interpretation that matches the leader's.
A gossip network connecting all [nodes](#node) of a [cluster](#cluster).
#### cooldown period
Some number of epochs after stake has been deactivated while it progressively
becomes available for withdrawal. During this period, the stake is considered to
be "deactivating". More info about:
[warmup and cooldown](stake-delegation-and-rewards.md#stake-warmup-cooldown-withdrawal)
#### credit
See [vote credit](#vote-credit).
#### data plane
A multicast network used to efficiently validate [entries](#entry) and gain
@@ -102,10 +91,6 @@ History](#proof-of-history).
The time, i.e. number of [slots](#slot), for which a [leader
schedule](#leader-schedule) is valid.
#### finality
When nodes representing 2/3rd of the stake have a common [root](#root).
#### fork
A [ledger](#ledger) derived from common entries but then diverged.
@@ -204,10 +189,6 @@ The number of [fullnodes](#fullnode) participating in a [cluster](#cluster).
See [Proof of History](#proof-of-history).
#### point
A weighted [credit](#credit) in a rewards regime. In the validator [rewards regime](staking-rewards.md), the number of points owed to a stake during redemption is the product of the [vote credits](#vote-credit) earned and the number of lamports staked.
#### program
The code that interprets [instructions](#instruction).
@@ -232,15 +213,6 @@ The public key of a [keypair](#keypair).
Storage mining client, stores some part of the ledger enumerated in blocks and
submits storage proofs to the chain. Not a full-node.
#### root
A [block](#block) or [slot](#slot) that has reached maximum [lockout](#lockout)
on a validator. The root is the highest block that is an ancestor of all active
forks on a validator. All ancestor blocks of a root are also transitively a
root. Blocks that are not an ancestor and not a descendant of the root are
excluded from consideration for consensus and can be discarded.
#### runtime
The component of a [fullnode](#fullnode) responsible for [program](#program)
@@ -291,11 +263,6 @@ hash values and a bit which says if this hash is valid or fake.
The number of keys and samples that a validator can verify each storage epoch.
#### sysvar
A synthetic [account](#account) provided by the runtime to allow programs to
access network state such as current tick height, rewards [points](#point) values, etc.
#### thin client
A type of [client](#client) that trusts it is communicating with a valid
@@ -343,15 +310,3 @@ that it ran, which can then be verified in less time than it took to produce.
#### vote
See [ledger vote](#ledger-vote).
#### vote credit
A reward tally for validators. A vote credit is awarded to a validator in its
vote account when the validator reaches a [root](#root).
#### warmup period
Some number of epochs after stake has been delegated while it progressively
becomes effective. During this period, the stake is considered to be
"activating". More info about:
[warmup and cooldown](stake-delegation-and-rewards.md#stake-warmup-cooldown-withdrawal)

View File

@@ -1,5 +1,273 @@
## Testnet Participation
This document describes how to participate in the testnet as a
validator node.
Participate in our testnet:
* [Running a Validator](running-validator.md)
* [Running a Replicator](running-replicator.md)
Please note some of the information and instructions described here may change
in future releases.
### Overview
The testnet features a validator running at testnet.solana.com, which
serves as the entrypoint to the cluster for your validator.
Additionally there is a blockexplorer available at
[http://testnet.solana.com/](http://testnet.solana.com/).
The testnet is configured to reset the ledger daily, or sooner
should the hourly automated cluster sanity test fail.
There is a **#validator-support** Discord channel available to reach other
testnet participants, [https://discord.gg/pquxPsq](https://discord.gg/pquxPsq).
Also we'd love it if you choose to register your validator node with us at
[https://forms.gle/LfFscZqJELbuUP139](https://forms.gle/LfFscZqJELbuUP139).
### Machine Requirements
Since the testnet is not intended for stress testing of max transaction
throughput, a higher-end machine with a GPU is not necessary to participate.
However ensure the machine used is not behind a residential NAT to avoid NAT
traversal issues. A cloud-hosted machine works best. **Ensure that IP ports
8000 through 10000 are not blocked for Internet inbound and outbound traffic.**
Prebuilt binaries are available for Linux x86_64 (Ubuntu 18.04 recommended).
MacOS or WSL users may build from source.
For a performance testnet with many transactions we have some preliminary recommended setups:
| | Low end | Medium end | High end | Notes |
| --- | ---------|------------|----------| -- |
| CPU | AMD Threadripper 1900x | AMD Threadripper 2920x | AMD Threadripper 2950x | Consider a 10Gb-capable motherboard with as many PCIe lanes and m.2 slots as possible. |
| RAM | 16GB | 32GB | 64GB | |
| OS Drive | Samsung 860 Evo 2TB | Samsung 860 Evo 4TB | Samsung 860 Evo 4TB | Or equivalent SSD |
| Accounts Drive(s) | None | Samsung 970 Pro 1TB | 2x Samsung 970 Pro 1TB | |
| GPU | 4x Nvidia 1070 or 2x Nvidia 1080 Ti or 2x Nvidia 2070 | 2x Nvidia 2080 Ti | 4x Nvidia 2080 Ti | Any number of cuda-capable GPUs are supported on Linux platforms. |
#### GPU Requirements
CUDA is required to make use of the GPU on your system. The provided Solana
release binaries are built on Ubuntu 18.04 with <a
href="https://developer.nvidia.com/cuda-toolkit-archive">CUDA Toolkit 10.1
update 1"</a>. If your machine is using a different CUDA version then you will
need to rebuild from source.
#### Confirm The Testnet Is Reachable
Before attaching a validator node, sanity check that the cluster is accessible
to your machine by running some simple commands. If any of the commands fail,
please retry 5-10 minutes later to confirm the testnet is not just restarting
itself before debugging further.
Fetch the current transaction count over JSON RPC:
```bash
$ curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://testnet.solana.com:8899
```
Inspect the blockexplorer at [http://testnet.solana.com/](http://testnet.solana.com/) for activity.
View the [metrics dashboard](
https://metrics.solana.com:3000/d/testnet-beta/testnet-monitor-beta?var-testnet=testnet)
for more detail on cluster activity.
### Validator Setup
#### Obtaining The Software
##### Bootstrap with `solana-install`
The `solana-install` tool can be used to easily install and upgrade the cluster
software on Linux x86_64 and mac OS systems.
```bash
$ export SOLANA_RELEASE=v0.16.5 # skip this line to install the latest release
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.16.5/install/solana-install-init.sh | sh -s
```
Alternatively build the `solana-install` program from source and run the
following command to obtain the same result:
```bash
$ solana-install init
```
After a successful install, `solana-install update` may be used to easily update the cluster
software to a newer version at any time.
##### Download Prebuilt Binaries
If you would rather not use `solana-install` to manage the install, you can manually download and install the binaries.
###### Linux
Download the binaries by navigating to
[https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest),
download **solana-release-x86_64-unknown-linux-gnu.tar.bz2**, then extract the
archive:
```bash
$ tar jxf solana-release-x86_64-unknown-linux-gnu.tar.bz2
$ cd solana-release/
$ export PATH=$PWD/bin:$PATH
```
###### mac OS
Download the binaries by navigating to
[https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest),
download **solana-release-x86_64-apple-darwin.tar.bz2**, then extract the
archive:
```bash
$ tar jxf solana-release-x86_64-apple-darwin.tar.bz2
$ cd solana-release/
$ export PATH=$PWD/bin:$PATH
```
##### Build From Source
If you are unable to use the prebuilt binaries or prefer to build it yourself
from source, navigate to
[https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest),
and download the **Source Code** archive. Extract the code and build the
binaries with:
```bash
$ ./scripts/cargo-install-all.sh .
$ export PATH=$PWD/bin:$PATH
```
If building for CUDA, include the `cuda` feature flag as well:
```bash
$ ./scripts/cargo-install-all.sh . cuda
$ export PATH=$PWD/bin:$PATH
```
### Starting The Validator
Sanity check that you are able to interact with the cluster by receiving a small
airdrop of lamports from the testnet drone:
```bash
$ solana-wallet airdrop 123
$ solana-wallet balance
```
Also try running following command to join the gossip network and view all the other nodes in the cluster:
```bash
$ solana-gossip --entrypoint testnet.solana.com:8001 spy
# Press ^C to exit
```
Now configure a key pair for your validator by running:
```bash
$ solana-keygen new -o ~/validator-keypair.json
```
Then use one of the following commands, depending on your installation
choice, to start the node:
If this is a `solana-install`-installation:
```bash
$ clear-config.sh
$ validator.sh --identity ~/validator-keypair.json --poll-for-new-genesis-block testnet.solana.com
```
Alternatively, the `solana-install run` command can be used to run the validator
node while periodically checking for and applying software updates:
```bash
$ clear-config.sh
$ solana-install run validator.sh -- --identity ~/validator-keypair.json --poll-for-new-genesis-block testnet.solana.com
```
If you built from source:
```bash
$ USE_INSTALL=1 ./multinode-demo/clear-config.sh
$ USE_INSTALL=1 ./multinode-demo/validator.sh --identity ~/validator-keypair.json --poll-for-new-genesis-block testnet.solana.com
```
#### Enabling CUDA
By default CUDA is disabled. If your machine has a GPU with CUDA installed,
define the SOLANA_CUDA flag in your environment *before* running any of the
previusly mentioned commands
```bash
$ export SOLANA_CUDA=1
```
When your validator is started look for the following log message to indicate that CUDA is enabled:
`"[<timestamp> solana::validator] CUDA is enabled"`
#### Controlling local network port allocation
By default the validator will dynamically select available network ports in the
8000-10000 range, and may be overridden with `--dynamic-port-range`. For
example, `validator.sh --dynamic-port-range 11000-11010 ...` will restrict the
validator to ports 11000-11011.
### Validator Monitoring
When `validator.sh` starts, it will output a validator configuration that looks
similar to:
```bash
======================[ validator configuration ]======================
identity pubkey: 4ceWXsL3UJvn7NYZiRkw7NsryMpviaKBDYr8GK7J61Dm
vote pubkey: 2ozWvfaXQd1X6uKh8jERoRGApDqSqcEy6fF1oN13LL2G
ledger: ...
accounts: ...
======================================================================
```
The **identity pubkey** for your validator can also be found by running:
```bash
$ solana-keygen pubkey ~/validator-keypair.json
```
From another console, confirm the IP address and **identity pubkey** of your validator is visible in the
gossip network by running:
```bash
$ solana-gossip --entrypoint testnet.solana.com:8001 spy
```
Provide the **vote pubkey** to the `solana-wallet show-vote-account` command to view
the recent voting activity from your validator:
```bash
$ solana-wallet show-vote-account 2ozWvfaXQd1X6uKh8jERoRGApDqSqcEy6fF1oN13LL2G
```
The vote pubkey for the validator can also be found by running:
```bash
# If this is a `solana-install`-installation run:
$ solana-keygen pubkey ~/.local/share/solana/install/active_release/config-local/validator-vote-keypair.json
# Otherwise run:
$ solana-keygen pubkey ./config-local/validator-vote-keypair.json
```
#### Validator Metrics
Metrics are available for local monitoring of your validator.
Docker must be installed and the current user added to the docker group. Then
download `solana-metrics.tar.bz2` from the Github Release and run
```bash
$ tar jxf solana-metrics.tar.bz2
$ cd solana-metrics/
$ ./start.sh
```
A local InfluxDB and Grafana instance is now running on your machine. Define
`SOLANA_METRICS_CONFIG` in your environment as described at the end of the
`start.sh` output and restart your validator.
Metrics should now be streaming and visible from your local Grafana dashboard.
#### Publishing Validator Info
You can publish your validator information to the chain to be publicly visible
to other users.
Run the solana-validator-info CLI to populate a validator-info account:
```bash
$ solana-validator-info publish ~/validator-keypair.json <VALIDATOR_NAME> <VALIDATOR_INFO_ARGS>
```
Optional fields for VALIDATOR_INFO_ARGS:
* Website
* Keybase ID
* Details
##### Keybase
Including a Keybase ID allows client applications (like the Solana Network
Explorer) to automatically pull in your validator public profile, including
cryptographic proofs, brand identity, etc. To connect your validator pubkey with
Keybase:
1. Join https://keybase.io/ and complete the profile for your validator
2. Add your validator **identity pubkey** to Keybase:
* Create an empty file on your local computer called `solana_pubkey_<PUBKEY>`
* In Keybase, navigate to the Files section, and upload your pubkey file to
your public folder: `/keybase/public/<KEYBASE_ID>`
* To check your pubkey, ensure you can successfully browse to
`https://keybase.pub/<KEYBASE_ID>/solana_pubkey_<PUBKEY>`
3. Add or update your `solana-validator-info` with your Keybase ID. The CLI will
verify the `solana_pubkey_<PUBKEY>` file

View File

@@ -1,4 +1,4 @@
## Running a Replicator
## Testnet Replicator
This document describes how to setup a replicator in the testnet
Please note some of the information and instructions described here may change
@@ -53,7 +53,8 @@ software.
##### Linux and mac OS
```bash
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.1/install/solana-install-init.sh | sh -s
$ export SOLANA_RELEASE=v0.16.0 # skip this line to install the latest release
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.16.0/install/solana-install-init.sh | sh -s
```
Alternatively build the `solana-install` program from source and run the
@@ -129,10 +130,10 @@ $ export STORAGE_IDENTITY=$(solana-keygen pubkey storage-keypair.json)
```
Then set up the storage accounts for your replicator by running:
```bash
$ solana --keypair replicator-keypair.json airdrop 100000
$ solana --keypair replicator-keypair.json create-replicator-storage-account $REPLICATOR_IDENTITY $STORAGE_IDENTITY
$ solana-wallet --keypair replicator-keypair.json airdrop 100000
$ solana-wallet --keypair replicator-keypair.json create-replicator-storage-account $REPLICATOR_IDENTITY $STORAGE_IDENTITY
```
Note: Every time the testnet restarts, run the steps to setup the replicator accounts again.
Note: Every time the testnet restarts, run the wallet steps to setup the replicator accounts again.
To start the replicator:
```bash
@@ -146,8 +147,8 @@ gossip network by running:
$ solana-gossip --entrypoint testnet.solana.com:8001 spy
```
Provide the **storage account pubkey** to the `solana show-storage-account` command to view
Provide the **storage account pubkey** to the `solana-wallet show-storage-account` command to view
the recent mining activity from your replicator:
```bash
$ solana --keypair storage-keypair.json show-storage-account $STORAGE_IDENTITY
$ solana-wallet --keypair storage-keypair.json show-storage-account $STORAGE_IDENTITY
```

View File

@@ -1,48 +0,0 @@
# The Transaction
### Components of a `Transaction`
* **Transaction:**
* **message:** Defines the transaction
* **header:** Details the account types of and signatures required by
the transaction
* **num_required_signatures:** The total number of signatures
required to make the transaction valid.
* **num_credit_only_signed_accounts:** The last
`num_credit_only_signed_accounts` signatures refer to signing
credit only accounts. Credit only accounts can be used concurrently
by multiple parallel transactions, but their balance may only be
increased, and their account data is read-only.
* **num_credit_only_unsigned_accounts:** The last
`num_credit_only_unsigned_accounts` pubkeys in `account_keys` refer
to non-signing credit only accounts
* **account_keys:** List of pubkeys used by the transaction, including
by the instructions and for signatures. The first
`num_required_signatures` pubkeys must sign the transaction.
* **recent_blockhash:** The ID of a recent ledger entry. Validators will
reject transactions with a `recent_blockhash` that is too old.
* **instructions:** A list of [instructions](instruction.md) that are
run sequentially and committed in one atomic transaction if all
succeed.
* **signatures:** A list of signatures applied to the transaction. The
list is always of length `num_required_signatures`, and the signature
at index `i` corresponds to the pubkey at index `i` in `account_keys`.
The list is initialized with empty signatures (i.e. zeros), and
populated as signatures are added.
### Transaction Signing
A `Transaction` is signed by using an ed25519 keypair to sign the
serialization of the `message`. The resulting signature is placed at the
index of `signatures` matching the index of the keypair's pubkey in
`account_keys`.
### Transaction Serialization
`Transaction`s (and their `message`s) are serialized and deserialized
using the [bincode](https://crates.io/crates/bincode) crate with a
non-standard vector serialization that uses only one byte for the length
if it can be encoded in 7 bits, 2 bytes if it fits in 14 bits, or 3
bytes if it requires 15 or 16 bits. The vector serialization is defined
by Solana's
[short-vec](https://github.com/solana-labs/solana/blob/master/sdk/src/short_vec.rs).

View File

@@ -1,43 +0,0 @@
# Anatomy of a Transaction
Transactions encode lists of instructions that are executed
sequentially, and only committed if all the instructions complete
successfully. All account states are reverted upon the failure of a
transaction. Each Transaction details the accounts used, including which
must sign and which are credit only, a recent blockhash, the
instructions, and any signatures.
## Accounts and Signatures
Each transaction explicitly lists all accounts that it needs access to.
This includes accounts that are transferring tokens, accounts whose user
data is being modified, and the program accounts that are being called
by the instructions. Each account that is not an executable program can
be marked as a requiring a signature and/or as credit only. All accounts
marked as signers must have a valid signature in the transaction's list
of signatures before the transaction is considered valid. Any accounts
marked as credit only may only have their token value increased, and
their user data is read only. Accounts are locked by the runtime,
ensuring that they are not modified by a concurrent program while the
transaction is running. Credit only accounts can safely be shared, so
the runtime will allow multiple concurrent credit only locks on an
account.
## Recent Blockhash
A Transaction includes a recent blockhash to prevent duplication and to
give transactions lifetimes. Any transaction that is completely
identical to a previous one is rejected, so adding a newer blockhash
allows multiple transactions to repeat the exact same action.
Transactions also have lifetimes that are defined by the blockhash, as
any transaction whose blockhash is too old will be rejected.
## Instructions
Each instruction specifies a single program account (which must be
marked executable), a subset of the transaction's accounts that should
be passed to the program, and a data byte array instruction that is
passed to the program. The program interprets the data array and
operates on the accounts specified by the instructions. The program can
return successfully, or with an error code. An error return causes the
entire transaction to fail immediately.

View File

@@ -1,2 +0,0 @@
# Validator FAQ
Coming soon...

View File

@@ -1,28 +0,0 @@
# Validator Hardware Requirements
Since the testnet is not intended for stress testing of max transaction
throughput, a higher-end machine with a GPU is not necessary to participate.
However ensure the machine used is not behind a residential NAT to avoid NAT
traversal issues. A cloud-hosted machine works best. **Ensure that IP ports
8000 through 10000 are not blocked for Internet inbound and outbound traffic.**
Prebuilt binaries are available for Linux x86_64 (Ubuntu 18.04 recommended).
MacOS or WSL users may build from source.
## Recommended Setups
For a performance testnet with many transactions we have some preliminary recommended setups:
| | Low end | Medium end | High end | Notes |
| --- | ---------|------------|----------| -- |
| CPU | AMD Threadripper 1900x | AMD Threadripper 2920x | AMD Threadripper 2950x | Consider a 10Gb-capable motherboard with as many PCIe lanes and m.2 slots as possible. |
| RAM | 16GB | 32GB | 64GB | |
| OS Drive | Samsung 860 Evo 2TB | Samsung 860 Evo 4TB | Samsung 860 Evo 4TB | Or equivalent SSD |
| Accounts Drive(s) | None | Samsung 970 Pro 1TB | 2x Samsung 970 Pro 1TB | |
| GPU | 4x Nvidia 1070 or 2x Nvidia 1080 Ti or 2x Nvidia 2070 | 2x Nvidia 2080 Ti | 4x Nvidia 2080 Ti | Any number of cuda-capable GPUs are supported on Linux platforms. |
## GPU Requirements
CUDA is required to make use of the GPU on your system. The provided Solana
release binaries are built on Ubuntu 18.04 with <a
href="https://developer.nvidia.com/cuda-toolkit-archive">CUDA Toolkit 10.1
update 1"</a>. If your machine is using a different CUDA version then you will
need to rebuild from source.

View File

@@ -1,31 +0,0 @@
# Publishing Validator Info
You can publish your validator information to the chain to be publicly visible
to other users.
## Run solana-validator-info
Run the solana-validator-info CLI to populate a validator-info account:
```bash
$ solana-validator-info publish ~/validator-keypair.json <VALIDATOR_NAME> <VALIDATOR_INFO_ARGS>
```
Optional fields for VALIDATOR_INFO_ARGS:
* Website
* Keybase Username
* Details
## Keybase
Including a Keybase username allows client applications (like the Solana Network
Explorer) to automatically pull in your validator public profile, including
cryptographic proofs, brand identity, etc. To connect your validator pubkey with
Keybase:
1. Join https://keybase.io/ and complete the profile for your validator
2. Add your validator **identity pubkey** to Keybase:
* Create an empty file on your local computer called `validator-<PUBKEY>`
* In Keybase, navigate to the Files section, and upload your pubkey file to
a `solana` subdirectory in your public folder: `/keybase/public/<KEYBASE_USERNAME>/solana`
* To check your pubkey, ensure you can successfully browse to
`https://keybase.pub/<KEYBASE_USERNAME>/solana/validator-<PUBKEY>`
3. Add or update your `solana-validator-info` with your Keybase username. The
CLI will verify the `validator-<PUBKEY>` file

View File

@@ -1,106 +0,0 @@
# Validator Monitoring
When `validator.sh` starts, it will output a validator configuration that looks
similar to:
```bash
======================[ validator configuration ]======================
identity pubkey: 4ceWXsL3UJvn7NYZiRkw7NsryMpviaKBDYr8GK7J61Dm
vote pubkey: 2ozWvfaXQd1X6uKh8jERoRGApDqSqcEy6fF1oN13LL2G
ledger: ...
accounts: ...
======================================================================
```
## Check Gossip
The **identity pubkey** for your validator can also be found by running:
```bash
$ solana-keygen pubkey ~/validator-keypair.json
```
From another console, confirm the IP address and **identity pubkey** of your
validator is visible in the gossip network by running:
```bash
$ solana-gossip --entrypoint testnet.solana.com:8001 spy
```
## Check Vote Activity
The vote pubkey for the validator can be found by running:
```bash
$ solana-keygen pubkey ~/validator-vote-keypair.json
```
Provide the **vote pubkey** to the `solana show-vote-account` command to view
the recent voting activity from your validator:
```bash
$ solana show-vote-account 2ozWvfaXQd1X6uKh8jERoRGApDqSqcEy6fF1oN13LL2G
```
## Check Your Balance
Your lamport balance should decrease by the transaction fee amount as your
validator submits votes, and increase after serving as the leader:
```bash
$ solana balance
```
## Check Slot Number
After your validator boots, it may take some time to catch up with the cluster.
Use the `get-slot` command to view the current slot that the cluster is
processing:
```bash
$ solana get-slot
```
The current slot that your validator is processing can then been seen with:
```bash
$ solana --url http://127.0.0.1:8899 get-slot
```
Until your validator has caught up, it will not be able to vote successfully and
stake cannot be delegated to it.
Also if you find the cluster's slot advancing faster than yours, you will likely
never catch up. This typically implies some kind of networking issue between
your validator and the rest of the cluster.
## Get Cluster Info
There are several useful JSON-RPC endpoints for monitoring your validator on the
cluster, as well as the health of the cluster:
```bash
# Similar to solana-gossip, you should see your validator in the list of cluster nodes
$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://testnet.solana.com:8899
# If your validator is properly voting, it should appear in the list of `current` vote accounts. If staked, `stake` should be > 0
$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://testnet.solana.com:8899
# Returns the current leader schedule
$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://testnet.solana.com:8899
# Returns info about the current epoch. slotIndex should progress on subsequent calls.
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://testnet.solana.com:8899
```
## Validator Metrics
Metrics are available for local monitoring of your validator.
Docker must be installed and the current user added to the docker group. Then
download `solana-metrics.tar.bz2` from the Github Release and run
```bash
$ tar jxf solana-metrics.tar.bz2
$ cd solana-metrics/
$ ./start.sh
```
A local InfluxDB and Grafana instance is now running on your machine. Define
`SOLANA_METRICS_CONFIG` in your environment as described at the end of the
`start.sh` output and restart your validator.
Metrics should now be streaming and visible from your local Grafana dashboard.
## Timezone For Log Messages
Log messages emitted by your validator include a timestamp. When sharing logs
with others to help triage issues, that timestamp can cause confusion as it does
not contain timezone information.
To make it easier to compare logs between different sources we request that
everybody use Pacific Time on their validator nodes. In Linux this can be
accomplished by running:
```bash
$ sudo ln -sf /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
```

View File

@@ -1,63 +0,0 @@
# Installing the Validator Software
## Bootstrap with `solana-install`
The `solana-install` tool can be used to easily install and upgrade the validator
software on Linux x86_64 and mac OS systems.
```bash
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.0/install/solana-install-init.sh | sh -s
```
Alternatively build the `solana-install` program from source and run the
following command to obtain the same result:
```bash
$ solana-install init
```
After a successful install, `solana-install update` may be used to easily update the cluster
software to a newer version at any time.
## Download Prebuilt Binaries
If you would rather not use `solana-install` to manage the install, you can manually download and install the binaries.
### Linux
Download the binaries by navigating to
[https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest),
download **solana-release-x86_64-unknown-linux-gnu.tar.bz2**, then extract the
archive:
```bash
$ tar jxf solana-release-x86_64-unknown-linux-gnu.tar.bz2
$ cd solana-release/
$ export PATH=$PWD/bin:$PATH
```
### mac OS
Download the binaries by navigating to
[https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest),
download **solana-release-x86_64-apple-darwin.tar.bz2**, then extract the
archive:
```bash
$ tar jxf solana-release-x86_64-apple-darwin.tar.bz2
$ cd solana-release/
$ export PATH=$PWD/bin:$PATH
```
## Build From Source
If you are unable to use the prebuilt binaries or prefer to build it yourself
from source, navigate to
[https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest),
and download the **Source Code** archive. Extract the code and build the
binaries with:
```bash
$ ./scripts/cargo-install-all.sh .
$ export PATH=$PWD/bin:$PATH
```
If building for CUDA (Linux only), fetch the perf-libs first then include the
`cuda` feature flag when building:
```bash
$ ./fetch-perf-libs.sh
$ source target/perf-libs/env.sh
$ ./scripts/cargo-install-all.sh . cuda
$ export PATH=$PWD/bin:$PATH
```

View File

@@ -1,41 +0,0 @@
## Staking a Validator
When your validator starts, it will have no stake, which means it will be
ineligible to become leader.
Adding stake can be accomplished by using the `solana` CLI
First create a stake account keypair with `solana-keygen`:
```bash
$ solana-keygen new -o ~/validator-config/stake-keypair.json
```
and use the cli's `delegate-stake` command to stake your validator with 42 lamports:
```bash
$ solana delegate-stake ~/validator-config/stake-keypair.json ~/validator-vote-keypair.json 42
```
Note that stakes need to warm up, and warmup increments are applied at Epoch boundaries, so it can take an hour
or more for the change to fully take effect.
Assuming your node is voting, now you're up and running and generating validator rewards. You'll want
to periodically redeem/claim your rewards:
```bash
$ solana-wallet redeem-vote-credits ~/validator-config/stake-keypair.json ~/validator-vote-keypair.json
```
The rewards lamports earned are split between your stake account and the vote account according to the
commission rate set in the vote account.
Stake can be deactivated by running:
```bash
$ solana deactivate-stake ~/validator-config/stake-keypair.json ~/validator-vote-keypair.json
```
The stake will cool down, deactivate over time. While cooling down, your stake will continue to earn
rewards.
Note that a stake account may only be used once, so after deactivation, use the
cli's `withdraw-stake` command to recover the previously staked lamports.
Be sure and redeem your credits before withdrawing all your lamports.
Once the account is fully withdrawn, the account is destroyed.

View File

@@ -1,112 +0,0 @@
# Starting a Validator
## Confirm The Testnet Is Reachable
Before attaching a validator node, sanity check that the cluster is accessible
to your machine by running some simple commands. If any of the commands fail,
please retry 5-10 minutes later to confirm the testnet is not just restarting
itself before debugging further.
Fetch the current transaction count over JSON RPC:
```bash
$ curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://testnet.solana.com:8899
```
Inspect the network explorer at
[https://explorer.solana.com/](https://explorer.solana.com/) for activity.
View the [metrics dashboard](
https://metrics.solana.com:3000/d/testnet-beta/testnet-monitor-beta?var-testnet=testnet)
for more detail on cluster activity.
## Confirm your Installation
Sanity check that you are able to interact with the cluster by receiving a small
airdrop of lamports from the testnet drone:
```bash
$ solana set --url http://testnet.solana.com:8899
$ solana get
$ solana airdrop 123
$ solana balance
```
Also try running following command to join the gossip network and view all the
other nodes in the cluster:
```bash
$ solana-gossip --entrypoint testnet.solana.com:8001 spy
# Press ^C to exit
```
## Start your Validator
Create an identity keypair for your validator by running:
```bash
$ solana-keygen new -o ~/validator-keypair.json
```
### Wallet Configuration
You can set solana configuration to use your validator keypair for all
following commands:
```bash
$ solana set --keypair ~/validator-keypair.json
```
**All following solana commands assume you have set `--keypair` config to
**your validator identity keypair.**
If you haven't, you will need to add the `--keypair` argument to each command, like:
```bash
$ solana --keypair ~/validator-keypair.json airdrop 1000
```
(You can always override the set configuration by explicitly passing the
`--keypair` argument with a command.)
### Validator Start
Airdrop yourself some lamports to get started:
```bash
$ solana airdrop 1000
```
Your validator will need a vote account. Create it now with the following
commands:
```bash
$ solana-keygen new -o ~/validator-vote-keypair.json
$ solana create-vote-account ~/validator-vote-keypair.json ~/validator-keypair.json 1
```
Then use one of the following commands, depending on your installation
choice, to start the node:
If this is a `solana-install`-installation:
```bash
$ validator.sh --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 --poll-for-new-genesis-block --entrypoint testnet.solana.com
```
Alternatively, the `solana-install run` command can be used to run the validator
node while periodically checking for and applying software updates:
```bash
$ solana-install run validator.sh -- --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 --poll-for-new-genesis-block --entrypoint testnet.solana.com
```
If you built from source:
```bash
$ NDEBUG=1 USE_INSTALL=1 ./multinode-demo/validator.sh --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --rpc-port 8899 --poll-for-new-genesis-block --entrypoint testnet.solana.com
```
### Enabling CUDA
By default CUDA is disabled. If your machine has a GPU with CUDA installed,
define the SOLANA_CUDA flag in your environment *before* running any of the
previusly mentioned commands
```bash
$ export SOLANA_CUDA=1
```
When your validator is started look for the following log message to indicate that CUDA is enabled:
`"[<timestamp> solana::validator] CUDA is enabled"`
### Controlling local network port allocation
By default the validator will dynamically select available network ports in the
8000-10000 range, and may be overridden with `--dynamic-port-range`. For
example, `validator.sh --dynamic-port-range 11000-11010 ...` will restrict the
validator to ports 11000-11011.
### Limiting ledger size to conserve disk space
By default the validator will retain the full ledger. To conserve disk space
start the validator with the `--limit-ledger-size`, which will instruct the
validator to only retain the last couple hours of ledger.

View File

@@ -1,72 +0,0 @@
# Choosing a Testnet
As noted in the overview, solana currently maintains several testnets, each featuring a validator that can serve as the entrypoint to the cluster for your validator.
Current testnet entrypoints:
- Stable, testnet.solana.com
- Beta, beta.testnet.solana.com
- Edge, edge.testnet.solana.com
Prior to mainnet, the testnets may be running different versions of solana
software, which may feature breaking changes. Generally, the edge testnet tracks
the tip of master, beta tracks the latest tagged minor release, and stable
tracks the most stable tagged release.
### Get Testnet Version
You can submit a JSON-RPC request to see the specific version of the cluster.
```bash
$ curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' edge.testnet.solana.com:8899
{"jsonrpc":"2.0","result":{"solana-core":"0.18.0-pre1"},"id":1}
```
## Using a Different Testnet
This guide is written in the context of testnet.solana.com, our most stable
cluster. To participate in another testnet, you will need to modify some of the
commands in the following pages.
### Downloading Software
If you are bootstrapping with `solana-install`, you can specify the release tag or named channel to install to match your desired testnet.
```bash
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.1/install/solana-install-init.sh | sh -s - 0.18.1
```
```bash
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.18.1/install/solana-install-init.sh | sh -s - beta
```
Similarly, you can add this argument to the `solana-install` command if you've built the program from source:
```bash
$ solana-install init 0.18.1
```
If you are downloading pre-compiled binaries or building from source, simply choose the release matching your desired testnet.
### Validator Commands
Solana CLI tools like solana and solana-validator-info point at
testnet.solana.com by default. Include a `--url` argument to point at a
different testnet. For instance:
```bash
$ solana --url http://beta.testnet.solana.com:8899 balance
```
The solana cli includes `get` and `set` configuration commands to automatically
set the `--url` argument for future wallet commands.
For example:
```bash
$ solana set --url http://beta.testnet.solana.com:8899
$ solana balance # Same result as command above
```
(You can always override the set configuration by explicitly passing the `--url`
argument with a command.)
Solana-gossip and solana-validator commands already require an explicit
`--entrypoint` argument. Simply replace testnet.solana.com in the examples with
an alternate url to interact with a different testnet. For example:
```bash
$ validator.sh --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 --poll-for-new-genesis-block beta.testnet.solana.com
```
You can also submit JSON-RPC requests to a different testnet, like:
```bash
$ curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://beta.testnet.solana.com:8899
```

View File

@@ -1,2 +0,0 @@
# Troubleshooting Validator Issues
Coming soon...

View File

@@ -1,6 +1,6 @@
## solana CLI
## solana-wallet CLI
The [solana-cli crate](https://crates.io/crates/solana-cli) provides a command-line interface tool for Solana
The [solana crate](https://crates.io/crates/solana) is distributed with a command-line interface tool
### Examples
@@ -8,7 +8,7 @@ The [solana-cli crate](https://crates.io/crates/solana-cli) provides a command-l
```sh
// Command
$ solana address
$ solana-wallet address
// Return
<PUBKEY>
@@ -18,7 +18,7 @@ $ solana address
```sh
// Command
$ solana airdrop 123
$ solana-wallet airdrop 123
// Return
"Your balance is: 123"
@@ -28,7 +28,7 @@ $ solana airdrop 123
```sh
// Command
$ solana balance
$ solana-wallet balance
// Return
"Your balance is: 123"
@@ -38,7 +38,7 @@ $ solana balance
```sh
// Command
$ solana confirm <TX_SIGNATURE>
$ solana-wallet confirm <TX_SIGNATURE>
// Return
"Confirmed" / "Not found" / "Transaction failed with error <ERR>"
@@ -48,7 +48,7 @@ $ solana confirm <TX_SIGNATURE>
```sh
// Command
$ solana deploy <PATH>
$ solana-wallet deploy <PATH>
// Return
<PROGRAM_ID>
@@ -58,7 +58,7 @@ $ solana deploy <PATH>
```sh
// Command
$ solana pay <PUBKEY> 123
$ solana-wallet pay <PUBKEY> 123
// Return
<TX_SIGNATURE>
@@ -68,7 +68,7 @@ $ solana pay <PUBKEY> 123
```sh
// Command
$ solana pay <PUBKEY> 123 \
$ solana-wallet pay <PUBKEY> 123 \
--after 2018-12-24T23:59:00 --require-timestamp-from <PUBKEY>
// Return
@@ -81,7 +81,7 @@ $ solana pay <PUBKEY> 123 \
A third party must send a signature to unlock the lamports.
```sh
// Command
$ solana pay <PUBKEY> 123 \
$ solana-wallet pay <PUBKEY> 123 \
--require-signature-from <PUBKEY>
// Return
@@ -92,7 +92,7 @@ $ solana pay <PUBKEY> 123 \
```sh
// Command
$ solana pay <PUBKEY> 123 \
$ solana-wallet pay <PUBKEY> 123 \
--after 2018-12-24T23:59 --require-timestamp-from <PUBKEY> \
--require-signature-from <PUBKEY>
@@ -104,7 +104,7 @@ $ solana pay <PUBKEY> 123 \
```sh
// Command
$ solana pay <PUBKEY> 123 \
$ solana-wallet pay <PUBKEY> 123 \
--require-signature-from <PUBKEY> \
--require-signature-from <PUBKEY>
@@ -116,7 +116,7 @@ $ solana pay <PUBKEY> 123 \
```sh
// Command
$ solana pay <PUBKEY> 123 \
$ solana-wallet pay <PUBKEY> 123 \
--require-signature-from <PUBKEY> \
--cancelable
@@ -128,7 +128,7 @@ $ solana pay <PUBKEY> 123 \
```sh
// Command
$ solana cancel <PROCESS_ID>
$ solana-wallet cancel <PROCESS_ID>
// Return
<TX_SIGNATURE>
@@ -138,7 +138,7 @@ $ solana cancel <PROCESS_ID>
```sh
// Command
$ solana send-signature <PUBKEY> <PROCESS_ID>
$ solana-wallet send-signature <PUBKEY> <PROCESS_ID>
// Return
<TX_SIGNATURE>
@@ -149,7 +149,7 @@ $ solana send-signature <PUBKEY> <PROCESS_ID>
Use the current system time:
```sh
// Command
$ solana send-timestamp <PUBKEY> <PROCESS_ID>
$ solana-wallet send-timestamp <PUBKEY> <PROCESS_ID>
// Return
<TX_SIGNATURE>
@@ -159,7 +159,7 @@ Or specify some other arbitrary timestamp:
```sh
// Command
$ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
$ solana-wallet send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
// Return
<TX_SIGNATURE>
@@ -168,10 +168,10 @@ $ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
### Usage
```manpage
solana 0.12.0
solana-wallet 0.12.0
USAGE:
solana [FLAGS] [OPTIONS] [SUBCOMMAND]
solana-wallet [FLAGS] [OPTIONS] [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
@@ -201,11 +201,11 @@ SUBCOMMANDS:
```
```manpage
solana-address
solana-wallet-address
Get your public key
USAGE:
solana address
solana-wallet address
FLAGS:
-h, --help Prints help information
@@ -213,11 +213,11 @@ FLAGS:
```
```manpage
solana-airdrop
solana-wallet-airdrop
Request a batch of lamports
USAGE:
solana airdrop <NUM>
solana-wallet airdrop <NUM>
FLAGS:
-h, --help Prints help information
@@ -228,11 +228,11 @@ ARGS:
```
```manpage
solana-balance
solana-wallet-balance
Get your balance
USAGE:
solana balance
solana-wallet balance
FLAGS:
-h, --help Prints help information
@@ -240,11 +240,11 @@ FLAGS:
```
```manpage
solana-cancel
solana-wallet-cancel
Cancel a transfer
USAGE:
solana cancel <PROCESS_ID>
solana-wallet cancel <PROCESS_ID>
FLAGS:
-h, --help Prints help information
@@ -255,11 +255,11 @@ ARGS:
```
```manpage
solana-confirm
solana-wallet-confirm
Confirm transaction by signature
USAGE:
solana confirm <SIGNATURE>
solana-wallet confirm <SIGNATURE>
FLAGS:
-h, --help Prints help information
@@ -270,11 +270,11 @@ ARGS:
```
```manpage
solana-deploy
solana-wallet-deploy
Deploy a program
USAGE:
solana deploy <PATH>
solana-wallet deploy <PATH>
FLAGS:
-h, --help Prints help information
@@ -285,11 +285,11 @@ ARGS:
```
```manpage
solana-fees
solana-wallet-fees
Display current cluster fees
USAGE:
solana fees
solana-wallet fees
FLAGS:
-h, --help Prints help information
@@ -297,11 +297,11 @@ FLAGS:
```
```manpage
solana-get-transaction-count
solana-wallet-get-transaction-count
Get current transaction count
USAGE:
solana get-transaction-count
solana-wallet get-transaction-count
FLAGS:
-h, --help Prints help information
@@ -309,11 +309,11 @@ FLAGS:
```
```manpage
solana-pay
solana-wallet-pay
Send a payment
USAGE:
solana pay [FLAGS] [OPTIONS] <PUBKEY> <NUM>
solana-wallet pay [FLAGS] [OPTIONS] <PUBKEY> <NUM>
FLAGS:
--cancelable
@@ -331,11 +331,11 @@ ARGS:
```
```manpage
solana-send-signature
solana-wallet-send-signature
Send a signature to authorize a transfer
USAGE:
solana send-signature <PUBKEY> <PROCESS_ID>
solana-wallet send-signature <PUBKEY> <PROCESS_ID>
FLAGS:
-h, --help Prints help information
@@ -347,11 +347,11 @@ ARGS:
```
```manpage
solana-send-timestamp
solana-wallet-send-timestamp
Send a timestamp to unlock a transfer
USAGE:
solana send-timestamp [OPTIONS] <PUBKEY> <PROCESS_ID>
solana-wallet send-timestamp [OPTIONS] <PUBKEY> <PROCESS_ID>
FLAGS:
-h, --help Prints help information

View File

@@ -1,2 +1 @@
/target/
/farf/

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-chacha-sys"
version = "0.18.1"
version = "0.16.5"
description = "Solana chacha-sys"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -9,4 +9,4 @@ license = "Apache-2.0"
edition = "2018"
[build-dependencies]
cc = "1.0.40"
cc = "1.0.37"

View File

@@ -1,15 +0,0 @@
# Build steps that run on a release tag
#
# All the steps in `buildkite.yml` are skipped and we jump directly to the
# secondary build steps since it's assumed the commit that was tagged is known
# to be good so there's no need to rebuild and retest it.
steps:
- trigger: "solana-secondary"
branches: "!pull/*"
async: true
build:
message: "${BUILDKITE_MESSAGE}"
commit: "${BUILDKITE_COMMIT}"
branch: "${BUILDKITE_BRANCH}"
env:
TRIGGERED_BUILDKITE_TAG: "${BUILDKITE_TAG}"

View File

@@ -1,19 +1,16 @@
#
# Build steps that run after the primary pipeline on pushes and tags.
# Pull requests to not run these steps.
steps:
- command: "sdk/docker-solana/build.sh"
timeout_in_minutes: 60
timeout_in_minutes: 20
name: "publish docker"
- command: "ci/publish-crate.sh"
timeout_in_minutes: 120
timeout_in_minutes: 60
name: "publish crate"
branches: "!master"
- command: "ci/publish-bpf-sdk.sh"
timeout_in_minutes: 5
name: "publish bpf sdk"
- command: "ci/publish-tarball.sh"
timeout_in_minutes: 60
timeout_in_minutes: 25
name: "publish tarball"
- command: "ci/publish-book.sh"
timeout_in_minutes: 15

View File

@@ -1,13 +1,10 @@
# Build steps that run on pushes and pull requests.
#
# Release tags use buildkite-release.yml instead
steps:
- command: "ci/shellcheck.sh"
name: "shellcheck"
timeout_in_minutes: 5
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-checks.sh"
name: "checks"
timeout_in_minutes: 35
timeout_in_minutes: 15
- wait
- command: "ci/test-stable-perf.sh"
name: "stable-perf"

View File

@@ -1,16 +1,14 @@
# Note: when the rust version is changed also modify
# ci/rust-version.sh to pick up the new image tag
FROM rust:1.37.0
# Add Google Protocol Buffers for Libra's metrics library.
ENV PROTOC_VERSION 3.8.0
ENV PROTOC_ZIP protoc-$PROTOC_VERSION-linux-x86_64.zip
FROM rust:1.35.0
RUN set -x \
&& apt update \
&& apt-get install apt-transport-https \
&& echo deb https://apt.buildkite.com/buildkite-agent stable main > /etc/apt/sources.list.d/buildkite-agent.list \
&& echo deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-7 main > /etc/apt/sources.list.d/llvm.list \
&& apt-key adv --no-tty --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 32A37959C2FA5C3C99EFBC32A79206696452D198 \
&& wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - \
&& apt update \
&& apt install -y \
buildkite-agent \
@@ -18,11 +16,10 @@ RUN set -x \
cmake \
lcov \
libclang-common-7-dev \
llvm-7 \
mscgen \
rsync \
sudo \
golang \
unzip \
\
&& rm -rf /var/lib/apt/lists/* \
&& rustup component add rustfmt \
@@ -31,8 +28,4 @@ RUN set -x \
&& cargo install svgbob_cli \
&& cargo install mdbook \
&& rustc --version \
&& cargo --version \
&& curl -OL https://github.com/google/protobuf/releases/download/v$PROTOC_VERSION/$PROTOC_ZIP \
&& unzip -o $PROTOC_ZIP -d /usr/local bin/protoc \
&& unzip -o $PROTOC_ZIP -d /usr/local include/* \
&& rm -f $PROTOC_ZIP
&& cargo --version

View File

@@ -59,7 +59,7 @@ if [[ -n $CI ]]; then
fi
if [[ $CI_LINUX = True ]]; then
export CI_OS_NAME=linux
else
elif [[ $CI_WINDOWS = True ]]; then
export CI_OS_NAME=windows
fi
export CI_REPO_SLUG=$APPVEYOR_REPO_NAME

View File

@@ -5,6 +5,7 @@ skipSetup=false
iterations=1
restartInterval=never
rollingRestart=false
maybeNoLeaderRotation=
extraNodes=0
walletRpcPort=:8899
@@ -53,6 +54,9 @@ while getopts "ch?i:k:brxR" opt; do
k)
restartInterval=$OPTARG
;;
b)
maybeNoLeaderRotation="--stake 0"
;;
x)
extraNodes=$((extraNodes + 1))
;;
@@ -74,25 +78,24 @@ source scripts/configure-metrics.sh
nodes=(
"multinode-demo/drone.sh"
"multinode-demo/bootstrap-leader.sh \
--init-complete-file init-complete-node1.log \
--dynamic-port-range 8000-8019"
"multinode-demo/validator.sh \
--enable-rpc-exit \
--no-restart \
--dynamic-port-range 8020-8039
--init-complete-file init-complete-node1.log"
"multinode-demo/validator.sh \
$maybeNoLeaderRotation \
--enable-rpc-exit \
--no-restart \
--init-complete-file init-complete-node2.log \
--rpc-port 18899"
)
for i in $(seq 1 $extraNodes); do
portStart=$((8040 + i * 20))
portEnd=$((portStart + 19))
nodes+=(
"multinode-demo/validator.sh \
--no-restart \
--dynamic-port-range $portStart-$portEnd
--label dyn$i \
--init-complete-file init-complete-node$((2 + i)).log"
--init-complete-file init-complete-node$((2 + i)).log \
$maybeNoLeaderRotation"
)
done
numNodes=$((2 + extraNodes))
@@ -122,26 +125,21 @@ startNode() {
echo "log: $log"
}
waitForNodeToInit() {
declare initCompleteFile=$1
while [[ ! -r $initCompleteFile ]]; do
if [[ $SECONDS -ge 240 ]]; then
echo "^^^ +++"
echo "Error: $initCompleteFile not found in $SECONDS seconds"
exit 1
fi
echo "Waiting for $initCompleteFile ($SECONDS)..."
sleep 2
done
echo "Found $initCompleteFile"
}
initCompleteFiles=()
waitForAllNodesToInit() {
echo "--- ${#initCompleteFiles[@]} nodes booting"
SECONDS=
for initCompleteFile in "${initCompleteFiles[@]}"; do
waitForNodeToInit "$initCompleteFile"
while [[ ! -r $initCompleteFile ]]; do
if [[ $SECONDS -ge 240 ]]; then
echo "^^^ +++"
echo "Error: $initCompleteFile not found in $SECONDS seconds"
exit 1
fi
echo "Waiting for $initCompleteFile ($SECONDS)..."
sleep 2
done
echo "Found $initCompleteFile"
done
echo "All nodes finished booting in $SECONDS seconds"
}
@@ -164,13 +162,6 @@ startNodes() {
if $addLogs; then
logs+=("$(getNodeLogFile "$i" "$cmd")")
fi
# 1 == bootstrap leader, wait until it boots before starting
# other validators
if [[ "$i" -eq 1 ]]; then
SECONDS=
waitForNodeToInit "$initCompleteFile"
fi
done
waitForAllNodesToInit
@@ -270,7 +261,7 @@ verifyLedger() {
(
source multinode-demo/common.sh
set -x
$solana_ledger_tool --ledger "$SOLANA_CONFIG_DIR"/$ledger verify
$solana_ledger_tool --ledger "$SOLANA_CONFIG_DIR"/$ledger-ledger verify
) || flag_error
done
}
@@ -309,6 +300,7 @@ else
fi
startNodes
lastTransactionCount=
enforceTransactionCountAdvance=true
while [[ $iteration -le $iterations ]]; do
echo "--- Node count ($iteration)"
(
@@ -344,20 +336,36 @@ while [[ $iteration -le $iterations ]]; do
transactionCount=$(sed -e 's/{"jsonrpc":"2.0","result":\([0-9]*\),"id":1}/\1/' log-transactionCount.txt)
if [[ -n $lastTransactionCount ]]; then
echo "--- Transaction count check: $lastTransactionCount < $transactionCount"
if [[ $lastTransactionCount -ge $transactionCount ]]; then
echo "Error: Transaction count is not advancing"
echo "* lastTransactionCount: $lastTransactionCount"
echo "* transactionCount: $transactionCount"
flag_error
if $enforceTransactionCountAdvance; then
if [[ $lastTransactionCount -ge $transactionCount ]]; then
echo "Error: Transaction count is not advancing"
echo "* lastTransactionCount: $lastTransactionCount"
echo "* transactionCount: $transactionCount"
flag_error
fi
else
echo "enforceTransactionCountAdvance=false"
fi
enforceTransactionCountAdvance=true
fi
lastTransactionCount=$transactionCount
echo "--- Wallet sanity ($iteration)"
flag_error_if_no_leader_rotation() {
# TODO: Stop ignoring wallet sanity failures when leader rotation is enabled
# once https://github.com/solana-labs/solana/issues/2474 is fixed
if [[ -n $maybeNoLeaderRotation ]]; then
flag_error
else
# Wallet error occurred (and was ignored) so transactionCount may not
# advance on the next iteration
enforceTransactionCountAdvance=false
fi
}
(
set -x
timeout 60s scripts/wallet-sanity.sh --url http://127.0.0.1"$walletRpcPort"
) || flag_error
) || flag_error_if_no_leader_rotation
iteration=$((iteration + 1))

View File

@@ -46,22 +46,16 @@ if _ git --no-pager grep -n 'Default::default()' -- '*.rs'; then
fi
# Let's keep a .gitignore for every crate, ensure it's got
# /target/ and /farf/ in it
# /target/ in it
declare gitignores_ok=true
for i in $(git --no-pager ls-files \*/Cargo.toml ); do
dir=$(dirname "$i")
if [[ ! -f $dir/.gitignore ]]; then
echo 'error: nits.sh .gitnore missing for crate '"$dir" >&2
gitignores_ok=false
else
if ! grep -q -e '^/target/$' "$dir"/.gitignore; then
elif ! grep -q -e '^/target/$' "$dir"/.gitignore; then
echo 'error: nits.sh "/target/" apparently missing from '"$dir"'/.gitignore' >&2
gitignores_ok=false
fi
if ! grep -q -e '^/farf/$' "$dir"/.gitignore ; then
echo 'error: nits.sh "/farf/" apparently missing from '"$dir"'/.gitignore' >&2
gitignores_ok=false
fi
fi
done
"$gitignores_ok"

View File

@@ -2,50 +2,8 @@
set -e
cd "$(dirname "$0")/.."
BOOK="book"
source ci/rust-version.sh stable
eval "$(ci/channel-info.sh)"
if [[ -n $PUBLISH_BOOK_TAG ]]; then
CURRENT_TAG="$(git describe --tags)"
COMMIT_TO_PUBLISH="$(git rev-list -n 1 "${PUBLISH_BOOK_TAG}")"
# book is manually published at a specified release tag
if [[ $PUBLISH_BOOK_TAG != "$CURRENT_TAG" ]]; then
(
cat <<EOF
steps:
- trigger: "$BUILDKITE_PIPELINE_SLUG"
async: true
build:
message: "$BUILDKITE_MESSAGE"
commit: "$COMMIT_TO_PUBLISH"
env:
PUBLISH_BOOK_TAG: "$PUBLISH_BOOK_TAG"
EOF
) | buildkite-agent pipeline upload
exit 0
fi
repo=git@github.com:solana-labs/book.git
else
# book-edge and book-beta are published automatically on the tip of the branch
case $CHANNEL in
edge)
repo=git@github.com:solana-labs/book-edge.git
;;
beta)
repo=git@github.com:solana-labs/book-beta.git
;;
*)
echo "--- publish skipped"
exit 0
;;
esac
BOOK=$CHANNEL
fi
ci/docker-run.sh "$rust_stable_docker_image" bash -exc "book/build.sh"
book/build.sh
echo --- create book repo
(
@@ -58,7 +16,22 @@ echo --- create book repo
git commit -m "${CI_COMMIT:-local}"
)
echo "--- publish $BOOK"
eval "$(ci/channel-info.sh)"
# Only publish the book from the edge and beta channels for now.
case $CHANNEL in
edge)
repo=git@github.com:solana-labs/book-edge.git
;;
beta)
repo=git@github.com:solana-labs/book.git
;;
*)
echo "--- publish skipped"
exit 0
;;
esac
echo "--- publish $CHANNEL"
cd book/html/
git remote add origin $repo
git fetch origin master

View File

@@ -2,7 +2,6 @@
set -e
cd "$(dirname "$0")/.."
source ci/semver_bash/semver.sh
source ci/rust-version.sh stable
# shellcheck disable=SC2086
is_crate_version_uploaded() {
@@ -26,6 +25,8 @@ expectedCrateVersion="$MAJOR.$MINOR.$PATCH$SPECIAL"
exit 1
}
cargoCommand="cargo publish --token $CRATES_IO_TOKEN"
Cargo_tomls=$(ci/order-crates-for-publishing.py)
for Cargo_toml in $Cargo_tomls; do
@@ -52,7 +53,7 @@ for Cargo_toml in $Cargo_tomls; do
crate=$(dirname "$Cargo_toml")
# TODO: the rocksdb package does not build with the stock rust docker image,
# so use the solana rust docker image until this is resolved upstream
cargoCommand="cargo publish --token $CRATES_IO_TOKEN"
source ci/rust-version.sh
ci/docker-run.sh "$rust_stable_docker_image" bash -exc "cd $crate; $cargoCommand"
) || true # <-- Don't fail. We want to be able to retry the job in cases when a publish fails halfway due to network/cloud issues
@@ -66,11 +67,11 @@ for Cargo_toml in $Cargo_tomls; do
(
set -x
rm -rf crate-test
cargo +"$rust_stable" init crate-test
cargo init crate-test
cd crate-test/
echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml
echo "[workspace]" >> Cargo.toml
cargo +"$rust_stable" check
cargo check
) && really_uploaded=1
if ((really_uploaded)); then
break;

View File

@@ -9,9 +9,8 @@ if [[ -n $APPVEYOR ]]; then
source ci/rust-version.sh
appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
export USERPROFILE="D:\\"
./rustup-init -yv --default-toolchain $rust_stable --default-host x86_64-pc-windows-msvc
export PATH="$PATH:/d/.cargo/bin"
export PATH="$PATH:$USERPROFILE/.cargo/bin"
rustc -vV
cargo -vV
fi
@@ -50,8 +49,7 @@ windows)
TARGET=x86_64-pc-windows-msvc
;;
*)
echo CI_OS_NAME unset
exit 1
TARGET=unknown-unknown-unknown
;;
esac
@@ -72,12 +70,6 @@ echo --- Creating tarball
source ci/rust-version.sh stable
scripts/cargo-install-all.sh +"$rust_stable" solana-release
# Reduce the archive size until
# https://github.com/appveyor/ci/issues/2997 is fixed
if [[ -n $APPVEYOR ]]; then
rm -f solana-release/bin/solana-validator.exe solana-release/bin/solana-bench-exchange.exe
fi
if $PERF_LIBS; then
rm -rf target/perf-libs
./fetch-perf-libs.sh
@@ -102,14 +94,22 @@ echo --- Creating tarball
set -e
cd "$(dirname "$0")"/..
export USE_INSTALL=1
export REQUIRE_LEDGER_DIR=1
export REQUIRE_KEYPAIRS=1
exec multinode-demo/validator.sh "$@"
EOF
chmod +x solana-release/bin/validator.sh
tar cvf solana-release-$TARGET.tar solana-release
bzip2 solana-release-$TARGET.tar
# Add a wrapper script for clear-config.sh
# TODO: Remove multinode/... from tarball
cat > solana-release/bin/clear-config.sh <<'EOF'
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")"/..
export USE_INSTALL=1
exec multinode-demo/clear-config.sh "$@"
EOF
chmod +x solana-release/bin/clear-config.sh
tar jvcf solana-release-$TARGET.tar.bz2 solana-release/
cp solana-release/bin/solana-install-init solana-install-init-$TARGET
)

View File

@@ -13,8 +13,8 @@
# $ source ci/rust-version.sh
#
stable_version=1.37.0
nightly_version=2019-08-21
stable_version=1.35.0
nightly_version=2019-06-20
export rust_stable="$stable_version"
export rust_stable_docker_image=solanalabs/rust:"$stable_version"

View File

@@ -10,30 +10,32 @@ source ci/rust-version.sh nightly
export RUST_BACKTRACE=1
export RUSTFLAGS="-D warnings"
do_bpf_check() {
_ cargo +"$rust_stable" fmt --all -- --check
_ cargo +"$rust_nightly" clippy --all -- --version
_ cargo +"$rust_nightly" clippy --all -- --deny=warnings
_ cargo +"$rust_stable" audit
}
(
(
cd sdk/bpf/rust/rust-utils
do_bpf_check
)
for project in programs/bpf/rust/*/ ; do
(
cd "$project"
do_bpf_check
)
done
)
_ cargo +"$rust_stable" fmt --all -- --check
# Clippy gets stuck for unknown reasons if sdk-c is included in the build, so check it separately.
# See https://github.com/solana-labs/solana/issues/5503
_ cargo +"$rust_stable" clippy --version
_ cargo +"$rust_stable" clippy --all --exclude solana-sdk-c -- --deny=warnings
_ cargo +"$rust_stable" clippy --manifest-path sdk-c/Cargo.toml -- --deny=warnings
# _ cargo +"$rust_stable" audit --version ### cargo-audit stopped supporting --version?? https://github.com/RustSec/cargo-audit/issues/100
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2019-0013
_ cargo +"$rust_stable" clippy --all -- --version
_ cargo +"$rust_stable" clippy --all -- --deny=warnings
_ cargo +"$rust_stable" audit
_ ci/nits.sh
_ ci/order-crates-for-publishing.py
_ book/build.sh
for project in sdk/bpf/rust/{rust-utils,rust-test} programs/bpf/rust/*/ ; do
echo "+++ do_bpf_check $project"
(
cd "$project"
_ cargo +"$rust_stable" fmt --all -- --check
_ cargo +"$rust_nightly" test --all
_ cargo +"$rust_nightly" clippy --version
_ cargo +"$rust_nightly" clippy --all -- --deny=warnings
_ cargo +"$rust_stable" audit
)
done
echo --- ok

View File

@@ -33,7 +33,7 @@ test-stable)
echo "Executing $testName"
_ cargo +"$rust_stable" build --all ${V:+--verbose}
_ cargo +"$rust_stable" test --all ${V:+--verbose} -- --nocapture
_ cargo +"$rust_stable" test --all ${V:+--verbose} -- --nocapture --test-threads=1
;;
test-stable-perf)
echo "Executing $testName"
@@ -77,7 +77,7 @@ test-stable-perf)
# Run root package library tests
_ cargo +"$rust_stable" build --all ${V:+--verbose} --features="$ROOT_FEATURES"
_ cargo +"$rust_stable" test --manifest-path=core/Cargo.toml ${V:+--verbose} --features="$ROOT_FEATURES" -- --nocapture
_ cargo +"$rust_stable" test --manifest-path=core/Cargo.toml ${V:+--verbose} --features="$ROOT_FEATURES" -- --nocapture --test-threads=1
;;
*)
echo "Error: Unknown test: $testName"

View File

@@ -24,9 +24,7 @@ blockstreamer=false
deployUpdateManifest=true
fetchLogs=true
maybeHashesPerTick=
maybeDisableAirdrops=
maybeInternalNodesStakeLamports=
maybeInternalNodesLamports=
maybeStakeNodesInGenesisBlock=
maybeExternalPrimordialAccountsFile=
maybeLamports=
maybeLetsEncrypt=
@@ -70,12 +68,8 @@ Deploys a CD testnet
-s - Skip start. Nodes will still be created or configured, but network software will not be started.
-S - Stop network software without tearing down nodes.
-f - Discard validator nodes that didn't bootup successfully
--no-airdrop
- If set, disables airdrops. Nodes must be funded in genesis block when airdrops are disabled.
--internal-nodes-stake-lamports NUM_LAMPORTS
- Amount to stake internal nodes.
--internal-nodes-lamports NUM_LAMPORTS
- Amount to fund internal nodes in genesis block
--stake-internal-nodes NUM_LAMPORTS
- Amount to stake internal nodes. If set, airdrops are disabled.
--external-accounts-file FILE_PATH
- Path to external Primordial Accounts file, if it exists.
--hashes-per-tick NUM_HASHES|sleep|auto
@@ -90,7 +84,7 @@ Deploys a CD testnet
- Attempt to generate a TLS certificate using this DNS name
--fullnode-additional-disk-size-gb [number]
- Size of additional disk in GB for all fullnodes
--no-snapshot-fetch
--no-snapshot
- If set, disables booting validators from a snapshot
Note: the SOLANA_METRICS_CONFIG environment variable is used to configure
@@ -110,14 +104,8 @@ while [[ -n $1 ]]; do
elif [[ $1 = --lamports ]]; then
maybeLamports="$1 $2"
shift 2
elif [[ $1 = --no-airdrop ]]; then
maybeDisableAirdrops="$1"
shift 1
elif [[ $1 = --internal-nodes-stake-lamports ]]; then
maybeInternalNodesStakeLamports="$1 $2"
shift 2
elif [[ $1 = --internal-nodes-lamports ]]; then
maybeInternalNodesLamports="$1 $2"
elif [[ $1 = --stake-internal-nodes ]]; then
maybeStakeNodesInGenesisBlock="$1 $2"
shift 2
elif [[ $1 = --external-accounts-file ]]; then
maybeExternalPrimordialAccountsFile="$1 $2"
@@ -137,7 +125,7 @@ while [[ -n $1 ]]; do
elif [[ $1 == --machine-type* ]]; then # Bypass quoted long args for GPUs
shortArgs+=("$1")
shift
elif [[ $1 = --no-snapshot-fetch ]]; then
elif [[ $1 = --no-snapshot ]]; then
maybeNoSnapshot="$1"
shift 1
else
@@ -401,17 +389,9 @@ if ! $skipStart; then
args+=(--deploy-update windows)
fi
if [[ -n $maybeDisableAirdrops ]]; then
# shellcheck disable=SC2206
args+=($maybeDisableAirdrops)
fi
if [[ -n $maybeInternalNodesStakeLamports ]]; then
# shellcheck disable=SC2206 # Do not want to quote $maybeInternalNodesStakeLamports
args+=($maybeInternalNodesStakeLamports)
fi
if [[ -n $maybeInternalNodesLamports ]]; then
# shellcheck disable=SC2206 # Do not want to quote $maybeInternalNodesLamports
args+=($maybeInternalNodesLamports)
if [[ -n $maybeStakeNodesInGenesisBlock ]]; then
# shellcheck disable=SC2206 # Do not want to quote $maybeStakeNodesInGenesisBlock
args+=($maybeStakeNodesInGenesisBlock)
fi
if [[ -n $maybeExternalPrimordialAccountsFile ]]; then
# shellcheck disable=SC2206 # Do not want to quote $maybeExternalPrimordialAccountsFile

View File

@@ -142,6 +142,8 @@ testnet-beta|testnet-beta-perf)
testnet)
CHANNEL_OR_TAG=$STABLE_CHANNEL_LATEST_TAG
CHANNEL_BRANCH=$STABLE_CHANNEL
: "${EC2_NODE_COUNT:=10}"
: "${GCE_NODE_COUNT:=}"
;;
testnet-perf)
CHANNEL_OR_TAG=$STABLE_CHANNEL_LATEST_TAG
@@ -154,9 +156,8 @@ testnet-demo)
: "${GCE_LOW_QUOTA_NODE_COUNT:=70}"
;;
tds)
: "${TDS_CHANNEL_OR_TAG:=edge}"
CHANNEL_OR_TAG="$TDS_CHANNEL_OR_TAG"
CHANNEL_BRANCH="$CI_BRANCH"
CHANNEL_OR_TAG=beta
CHANNEL_BRANCH=$BETA_CHANNEL
;;
*)
echo "Error: Invalid TESTNET=$TESTNET"
@@ -177,11 +178,11 @@ for val in "${GCE_LOW_QUOTA_ZONES[@]}"; do
GCE_LOW_QUOTA_ZONE_ARGS+=("-z $val")
done
if [[ -z $TESTNET_DB_HOST ]]; then
TESTNET_DB_HOST="https://metrics.solana.com:8086"
if [[ -n $TESTNET_DB_HOST ]]; then
SOLANA_METRICS_PARTIAL_CONFIG="host=$TESTNET_DB_HOST,$SOLANA_METRICS_PARTIAL_CONFIG"
fi
export SOLANA_METRICS_CONFIG="db=$TESTNET,host=$TESTNET_DB_HOST,$SOLANA_METRICS_PARTIAL_CONFIG"
export SOLANA_METRICS_CONFIG="db=$TESTNET,$SOLANA_METRICS_PARTIAL_CONFIG"
echo "SOLANA_METRICS_CONFIG: $SOLANA_METRICS_CONFIG"
source scripts/configure-metrics.sh
@@ -202,6 +203,7 @@ steps:
TESTNET: "$TESTNET"
TESTNET_OP: "$TESTNET_OP"
TESTNET_DB_HOST: "$TESTNET_DB_HOST"
EC2_NODE_COUNT: "$EC2_NODE_COUNT"
GCE_NODE_COUNT: "$GCE_NODE_COUNT"
GCE_LOW_QUOTA_NODE_COUNT: "$GCE_LOW_QUOTA_NODE_COUNT"
EOF
@@ -218,8 +220,7 @@ sanity() {
set -x
NO_INSTALL_CHECK=1 \
NO_LEDGER_VERIFY=1 \
NO_VALIDATOR_SANITY=1 \
ci/testnet-sanity.sh edge-testnet-solana-com gce us-west1-b
ci/testnet-sanity.sh edge-testnet-solana-com ec2 us-west-1a
)
;;
testnet-edge-perf)
@@ -236,8 +237,7 @@ sanity() {
set -x
NO_INSTALL_CHECK=1 \
NO_LEDGER_VERIFY=1 \
NO_VALIDATOR_SANITY=1 \
ci/testnet-sanity.sh beta-testnet-solana-com gce us-west1-b
ci/testnet-sanity.sh beta-testnet-solana-com ec2 us-west-1a
)
;;
testnet-beta-perf)
@@ -252,9 +252,19 @@ sanity() {
testnet)
(
set -x
NO_LEDGER_VERIFY=1 \
NO_VALIDATOR_SANITY=1 \
ci/testnet-sanity.sh testnet-solana-com gce us-west1-b
ok=true
if [[ -n $EC2_NODE_COUNT ]]; then
NO_LEDGER_VERIFY=1 \
ci/testnet-sanity.sh testnet-solana-com ec2 "${EC2_ZONES[0]}" || ok=false
elif [[ -n $GCE_NODE_COUNT ]]; then
NO_LEDGER_VERIFY=1 \
ci/testnet-sanity.sh testnet-solana-com gce "${GCE_ZONES[0]}" || ok=false
else
echo "Error: no EC2 or GCE nodes"
ok=false
fi
$ok
)
;;
testnet-perf)
@@ -324,9 +334,9 @@ deploy() {
testnet-edge)
(
set -x
ci/testnet-deploy.sh -p edge-testnet-solana-com -C gce -z us-west1-b \
-t "$CHANNEL_OR_TAG" -n 2 -c 0 -u -P \
-a edge-testnet-solana-com --letsencrypt edge.testnet.solana.com \
ci/testnet-deploy.sh -p edge-testnet-solana-com -C ec2 -z us-west-1a \
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
-a eipalloc-0ccd4f2239886fa94 --letsencrypt edge.testnet.solana.com \
${skipCreate:+-e} \
${skipStart:+-s} \
${maybeStop:+-S} \
@@ -351,9 +361,9 @@ deploy() {
(
set -x
NO_VALIDATOR_SANITY=1 \
ci/testnet-deploy.sh -p beta-testnet-solana-com -C gce -z us-west1-b \
-t "$CHANNEL_OR_TAG" -n 2 -c 0 -u -P \
-a beta-testnet-solana-com --letsencrypt beta.testnet.solana.com \
ci/testnet-deploy.sh -p beta-testnet-solana-com -C ec2 -z us-west-1a \
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
-a eipalloc-0f286cf8a0771ce35 --letsencrypt beta.testnet.solana.com \
${skipCreate:+-e} \
${skipStart:+-s} \
${maybeStop:+-S} \
@@ -377,14 +387,30 @@ deploy() {
testnet)
(
set -x
NO_VALIDATOR_SANITY=1 \
ci/testnet-deploy.sh -p testnet-solana-com -C gce -z us-west1-b \
-t "$CHANNEL_OR_TAG" -n 2 -c 0 -u -P \
-a testnet-solana-com --letsencrypt testnet.solana.com \
if [[ -n $GCE_NODE_COUNT ]] || [[ -n $skipStart ]]; then
maybeSkipStart="skip"
fi
# shellcheck disable=SC2068
ci/testnet-deploy.sh -p testnet-solana-com -C ec2 ${EC2_ZONE_ARGS[@]} \
-t "$CHANNEL_OR_TAG" -n "$EC2_NODE_COUNT" -c 0 -u -P -f \
-a eipalloc-0fa502bf95f6f18b2 --letsencrypt testnet.solana.com \
${skipCreate:+-e} \
${maybeSkipStart:+-s} \
${maybeStop:+-S} \
${maybeDelete:+-D}
if [[ -n $GCE_NODE_COUNT ]]; then
# shellcheck disable=SC2068
ci/testnet-deploy.sh -p testnet-solana-com -C gce ${GCE_ZONE_ARGS[@]} \
-t "$CHANNEL_OR_TAG" -n "$GCE_NODE_COUNT" -c 0 -P -f \
${skipCreate:+-e} \
${skipStart:+-s} \
${maybeStop:+-S} \
${maybeDelete:+-D}
${maybeDelete:+-D} \
-x
fi
)
;;
testnet-perf)
@@ -474,40 +500,23 @@ deploy() {
maybeHashesPerTick="--hashes-per-tick ${HASHES_PER_TICK}"
fi
if [[ -z $DISABLE_AIRDROPS ]]; then
DISABLE_AIRDROPS="true"
fi
if [[ $DISABLE_AIRDROPS == true ]] ; then
maybeDisableAirdrops="--no-airdrop"
if [[ -z $STAKE_INTERNAL_NODES ]]; then
maybeStakeInternalNodes="--stake-internal-nodes 1000000000000"
elif [[ $STAKE_INTERNAL_NODES == skip ]]; then
maybeStakeInternalNodes=""
else
maybeDisableAirdrops=""
fi
if [[ -z $INTERNAL_NODES_STAKE_LAMPORTS ]]; then
maybeInternalNodesStakeLamports="--internal-nodes-stake-lamports 1000000000000"
elif [[ $INTERNAL_NODES_STAKE_LAMPORTS == skip ]]; then
maybeInternalNodesStakeLamports=""
else
maybeInternalNodesStakeLamports="--internal-nodes-stake-lamports ${INTERNAL_NODES_STAKE_LAMPORTS}"
fi
if [[ -z $INTERNAL_NODES_LAMPORTS ]]; then
maybeInternalNodesLamports="--internal-nodes-lamports 2000000000000"
elif [[ $INTERNAL_NODES_LAMPORTS == skip ]]; then
maybeInternalNodesLamports=""
else
maybeInternalNodesLamports="--internal-nodes-lamports ${INTERNAL_NODES_LAMPORTS}"
maybeStakeInternalNodes="--stake-internal-nodes ${STAKE_INTERNAL_NODES}"
fi
EXTERNAL_ACCOUNTS_FILE=/tmp/validator.yml
if [[ -z $EXTERNAL_ACCOUNTS_FILE_URL ]]; then
EXTERNAL_ACCOUNTS_FILE_URL=https://raw.githubusercontent.com/solana-labs/tour-de-sol/master/validators/all.yml
EXTERNAL_ACCOUNTS_FILE_URL=https://raw.githubusercontent.com/solana-labs/tour-de-sol/master/stage1/validator.yml
wget ${EXTERNAL_ACCOUNTS_FILE_URL} -O ${EXTERNAL_ACCOUNTS_FILE}
maybeExternalAccountsFile="--external-accounts-file ${EXTERNAL_ACCOUNTS_FILE}"
elif [[ $EXTERNAL_ACCOUNTS_FILE_URL == skip ]]; then
maybeExternalAccountsFile=""
else
EXTERNAL_ACCOUNTS_FILE_URL=https://raw.githubusercontent.com/solana-labs/tour-de-sol/master/stage1/validator.yml
wget ${EXTERNAL_ACCOUNTS_FILE_URL} -O ${EXTERNAL_ACCOUNTS_FILE}
maybeExternalAccountsFile="--external-accounts-file ${EXTERNAL_ACCOUNTS_FILE}"
fi
@@ -548,13 +557,12 @@ deploy() {
${skipStart:+-s} \
${maybeStop:+-S} \
${maybeDelete:+-D} \
${maybeDisableAirdrops} \
${maybeInternalNodesStakeLamports} \
${maybeInternalNodesLamports} \
${maybeStakeInternalNodes} \
${maybeExternalAccountsFile} \
${maybeLamports} \
${maybeAdditionalDisk} \
--skip-deploy-update
--skip-deploy-update \
--no-snapshot
)
;;
*)

View File

@@ -1,50 +0,0 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-cli"
description = "Blockchain, Rebuilt for Scale"
version = "0.18.1"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
bincode = "1.1.4"
bs58 = "0.2.4"
chrono = { version = "0.4.7", features = ["serde"] }
clap = "2.33.0"
criterion-stats = "0.3.0"
ctrlc = { version = "3.1.3", features = ["termination"] }
console = "0.7.7"
dirs = "2.0.2"
lazy_static = "1.3.0"
log = "0.4.8"
num-traits = "0.2"
pretty-hex = "0.1.0"
serde = "1.0.99"
serde_derive = "1.0.99"
serde_json = "1.0.40"
serde_yaml = "0.8.9"
solana-budget-api = { path = "../programs/budget_api", version = "0.18.1" }
solana-client = { path = "../client", version = "0.18.1" }
solana-drone = { path = "../drone", version = "0.18.1" }
solana-logger = { path = "../logger", version = "0.18.1" }
solana-netutil = { path = "../utils/netutil", version = "0.18.1" }
solana-runtime = { path = "../runtime", version = "0.18.1" }
solana-sdk = { path = "../sdk", version = "0.18.1" }
solana-stake-api = { path = "../programs/stake_api", version = "0.18.1" }
solana-storage-api = { path = "../programs/storage_api", version = "0.18.1" }
solana-vote-api = { path = "../programs/vote_api", version = "0.18.1" }
solana-vote-signer = { path = "../vote-signer", version = "0.18.1" }
url = "2.1.0"
[dev-dependencies]
solana-core = { path = "../core", version = "0.18.1" }
solana-budget-program = { path = "../programs/budget_program", version = "0.18.1" }
[features]
cuda = []
[[bin]]
name = "solana"
path = "src/main.rs"

View File

@@ -1,49 +0,0 @@
// Wallet settings that can be configured for long-term use
use serde_derive::{Deserialize, Serialize};
use std::fs::{create_dir_all, File};
use std::io::{self, Write};
use std::path::Path;
lazy_static! {
pub static ref CONFIG_FILE: Option<String> = {
dirs::home_dir().map(|mut path| {
path.extend(&[".config", "solana", "wallet", "config.yml"]);
path.to_str().unwrap().to_string()
})
};
}
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct Config {
pub url: String,
pub keypair: String,
}
impl Config {
pub fn new(url: &str, keypair: &str) -> Self {
Self {
url: url.to_string(),
keypair: keypair.to_string(),
}
}
pub fn load(config_file: &str) -> Result<Self, io::Error> {
let file = File::open(config_file.to_string())?;
let config = serde_yaml::from_reader(file)
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
Ok(config)
}
pub fn save(&self, config_file: &str) -> Result<(), io::Error> {
let serialized = serde_yaml::to_string(self)
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
if let Some(outdir) = Path::new(&config_file).parent() {
create_dir_all(outdir)?;
}
let mut file = File::create(config_file)?;
file.write_all(&serialized.into_bytes())?;
Ok(())
}
}

View File

@@ -1,11 +0,0 @@
use console::style;
// Pretty print a "name value"
pub fn println_name_value(name: &str, value: &str) {
let styled_value = if value == "" {
style("(not set)").italic()
} else {
style(value)
};
println!("{} {}", style(name).bold(), styled_value);
}

View File

@@ -1,6 +0,0 @@
#[macro_use]
extern crate lazy_static;
pub mod config;
pub mod display;
pub mod wallet;

View File

@@ -1,237 +0,0 @@
use clap::{crate_description, crate_name, crate_version, Arg, ArgGroup, ArgMatches, SubCommand};
use console::style;
use solana_cli::config::{self, Config};
use solana_cli::display::println_name_value;
use solana_cli::wallet::{app, parse_command, process_command, WalletConfig, WalletError};
use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil};
use std::error;
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
let parse_args = match matches.subcommand() {
("get", Some(subcommand_matches)) => {
if let Some(config_file) = matches.value_of("config_file") {
let config = Config::load(config_file).unwrap_or_default();
if let Some(field) = subcommand_matches.value_of("specific_setting") {
let value = match field {
"url" => config.url,
"keypair" => config.keypair,
_ => unreachable!(),
};
println_name_value(&format!("* {}:", field), &value);
} else {
println_name_value("Wallet Config:", config_file);
println_name_value("* url:", &config.url);
println_name_value("* keypair:", &config.keypair);
}
} else {
println!("{} Either provide the `--config` arg or ensure home directory exists to use the default config location", style("No config file found.").bold());
}
false
}
("set", Some(subcommand_matches)) => {
if let Some(config_file) = matches.value_of("config_file") {
let mut config = Config::load(config_file).unwrap_or_default();
if let Some(url) = subcommand_matches.value_of("url") {
config.url = url.to_string();
}
if let Some(keypair) = subcommand_matches.value_of("keypair") {
config.keypair = keypair.to_string();
}
config.save(config_file)?;
println_name_value("Wallet Config Updated:", config_file);
println_name_value("* url:", &config.url);
println_name_value("* keypair:", &config.keypair);
} else {
println!("{} Either provide the `--config` arg or ensure home directory exists to use the default config location", style("No config file found.").bold());
}
false
}
_ => true,
};
Ok(parse_args)
}
pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn error::Error>> {
let config = if let Some(config_file) = matches.value_of("config_file") {
Config::load(config_file).unwrap_or_default()
} else {
Config::default()
};
let json_rpc_url = if let Some(url) = matches.value_of("json_rpc_url") {
url.to_string()
} else if config.url != "" {
config.url
} else {
let default = WalletConfig::default();
default.json_rpc_url
};
let drone_host = if let Some(drone_host) = matches.value_of("drone_host") {
Some(solana_netutil::parse_host(drone_host).or_else(|err| {
Err(WalletError::BadParameter(format!(
"Invalid drone host: {:?}",
err
)))
})?)
} else {
None
};
let drone_port = matches
.value_of("drone_port")
.unwrap()
.parse()
.or_else(|err| {
Err(WalletError::BadParameter(format!(
"Invalid drone port: {:?}",
err
)))
})?;
let mut path = dirs::home_dir().expect("home directory");
let id_path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap()
} else if config.keypair != "" {
&config.keypair
} else {
path.extend(&[".config", "solana", "id.json"]);
if !path.exists() {
gen_keypair_file(path.to_str().unwrap())?;
println!("New keypair generated at: {}", path.to_str().unwrap());
}
path.to_str().unwrap()
};
let keypair = read_keypair(id_path).or_else(|err| {
Err(WalletError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, id_path
)))
})?;
let command = parse_command(&keypair.pubkey(), &matches)?;
Ok(WalletConfig {
command,
drone_host,
drone_port,
json_rpc_url,
keypair,
rpc_client: None,
})
}
// Return an error if a url cannot be parsed.
fn is_url(string: String) -> Result<(), String> {
match url::Url::parse(&string) {
Ok(url) => {
if url.has_host() {
Ok(())
} else {
Err("no host provided".to_string())
}
}
Err(err) => Err(format!("{:?}", err)),
}
}
fn main() -> Result<(), Box<dyn error::Error>> {
solana_logger::setup();
let default = WalletConfig::default();
let default_drone_port = format!("{}", default.drone_port);
let matches = app(crate_name!(), crate_description!(), crate_version!())
.arg({
let arg = Arg::with_name("config_file")
.short("c")
.long("config")
.value_name("PATH")
.takes_value(true)
.help("Configuration file to use");
if let Some(ref config_file) = *config::CONFIG_FILE {
arg.default_value(&config_file)
} else {
arg
}
})
.arg(
Arg::with_name("json_rpc_url")
.short("u")
.long("url")
.value_name("URL")
.takes_value(true)
.validator(is_url)
.help("JSON RPC URL for the solana cluster"),
)
.arg(
Arg::with_name("drone_host")
.long("drone-host")
.value_name("HOST")
.takes_value(true)
.help("Drone host to use [default: same as the --url host]"),
)
.arg(
Arg::with_name("drone_port")
.long("drone-port")
.value_name("PORT")
.takes_value(true)
.default_value(&default_drone_port)
.help("Drone port to use"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/id.json"),
)
.subcommand(
SubCommand::with_name("get")
.about("Get wallet config settings")
.arg(
Arg::with_name("specific_setting")
.index(1)
.value_name("CONFIG_FIELD")
.takes_value(true)
.possible_values(&["url", "keypair"])
.help("Return a specific config setting"),
),
)
.subcommand(
SubCommand::with_name("set")
.about("Set a wallet config setting")
.arg(
Arg::with_name("url")
.short("u")
.long("url")
.value_name("URL")
.takes_value(true)
.validator(is_url)
.help("Set default JSON RPC URL to query"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/id.json"),
)
.group(
ArgGroup::with_name("config_settings")
.args(&["url", "keypair"])
.multiple(true)
.required(true),
),
)
.get_matches();
if parse_settings(&matches)? {
let config = parse_args(&matches)?;
let result = process_command(&config)?;
println!("{}", result);
}
Ok(())
}

1
client/.gitignore vendored
View File

@@ -1,2 +1 @@
/target/
/farf/

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "0.18.1"
version = "0.16.5"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -10,19 +10,19 @@ edition = "2018"
[dependencies]
bincode = "1.1.4"
bs58 = "0.2.4"
jsonrpc-core = "13.0.0"
log = "0.4.8"
bs58 = "0.2.0"
jsonrpc-core = "12.0.0"
log = "0.4.2"
rand = "0.6.5"
rayon = "1.1.0"
reqwest = "0.9.20"
serde = "1.0.99"
serde_derive = "1.0.99"
serde_json = "1.0.40"
solana-netutil = { path = "../utils/netutil", version = "0.18.1" }
solana-sdk = { path = "../sdk", version = "0.18.1" }
reqwest = "0.9.18"
serde = "1.0.92"
serde_derive = "1.0.92"
serde_json = "1.0.39"
solana-netutil = { path = "../netutil", version = "0.16.5" }
solana-sdk = { path = "../sdk", version = "0.16.5" }
[dev-dependencies]
jsonrpc-core = "13.0.0"
jsonrpc-http-server = "13.0.0"
solana-logger = { path = "../logger", version = "0.18.1" }
jsonrpc-core = "12.0.0"
jsonrpc-http-server = "12.0.0"
solana-logger = { path = "../logger", version = "0.16.5" }

View File

@@ -9,10 +9,9 @@ use serde_json::{json, Value};
use solana_sdk::account::Account;
use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::hash::Hash;
use solana_sdk::inflation::Inflation;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{KeypairUtil, Signature};
use solana_sdk::timing::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT};
use solana_sdk::timing::{DEFAULT_NUM_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT};
use solana_sdk::transaction::{self, Transaction, TransactionError};
use std::error;
use std::io;
@@ -21,7 +20,7 @@ use std::thread::sleep;
use std::time::{Duration, Instant};
pub struct RpcClient {
client: Box<dyn GenericRpcClientRequest + Send + Sync>,
client: Box<GenericRpcClientRequest + Send + Sync>,
}
impl RpcClient {
@@ -95,44 +94,6 @@ impl RpcClient {
})
}
pub fn get_inflation(&self) -> io::Result<Inflation> {
let response = self
.client
.send(&RpcRequest::GetInflation, None, 0)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetInflation request failure: {:?}", err),
)
})?;
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetInflation parse failure: {}", err),
)
})
}
pub fn get_version(&self) -> io::Result<String> {
let response = self
.client
.send(&RpcRequest::GetVersion, None, 0)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetVersion request failure: {:?}", err),
)
})?;
serde_json::to_string(&response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetVersion parse failure: {}", err),
)
})
}
pub fn send_and_confirm_transaction<T: KeypairUtil>(
&self,
transaction: &mut Transaction,
@@ -155,7 +116,7 @@ impl RpcClient {
if cfg!(not(test)) {
// Retry ~twice during a slot
sleep(Duration::from_millis(
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND,
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_NUM_TICKS_PER_SECOND,
));
}
};
@@ -201,7 +162,7 @@ impl RpcClient {
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
// when all the write transactions modify the same program account (eg, deploying a
// new program)
sleep(Duration::from_millis(1000 / DEFAULT_TICKS_PER_SECOND));
sleep(Duration::from_millis(1000 / DEFAULT_NUM_TICKS_PER_SECOND));
}
let signature = self.send_transaction(&transaction).ok();
@@ -215,7 +176,7 @@ impl RpcClient {
if cfg!(not(test)) {
// Retry ~twice during a slot
sleep(Duration::from_millis(
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND,
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_NUM_TICKS_PER_SECOND,
));
}
@@ -389,7 +350,7 @@ impl RpcClient {
let blockhash = blockhash.parse().map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetRecentBlockhash hash parse failure: {:?}", err),
format!("GetRecentBlockhash parse failure: {:?}", err),
)
})?;
Ok((blockhash, fee_calculator))
@@ -407,7 +368,7 @@ impl RpcClient {
// Retry ~twice during a slot
sleep(Duration::from_millis(
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND,
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_NUM_TICKS_PER_SECOND,
));
num_retries -= 1;
}
@@ -417,33 +378,6 @@ impl RpcClient {
))
}
pub fn get_genesis_blockhash(&self) -> io::Result<Hash> {
let response = self
.client
.send(&RpcRequest::GetGenesisBlockhash, None, 0)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetGenesisBlockhash request failure: {:?}", err),
)
})?;
let blockhash = serde_json::from_value::<String>(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetGenesisBlockhash parse failure: {:?}", err),
)
})?;
let blockhash = blockhash.parse().map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetGenesisBlockhash hash parse failure: {:?}", err),
)
})?;
Ok(blockhash)
}
pub fn poll_balance_with_timeout(
&self,
pubkey: &Pubkey,
@@ -542,7 +476,7 @@ impl RpcClient {
&self,
signature: &Signature,
min_confirmed_blocks: usize,
) -> io::Result<usize> {
) -> io::Result<()> {
let mut now = Instant::now();
let mut confirmed_blocks = 0;
loop {
@@ -551,11 +485,8 @@ impl RpcClient {
Ok(count) => {
if confirmed_blocks != count {
info!(
"signature {} confirmed {} out of {} after {} ms",
signature,
count,
min_confirmed_blocks,
now.elapsed().as_millis()
"signature {} confirmed {} out of {}",
signature, count, min_confirmed_blocks
);
now = Instant::now();
confirmed_blocks = count;
@@ -569,23 +500,12 @@ impl RpcClient {
}
};
if now.elapsed().as_secs() > 15 {
info!(
"signature {} confirmed {} out of {} failed after {} ms",
signature,
confirmed_blocks,
min_confirmed_blocks,
now.elapsed().as_millis()
);
if confirmed_blocks > 0 {
return Ok(confirmed_blocks);
} else {
// TODO: Return a better error.
return Err(io::Error::new(io::ErrorKind::Other, "signature not found"));
}
// TODO: Return a better error.
return Err(io::Error::new(io::ErrorKind::Other, "signature not found"));
}
sleep(Duration::from_secs(1));
sleep(Duration::from_millis(250));
}
Ok(confirmed_blocks)
Ok(())
}
pub fn get_num_blocks_since_signature_confirmation(

View File

@@ -4,7 +4,7 @@ use crate::rpc_request::{RpcError, RpcRequest};
use log::*;
use reqwest;
use reqwest::header::CONTENT_TYPE;
use solana_sdk::timing::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT};
use solana_sdk::timing::{DEFAULT_NUM_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT};
use std::thread::sleep;
use std::time::Duration;
@@ -73,7 +73,7 @@ impl GenericRpcClientRequest for RpcClientRequest {
// Sleep for approximately half a slot
sleep(Duration::from_millis(
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND,
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_NUM_TICKS_PER_SECOND,
));
}
}

View File

@@ -9,21 +9,17 @@ pub enum RpcRequest {
GetAccountInfo,
GetBalance,
GetClusterNodes,
GetGenesisBlockhash,
GetInflation,
GetNumBlocksSinceSignatureConfirmation,
GetProgramAccounts,
GetRecentBlockhash,
GetSignatureStatus,
GetSlot,
GetSlotLeader,
GetStorageTurn,
GetStorageTurnRate,
GetSlotsPerSegment,
GetEpochVoteAccounts,
GetStorageBlockhash,
GetStorageSlot,
GetStoragePubkeysForSlot,
GetTransactionCount,
GetVersion,
GetVoteAccounts,
RegisterNode,
RequestAirdrop,
SendTransaction,
@@ -40,8 +36,6 @@ impl RpcRequest {
RpcRequest::GetAccountInfo => "getAccountInfo",
RpcRequest::GetBalance => "getBalance",
RpcRequest::GetClusterNodes => "getClusterNodes",
RpcRequest::GetGenesisBlockhash => "getGenesisBlockhash",
RpcRequest::GetInflation => "getInflation",
RpcRequest::GetNumBlocksSinceSignatureConfirmation => {
"getNumBlocksSinceSignatureConfirmation"
}
@@ -50,13 +44,11 @@ impl RpcRequest {
RpcRequest::GetSignatureStatus => "getSignatureStatus",
RpcRequest::GetSlot => "getSlot",
RpcRequest::GetSlotLeader => "getSlotLeader",
RpcRequest::GetStorageTurn => "getStorageTurn",
RpcRequest::GetStorageTurnRate => "getStorageTurnRate",
RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
RpcRequest::GetEpochVoteAccounts => "getEpochVoteAccounts",
RpcRequest::GetStorageBlockhash => "getStorageBlockhash",
RpcRequest::GetStorageSlot => "getStorageSlot",
RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
RpcRequest::GetTransactionCount => "getTransactionCount",
RpcRequest::GetVersion => "getVersion",
RpcRequest::GetVoteAccounts => "getVoteAccounts",
RpcRequest::RegisterNode => "registerNode",
RpcRequest::RequestAirdrop => "requestAirdrop",
RpcRequest::SendTransaction => "sendTransaction",
@@ -112,10 +104,6 @@ mod tests {
let request = test_request.build_request_json(1, Some(addr));
assert_eq!(request["method"], "getBalance");
let test_request = RpcRequest::GetInflation;
let request = test_request.build_request_json(1, None);
assert_eq!(request["method"], "getInflation");
let test_request = RpcRequest::GetRecentBlockhash;
let request = test_request.build_request_json(1, None);
assert_eq!(request["method"], "getRecentBlockhash");

View File

@@ -16,7 +16,7 @@ use solana_sdk::packet::PACKET_DATA_SIZE;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
use solana_sdk::system_instruction;
use solana_sdk::timing::{duration_as_ms, MAX_PROCESSING_AGE};
use solana_sdk::timing::duration_as_ms;
use solana_sdk::transaction::{self, Transaction};
use solana_sdk::transport::Result as TransportResult;
use std::io;
@@ -203,39 +203,20 @@ impl ThinClient {
keypairs: &[&Keypair],
transaction: &mut Transaction,
tries: usize,
pending_confirmations: usize,
min_confirmed_blocks: usize,
) -> io::Result<Signature> {
for x in 0..tries {
let now = Instant::now();
let mut buf = vec![0; serialized_size(&transaction).unwrap() as usize];
let mut wr = std::io::Cursor::new(&mut buf[..]);
let mut num_confirmed = 0;
let mut wait_time = MAX_PROCESSING_AGE;
serialize_into(&mut wr, &transaction)
.expect("serialize Transaction in pub fn transfer_signed");
// resend the same transaction until the transaction has no chance of succeeding
while now.elapsed().as_secs() < wait_time as u64 {
if num_confirmed == 0 {
// Send the transaction if there has been no confirmation (e.g. the first time)
self.transactions_socket
.send_to(&buf[..], &self.transactions_addr())?;
}
if let Ok(confirmed_blocks) = self.poll_for_signature_confirmation(
&transaction.signatures[0],
pending_confirmations,
) {
num_confirmed = confirmed_blocks;
if confirmed_blocks >= pending_confirmations {
return Ok(transaction.signatures[0]);
}
// Since network has seen the transaction, wait longer to receive
// all pending confirmations. Resending the transaction could result into
// extra transaction fees
wait_time = wait_time.max(
MAX_PROCESSING_AGE * pending_confirmations.saturating_sub(num_confirmed),
);
}
self.transactions_socket
.send_to(&buf[..], &self.transactions_addr())?;
if self
.poll_for_signature_confirmation(&transaction.signatures[0], min_confirmed_blocks)
.is_ok()
{
return Ok(transaction.signatures[0]);
}
info!(
"{} tries failed transfer to {}",
@@ -397,7 +378,7 @@ impl SyncClient for ThinClient {
&self,
signature: &Signature,
min_confirmed_blocks: usize,
) -> TransportResult<usize> {
) -> TransportResult<()> {
Ok(self
.rpc_client()
.poll_for_signature_confirmation(signature, min_confirmed_blocks)?)

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