Compare commits

...

41 Commits

Author SHA1 Message Date
f2fda14333 Reduce max snapshot hashes to stay under MTU (bp #8541) (#8544)
automerge
2020-02-29 09:17:09 -08:00
1c576d4a68 Upgrade to Rust 1.41.1
(cherry picked from commit 7d27be2a73)
2020-02-29 09:27:11 -07:00
f6232e1b3c Fix skipping own leader slots (#8533) (#8540)
automerge
2020-02-29 01:27:46 -08:00
ad71fa3f12 rpc: GET for /snapshot.tar.bz2 now redirects to the latest snapshot (bp #8536) (#8538)
automerge
2020-02-28 23:45:15 -08:00
9c326c7c71 Ensure the validator's identity pubkey is not provided as a --trusted-validator (#8525) (#8527)
automerge
2020-02-27 22:07:07 -08:00
ac545cadaf Add versioning (#8348) (#8524)
automerge
2020-02-27 20:12:49 -08:00
082d8fff36 Use legit solana message in verify (#8513) (#8523)
automerge
2020-02-27 19:36:37 -08:00
2c3632a042 Determine vote_state ahead of time (#8303) (#8521)
automerge
2020-02-27 18:32:27 -08:00
7b23e79922 Add snapshot hash of full accounts state (#8295) (#8515)
* Add snapshot hash of full accounts state

* Use normal hashing for the accounts delta state

* Add merkle

(cherry picked from commit 947a339714)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-02-27 17:36:28 -08:00
9afd14a0c6 Import Tour de SOL docs (#8516) (#8519)
automerge
2020-02-27 17:15:36 -08:00
f42aa34ab6 Reorder InstructionError to remain compatible with v0.23
(cherry picked from commit 7dac8e2dde)
2020-02-27 18:07:00 -07:00
43af91ff4b Ledger messaging cleanup (#8506) (#8508)
automerge
2020-02-27 12:38:09 -08:00
a920a0f946 Fix cluster economics figures and spelling in docs (#8502) (#8505)
automerge
2020-02-27 02:49:10 -08:00
4a8a6d0b3f Remove loop (#8496)
automerge
2020-02-27 00:38:27 -08:00
a64b8a2705 Rename snapshot.tar.bz2 to snapshot-<slot>-<hash>.tar.bz2 (bp #8482) (#8501)
automerge
2020-02-26 23:31:44 -08:00
0198f9e8af Peg snapshot version to 1.0.0 2020-02-26 22:07:50 -07:00
1582a3a927 Cargo.lock 2020-02-26 21:17:40 -07:00
e713f0e5fb Update voting simulation (#8489) 2020-02-26 20:00:20 -08:00
77031000c4 Choose more appropriate options for pubsub websocket server (#8354) (#8492)
automerge
2020-02-26 18:23:50 -08:00
635a962fba Reference the v1.0.0 installer 2020-02-26 19:20:58 -07:00
c59ec2dcff Add flag to confirm key on device (#8478) (#8490)
automerge
2020-02-26 17:31:52 -08:00
abc6c5e264 Limit leader schedule search space (#8468) (#8486)
automerge
2020-02-26 16:11:14 -08:00
87cfac12dd Validate the genesis config downloaded over RPC before accepting it (bp #8474) (#8481)
automerge
2020-02-26 15:12:06 -08:00
60b43e34b6 Ledger hardware wallet docs (#8472) (#8479)
automerge
2020-02-26 14:42:08 -08:00
9ab6222f03 Move docs from book/ to docs/ (#8469) (#8471)
automerge
2020-02-26 08:01:44 -08:00
2298dd5c07 Use runtime executor to send pubsub notifications (#8353) (#8465)
automerge
2020-02-25 21:45:18 -08:00
822d166115 live-slots now displays the rate the root slot is advancing (#8464)
automerge
2020-02-25 21:18:10 -08:00
8f71580615 Allow withdrawer to change the authorized stake key (#8456) (#8462)
automerge
2020-02-25 19:30:59 -08:00
cc3352ff06 Ledger key path rework (#8453) (#8457)
automerge
2020-02-25 18:00:53 -08:00
8f5928b7c7 Promote dangerous cond. from just warning to panic (#8439) (#8449)
automerge
2020-02-25 15:31:06 -08:00
888e9617ff 🐌🐌 Publish crates for even longer longer 2020-02-25 09:23:20 -07:00
4695c4cf7d Add --no-check-vote-account argument (#8430) (#8435)
automerge
2020-02-25 00:23:50 -08:00
bbfc56ff7f Make solana root key accessible on Ledger (#8421) (#8431)
automerge
2020-02-24 22:28:28 -08:00
4103d99018 Bump version to 1.0.1 2020-02-24 23:24:27 -07:00
c375ce1fcd CLI: collect and deduplicate signers (#8398) (#8423)
automerge
2020-02-24 17:29:34 -08:00
df813b31c5 Fix SDK deps 2020-02-24 17:30:46 -07:00
7db92e2951 Drop print- prefix from slot/accounts command
(cherry picked from commit 89baa94002)
2020-02-24 17:28:36 -07:00
6585518f78 Add genesis subcommand
(cherry picked from commit 1ef3478709)
2020-02-24 17:28:36 -07:00
6398e256e4 Move shred_version module to sdk/
(cherry picked from commit 73063544bd)
2020-02-24 17:28:36 -07:00
b64ebb45e7 validator: snapshot fetching cleanup (bp #8413) (#8417)
automerge
2020-02-24 15:22:34 -08:00
307ac66d9c Reinstate create-stale-account w/ seed test (#8401) (#8402)
automerge
2020-02-22 10:56:39 -08:00
319 changed files with 6997 additions and 3949 deletions

View File

@ -1,4 +1,4 @@
root: ./book/src root: ./docs/src
structure: structure:
readme: introduction.md readme: introduction.md

6
.gitignore vendored
View File

@ -1,6 +1,6 @@
/book/html/ /docs/html/
/book/src/tests.ok /docs/src/tests.ok
/book/src/.gitbook/assets/*.svg /docs/src/.gitbook/assets/*.svg
/farf/ /farf/
/solana-release/ /solana-release/
/solana-release.tar.bz2 /solana-release.tar.bz2

View File

@ -45,7 +45,7 @@ $ git pull --rebase upstream master
If there are no functional changes, PRs can be very large and that's no If there are no functional changes, PRs can be very large and that's no
problem. If, however, your changes are making meaningful changes or additions, problem. If, however, your changes are making meaningful changes or additions,
then about 1,000 lines of changes is about the most you should ask a Solana then about 1.0.1 lines of changes is about the most you should ask a Solana
maintainer to review. maintainer to review.
### Should I send small PRs as I develop large, new components? ### Should I send small PRs as I develop large, new components?
@ -224,21 +224,20 @@ Inventing new terms is allowed, but should only be done when the term is widely
used and understood. Avoid introducing new 3-letter terms, which can be used and understood. Avoid introducing new 3-letter terms, which can be
confused with 3-letter acronyms. confused with 3-letter acronyms.
[Terms currently in use](book/src/terminology.md) [Terms currently in use](docs/src/terminology.md)
## Design Proposals ## Design Proposals
Solana's architecture is described by a book generated from markdown files in Solana's architecture is described by docs generated from markdown files in
the `book/src/` directory, maintained by an *editor* (currently @garious). To the `docs/src/` directory, maintained by an *editor* (currently @garious). To
add a design proposal, you'll need to at least propose a change the content add a design proposal, you'll need to include it in the
under the [Accepted Design [Accepted Design Proposals](https://docs.solana.com/proposals)
Proposals](https://docs.solana.com/book/v/master/proposals) chapter. Here's section of the Solana docs. Here's the full process:
the full process:
1. Propose a design by creating a PR that adds a markdown document to the 1. Propose a design by creating a PR that adds a markdown document to the
directory `book/src/` and references it from the [table of `docs/src/proposals` directory and references it from the [table of
contents](book/src/SUMMARY.md). Add any relevant *maintainers* to the PR contents](docs/src/SUMMARY.md). Add any relevant *maintainers* to the PR
review. review.
2. The PR being merged indicates your proposed change was accepted and that the 2. The PR being merged indicates your proposed change was accepted and that the
maintainers support your plan of attack. maintainers support your plan of attack.

880
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -23,12 +23,12 @@ It's possible for a centralized database to process 710,000 transactions per sec
Furthermore, and much to our surprise, it can be implemented using a mechanism that has existed in Bitcoin since day one. The Bitcoin feature is called nLocktime and it can be used to postdate transactions using block height instead of a timestamp. As a Bitcoin client, you'd use block height instead of a timestamp if you don't trust the network. Block height turns out to be an instance of what's being called a Verifiable Delay Function in cryptography circles. It's a cryptographically secure way to say time has passed. In Solana, we use a far more granular verifiable delay function, a SHA 256 hash chain, to checkpoint the ledger and coordinate consensus. With it, we implement Optimistic Concurrency Control and are now well en route towards that theoretical limit of 710,000 transactions per second. Furthermore, and much to our surprise, it can be implemented using a mechanism that has existed in Bitcoin since day one. The Bitcoin feature is called nLocktime and it can be used to postdate transactions using block height instead of a timestamp. As a Bitcoin client, you'd use block height instead of a timestamp if you don't trust the network. Block height turns out to be an instance of what's being called a Verifiable Delay Function in cryptography circles. It's a cryptographically secure way to say time has passed. In Solana, we use a far more granular verifiable delay function, a SHA 256 hash chain, to checkpoint the ledger and coordinate consensus. With it, we implement Optimistic Concurrency Control and are now well en route towards that theoretical limit of 710,000 transactions per second.
Architecture Documentation
=== ===
Before you jump into the code, review the online book [Solana: Blockchain Rebuilt for Scale](https://docs.solana.com/book/). Before you jump into the code, review the documentation [Solana: Blockchain Rebuilt for Scale](https://docs.solana.com).
(The _latest_ development version of the online book is also [available here](https://docs.solana.com/book/v/master/).) (The _latest_ development version of the docs is [available here](https://docs.solana.com/v/master).)
Release Binaries Release Binaries
=== ===
@ -121,7 +121,7 @@ $ cargo test
Local Testnet Local Testnet
--- ---
Start your own testnet locally, instructions are in the book [Solana: Blockchain Rebuild for Scale: Getting Started](https://docs.solana.com/book/building-from-source). Start your own testnet locally, instructions are in the online docs [Solana: Blockchain Rebuild for Scale: Getting Started](https://docs.solana.com/building-from-source).
Remote Testnets Remote Testnets
--- ---

View File

@ -138,7 +138,7 @@ There are three release channels that map to branches as follows:
### Update documentation ### Update documentation
TODO: Documentation update procedure is WIP as we move to gitbook TODO: Documentation update procedure is WIP as we move to gitbook
Document the new recommended version by updating `book/src/running-archiver.md` and `book/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version. Document the new recommended version by updating `docs/src/running-archiver.md` and `docs/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
### Update software on devnet.solana.com ### Update software on devnet.solana.com

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-archiver-lib" name = "solana-archiver-lib"
version = "1.0.0" version = "1.0.1"
description = "Solana Archiver Library" description = "Solana Archiver Library"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@ -15,22 +15,22 @@ ed25519-dalek = "=1.0.0-pre.1"
log = "0.4.8" log = "0.4.8"
rand = "0.6.5" rand = "0.6.5"
rand_chacha = "0.1.1" rand_chacha = "0.1.1"
solana-client = { path = "../client", version = "1.0.0" } solana-client = { path = "../client", version = "1.0.1" }
solana-storage-program = { path = "../programs/storage", version = "1.0.0" } solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
thiserror = "1.0" thiserror = "1.0"
serde = "1.0.104" serde = "1.0.104"
serde_json = "1.0.46" serde_json = "1.0.46"
serde_derive = "1.0.103" serde_derive = "1.0.103"
solana-net-utils = { path = "../net-utils", version = "1.0.0" } solana-net-utils = { path = "../net-utils", version = "1.0.1" }
solana-chacha = { path = "../chacha", version = "1.0.0" } solana-chacha = { path = "../chacha", version = "1.0.1" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.0" } solana-chacha-sys = { path = "../chacha-sys", version = "1.0.1" }
solana-ledger = { path = "../ledger", version = "1.0.0" } solana-ledger = { path = "../ledger", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-perf = { path = "../perf", version = "1.0.0" } solana-perf = { path = "../perf", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
solana-core = { path = "../core", version = "1.0.0" } solana-core = { path = "../core", version = "1.0.1" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.0" } solana-archiver-utils = { path = "../archiver-utils", version = "1.0.1" }
solana-metrics = { path = "../metrics", version = "1.0.0" } solana-metrics = { path = "../metrics", version = "1.0.1" }
[dev-dependencies] [dev-dependencies]
hex = "0.4.0" hex = "0.4.0"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-archiver-utils" name = "solana-archiver-utils"
version = "1.0.0" version = "1.0.1"
description = "Solana Archiver Utils" description = "Solana Archiver Utils"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@ -11,12 +11,12 @@ edition = "2018"
[dependencies] [dependencies]
log = "0.4.8" log = "0.4.8"
rand = "0.6.5" rand = "0.6.5"
solana-chacha = { path = "../chacha", version = "1.0.0" } solana-chacha = { path = "../chacha", version = "1.0.1" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.0" } solana-chacha-sys = { path = "../chacha-sys", version = "1.0.1" }
solana-ledger = { path = "../ledger", version = "1.0.0" } solana-ledger = { path = "../ledger", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-perf = { path = "../perf", version = "1.0.0" } solana-perf = { path = "../perf", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
[dev-dependencies] [dev-dependencies]
hex = "0.4.0" hex = "0.4.0"

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-archiver" name = "solana-archiver"
version = "1.0.0" version = "1.0.1"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@ -10,11 +10,11 @@ homepage = "https://solana.com/"
[dependencies] [dependencies]
clap = "2.33.0" clap = "2.33.0"
console = "0.9.2" console = "0.9.2"
solana-clap-utils = { path = "../clap-utils", version = "1.0.0" } solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
solana-core = { path = "../core", version = "1.0.0" } solana-core = { path = "../core", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-metrics = { path = "../metrics", version = "1.0.0" } solana-metrics = { path = "../metrics", version = "1.0.1" }
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.0" } solana-archiver-lib = { path = "../archiver-lib", version = "1.0.1" }
solana-net-utils = { path = "../net-utils", version = "1.0.0" } solana-net-utils = { path = "../net-utils", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }

View File

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

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-bench-exchange" name = "solana-bench-exchange"
version = "1.0.0" version = "1.0.1"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@ -18,17 +18,17 @@ rand = "0.6.5"
rayon = "1.2.0" rayon = "1.2.0"
serde_json = "1.0.46" serde_json = "1.0.46"
serde_yaml = "0.8.11" serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.0" } solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
solana-core = { path = "../core", version = "1.0.0" } solana-core = { path = "../core", version = "1.0.1" }
solana-genesis = { path = "../genesis", version = "1.0.0" } solana-genesis = { path = "../genesis", version = "1.0.1" }
solana-client = { path = "../client", version = "1.0.0" } solana-client = { path = "../client", version = "1.0.1" }
solana-faucet = { path = "../faucet", version = "1.0.0" } solana-faucet = { path = "../faucet", version = "1.0.1" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.0" } solana-exchange-program = { path = "../programs/exchange", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-metrics = { path = "../metrics", version = "1.0.0" } solana-metrics = { path = "../metrics", version = "1.0.1" }
solana-net-utils = { path = "../net-utils", version = "1.0.0" } solana-net-utils = { path = "../net-utils", version = "1.0.1" }
solana-runtime = { path = "../runtime", version = "1.0.0" } solana-runtime = { path = "../runtime", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
[dev-dependencies] [dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "1.0.0" } solana-local-cluster = { path = "../local-cluster", version = "1.0.1" }

View File

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

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-bench-tps" name = "solana-bench-tps"
version = "1.0.0" version = "1.0.1"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@ -14,24 +14,24 @@ log = "0.4.8"
rayon = "1.2.0" rayon = "1.2.0"
serde_json = "1.0.46" serde_json = "1.0.46"
serde_yaml = "0.8.11" serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.0" } solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
solana-core = { path = "../core", version = "1.0.0" } solana-core = { path = "../core", version = "1.0.1" }
solana-genesis = { path = "../genesis", version = "1.0.0" } solana-genesis = { path = "../genesis", version = "1.0.1" }
solana-client = { path = "../client", version = "1.0.0" } solana-client = { path = "../client", version = "1.0.1" }
solana-faucet = { path = "../faucet", version = "1.0.0" } solana-faucet = { path = "../faucet", version = "1.0.1" }
solana-librapay = { path = "../programs/librapay", version = "1.0.0", optional = true } solana-librapay = { path = "../programs/librapay", version = "1.0.1", optional = true }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-metrics = { path = "../metrics", version = "1.0.0" } solana-metrics = { path = "../metrics", version = "1.0.1" }
solana-measure = { path = "../measure", version = "1.0.0" } solana-measure = { path = "../measure", version = "1.0.1" }
solana-net-utils = { path = "../net-utils", version = "1.0.0" } solana-net-utils = { path = "../net-utils", version = "1.0.1" }
solana-runtime = { path = "../runtime", version = "1.0.0" } solana-runtime = { path = "../runtime", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
solana-move-loader-program = { path = "../programs/move_loader", version = "1.0.0", optional = true } solana-move-loader-program = { path = "../programs/move_loader", version = "1.0.1", optional = true }
[dev-dependencies] [dev-dependencies]
serial_test = "0.3.2" serial_test = "0.3.2"
serial_test_derive = "0.4.0" serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.0.0" } solana-local-cluster = { path = "../local-cluster", version = "1.0.1" }
[features] [features]
move = ["solana-librapay", "solana-move-loader-program"] move = ["solana-librapay", "solana-move-loader-program"]

View File

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

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-chacha-cuda" name = "solana-chacha-cuda"
version = "1.0.0" version = "1.0.1"
description = "Solana Chacha Cuda APIs" description = "Solana Chacha Cuda APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@ -10,12 +10,12 @@ edition = "2018"
[dependencies] [dependencies]
log = "0.4.8" log = "0.4.8"
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.0" } solana-archiver-utils = { path = "../archiver-utils", version = "1.0.1" }
solana-chacha = { path = "../chacha", version = "1.0.0" } solana-chacha = { path = "../chacha", version = "1.0.1" }
solana-ledger = { path = "../ledger", version = "1.0.0" } solana-ledger = { path = "../ledger", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-perf = { path = "../perf", version = "1.0.0" } solana-perf = { path = "../perf", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
[dev-dependencies] [dev-dependencies]
hex-literal = "0.2.1" hex-literal = "0.2.1"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-chacha-sys" name = "solana-chacha-sys"
version = "1.0.0" version = "1.0.1"
description = "Solana chacha-sys" description = "Solana chacha-sys"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-chacha" name = "solana-chacha"
version = "1.0.0" version = "1.0.1"
description = "Solana Chacha APIs" description = "Solana Chacha APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@ -12,11 +12,11 @@ edition = "2018"
log = "0.4.8" log = "0.4.8"
rand = "0.6.5" rand = "0.6.5"
rand_chacha = "0.1.1" rand_chacha = "0.1.1"
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.0" } solana-chacha-sys = { path = "../chacha-sys", version = "1.0.1" }
solana-ledger = { path = "../ledger", version = "1.0.0" } solana-ledger = { path = "../ledger", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-perf = { path = "../perf", version = "1.0.0" } solana-perf = { path = "../perf", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
[dev-dependencies] [dev-dependencies]
hex-literal = "0.2.1" hex-literal = "0.2.1"

View File

@ -9,7 +9,7 @@
# ./affects-files.sh ^snap/ -- anything under the snap/ subdirectory # ./affects-files.sh ^snap/ -- anything under the snap/ subdirectory
# ./affects-files.sh snap/ -- also matches foo/snap/ # ./affects-files.sh snap/ -- also matches foo/snap/
# Any pattern starting with the ! character will be negated: # Any pattern starting with the ! character will be negated:
# ./affects-files.sh !^book/ -- anything *not* under the book/ subdirectory # ./affects-files.sh !^docs/ -- anything *not* under the docs/ subdirectory
# #
set -e set -e
cd "$(dirname "$0")"/.. cd "$(dirname "$0")"/..

View File

@ -6,7 +6,7 @@ steps:
timeout_in_minutes: 60 timeout_in_minutes: 60
name: "publish docker" name: "publish docker"
- command: "ci/publish-crate.sh" - command: "ci/publish-crate.sh"
timeout_in_minutes: 120 timeout_in_minutes: 240
name: "publish crate" name: "publish crate"
branches: "!master" branches: "!master"
- command: "ci/publish-bpf-sdk.sh" - command: "ci/publish-bpf-sdk.sh"
@ -15,6 +15,6 @@ steps:
- command: "ci/publish-tarball.sh" - command: "ci/publish-tarball.sh"
timeout_in_minutes: 60 timeout_in_minutes: 60
name: "publish tarball" name: "publish tarball"
- command: "ci/publish-book.sh" - command: "ci/publish-docs.sh"
timeout_in_minutes: 15 timeout_in_minutes: 15
name: "publish book" name: "publish docs"

View File

@ -1,4 +1,4 @@
FROM solanalabs/rust:1.41.0 FROM solanalabs/rust:1.41.1
ARG date ARG date
RUN set -x \ RUN set -x \

View File

@ -1,6 +1,6 @@
# Note: when the rust version is changed also modify # Note: when the rust version is changed also modify
# ci/rust-version.sh to pick up the new image tag # ci/rust-version.sh to pick up the new image tag
FROM rust:1.41.0 FROM rust:1.41.1
# Add Google Protocol Buffers for Libra's metrics library. # Add Google Protocol Buffers for Libra's metrics library.
ENV PROTOC_VERSION 3.8.0 ENV PROTOC_VERSION 3.8.0

View File

@ -11,10 +11,10 @@ if [[ -n $CI_BRANCH ]]; then
set -x set -x
( (
. ci/rust-version.sh stable . ci/rust-version.sh stable
ci/docker-run.sh "$rust_stable_docker_image" make -Cbook -B svg ci/docker-run.sh "$rust_stable_docker_image" make -C docs -B svg
) )
# make a local commit for the svgs # make a local commit for the svgs
git add -A -f book/src/.gitbook/assets/. git add -A -f docs/src/.gitbook/assets/.
if ! git diff-index --quiet HEAD; then if ! git diff-index --quiet HEAD; then
git config user.email maintainers@solana.com git config user.email maintainers@solana.com
git config user.name "$me" git config user.name "$me"

View File

@ -16,13 +16,13 @@
if [[ -n $RUST_STABLE_VERSION ]]; then if [[ -n $RUST_STABLE_VERSION ]]; then
stable_version="$RUST_STABLE_VERSION" stable_version="$RUST_STABLE_VERSION"
else else
stable_version=1.41.0 stable_version=1.41.1
fi fi
if [[ -n $RUST_NIGHTLY_VERSION ]]; then if [[ -n $RUST_NIGHTLY_VERSION ]]; then
nightly_version="$RUST_NIGHTLY_VERSION" nightly_version="$RUST_NIGHTLY_VERSION"
else else
nightly_version=2020-02-06 nightly_version=2020-02-27
fi fi

View File

@ -25,7 +25,7 @@ _ cargo +"$rust_stable" audit --version
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2020-0002 _ cargo +"$rust_stable" audit --ignore RUSTSEC-2020-0002
_ ci/nits.sh _ ci/nits.sh
_ ci/order-crates-for-publishing.py _ ci/order-crates-for-publishing.py
_ book/build.sh _ docs/build.sh
_ ci/check-ssh-keys.sh _ ci/check-ssh-keys.sh
{ {

View File

@ -13,12 +13,12 @@ annotate() {
# Run the appropriate test based on entrypoint # Run the appropriate test based on entrypoint
testName=$(basename "$0" .sh) testName=$(basename "$0" .sh)
# Skip if only the book has been modified # Skip if only the docs have been modified
ci/affects-files.sh \ ci/affects-files.sh \
\!^book/ \ \!^docs/ \
|| { || {
annotate --style info \ annotate --style info \
"Skipped $testName as only book files were modified" "Skipped $testName as only docs/ files were modified"
exit 0 exit 0
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-clap-utils" name = "solana-clap-utils"
version = "1.0.0" version = "1.0.1"
description = "Solana utilities for the clap" description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@ -11,8 +11,8 @@ edition = "2018"
[dependencies] [dependencies]
clap = "2.33.0" clap = "2.33.0"
rpassword = "4.0" rpassword = "4.0"
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.0" } solana-remote-wallet = { path = "../remote-wallet", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
tiny-bip39 = "0.7.0" tiny-bip39 = "0.7.0"
url = "2.1.0" url = "2.1.0"
chrono = "0.4" chrono = "0.4"

View File

@ -1,16 +1,16 @@
use crate::keypair::{ use crate::keypair::{
keypair_from_seed_phrase, keypair_util_from_path, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG, keypair_from_seed_phrase, signer_from_path, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
}; };
use chrono::DateTime; use chrono::DateTime;
use clap::ArgMatches; use clap::ArgMatches;
use solana_remote_wallet::remote_wallet::DerivationPath; use solana_remote_wallet::remote_wallet::{DerivationPath, RemoteWalletManager};
use solana_sdk::{ use solana_sdk::{
clock::UnixTimestamp, clock::UnixTimestamp,
native_token::sol_to_lamports, native_token::sol_to_lamports,
pubkey::Pubkey, pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer}, signature::{read_keypair_file, Keypair, Signature, Signer},
}; };
use std::str::FromStr; use std::{str::FromStr, sync::Arc};
// Return parsed values from matches at `name` // Return parsed values from matches at `name`
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>> pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
@ -96,14 +96,18 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
} }
// Return a signer from matches at `name` // Return a signer from matches at `name`
#[allow(clippy::type_complexity)]
pub fn signer_of( pub fn signer_of(
name: &str,
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
) -> Result<Option<Box<dyn Signer>>, Box<dyn std::error::Error>> { name: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<(Option<Box<dyn Signer>>, Option<Pubkey>), Box<dyn std::error::Error>> {
if let Some(location) = matches.value_of(name) { if let Some(location) = matches.value_of(name) {
keypair_util_from_path(matches, location, name).map(Some) let signer = signer_from_path(matches, location, name, wallet_manager)?;
let signer_pubkey = signer.pubkey();
Ok((Some(signer), Some(signer_pubkey)))
} else { } else {
Ok(None) Ok((None, None))
} }
} }
@ -115,8 +119,8 @@ pub fn derivation_of(matches: &ArgMatches<'_>, name: &str) -> Option<DerivationP
matches.value_of(name).map(|derivation_str| { matches.value_of(name).map(|derivation_str| {
let derivation_str = derivation_str.replace("'", ""); let derivation_str = derivation_str.replace("'", "");
let mut parts = derivation_str.split('/'); let mut parts = derivation_str.split('/');
let account = parts.next().unwrap().parse::<u16>().unwrap(); let account = parts.next().map(|account| account.parse::<u32>().unwrap());
let change = parts.next().map(|change| change.parse::<u16>().unwrap()); let change = parts.next().map(|change| change.parse::<u32>().unwrap());
DerivationPath { account, change } DerivationPath { account, change }
}) })
} }
@ -304,7 +308,7 @@ mod tests {
assert_eq!( assert_eq!(
derivation_of(&matches, "single"), derivation_of(&matches, "single"),
Some(DerivationPath { Some(DerivationPath {
account: 2, account: Some(2),
change: Some(3) change: Some(3)
}) })
); );
@ -315,7 +319,7 @@ mod tests {
assert_eq!( assert_eq!(
derivation_of(&matches, "single"), derivation_of(&matches, "single"),
Some(DerivationPath { Some(DerivationPath {
account: 2, account: Some(2),
change: None change: None
}) })
); );
@ -326,7 +330,7 @@ mod tests {
assert_eq!( assert_eq!(
derivation_of(&matches, "single"), derivation_of(&matches, "single"),
Some(DerivationPath { Some(DerivationPath {
account: 2, account: Some(2),
change: Some(3) change: Some(3)
}) })
); );

View File

@ -1,6 +1,5 @@
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD}; use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
use chrono::DateTime; use chrono::DateTime;
use solana_remote_wallet::remote_keypair::generate_remote_keypair;
use solana_sdk::{ use solana_sdk::{
hash::Hash, hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
@ -53,9 +52,6 @@ pub fn is_pubkey_or_keypair_or_ask_keyword(string: String) -> Result<(), String>
pub fn is_valid_signer(string: String) -> Result<(), String> { pub fn is_valid_signer(string: String) -> Result<(), String> {
match parse_keypair_path(&string) { match parse_keypair_path(&string) {
KeypairUrl::Usb(path) => generate_remote_keypair(path, None)
.map(|_| ())
.map_err(|err| format!("{:?}", err)),
KeypairUrl::Filepath(path) => is_keypair(path), KeypairUrl::Filepath(path) => is_keypair(path),
_ => Ok(()), _ => Ok(()),
} }
@ -146,7 +142,7 @@ pub fn is_derivation(value: String) -> Result<(), String> {
let mut parts = value.split('/'); let mut parts = value.split('/');
let account = parts.next().unwrap(); let account = parts.next().unwrap();
account account
.parse::<u16>() .parse::<u32>()
.map_err(|e| { .map_err(|e| {
format!( format!(
"Unable to parse derivation, provided: {}, err: {:?}", "Unable to parse derivation, provided: {}, err: {:?}",
@ -155,7 +151,7 @@ pub fn is_derivation(value: String) -> Result<(), String> {
}) })
.and_then(|_| { .and_then(|_| {
if let Some(change) = parts.next() { if let Some(change) = parts.next() {
change.parse::<u16>().map_err(|e| { change.parse::<u32>().map_err(|e| {
format!( format!(
"Unable to parse derivation, provided: {}, err: {:?}", "Unable to parse derivation, provided: {}, err: {:?}",
change, e change, e
@ -176,11 +172,12 @@ mod tests {
fn test_is_derivation() { fn test_is_derivation() {
assert_eq!(is_derivation("2".to_string()), Ok(())); assert_eq!(is_derivation("2".to_string()), Ok(()));
assert_eq!(is_derivation("0".to_string()), Ok(())); assert_eq!(is_derivation("0".to_string()), Ok(()));
assert_eq!(is_derivation("65537".to_string()), Ok(()));
assert_eq!(is_derivation("0/2".to_string()), Ok(())); assert_eq!(is_derivation("0/2".to_string()), Ok(()));
assert_eq!(is_derivation("0'/2'".to_string()), Ok(())); assert_eq!(is_derivation("0'/2'".to_string()), Ok(()));
assert!(is_derivation("a".to_string()).is_err()); assert!(is_derivation("a".to_string()).is_err());
assert!(is_derivation("65537".to_string()).is_err()); assert!(is_derivation("4294967296".to_string()).is_err());
assert!(is_derivation("a/b".to_string()).is_err()); assert!(is_derivation("a/b".to_string()).is_err());
assert!(is_derivation("0/65537".to_string()).is_err()); assert!(is_derivation("0/4294967296".to_string()).is_err());
} }
} }

View File

@ -6,7 +6,10 @@ use crate::{
use bip39::{Language, Mnemonic, Seed}; use bip39::{Language, Mnemonic, Seed};
use clap::{values_t, ArgMatches, Error, ErrorKind}; use clap::{values_t, ArgMatches, Error, ErrorKind};
use rpassword::prompt_password_stderr; use rpassword::prompt_password_stderr;
use solana_remote_wallet::remote_keypair::generate_remote_keypair; use solana_remote_wallet::{
remote_keypair::generate_remote_keypair,
remote_wallet::{RemoteWalletError, RemoteWalletManager},
};
use solana_sdk::{ use solana_sdk::{
pubkey::Pubkey, pubkey::Pubkey,
signature::{ signature::{
@ -19,6 +22,7 @@ use std::{
io::{stdin, stdout, Write}, io::{stdin, stdout, Write},
process::exit, process::exit,
str::FromStr, str::FromStr,
sync::Arc,
}; };
pub enum KeypairUrl { pub enum KeypairUrl {
@ -35,7 +39,7 @@ pub fn parse_keypair_path(path: &str) -> KeypairUrl {
} else if path == ASK_KEYWORD { } else if path == ASK_KEYWORD {
KeypairUrl::Ask KeypairUrl::Ask
} else if path.starts_with("usb://") { } else if path.starts_with("usb://") {
KeypairUrl::Usb(path.split_at(6).1.to_string()) KeypairUrl::Usb(path.to_string())
} else if let Ok(pubkey) = Pubkey::from_str(path) { } else if let Ok(pubkey) = Pubkey::from_str(path) {
KeypairUrl::Pubkey(pubkey) KeypairUrl::Pubkey(pubkey)
} else { } else {
@ -56,10 +60,11 @@ pub fn presigner_from_pubkey_sigs(
}) })
} }
pub fn keypair_util_from_path( pub fn signer_from_path(
matches: &ArgMatches, matches: &ArgMatches,
path: &str, path: &str,
keypair_name: &str, keypair_name: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> { ) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
match parse_keypair_path(path) { match parse_keypair_path(path) {
KeypairUrl::Ask => { KeypairUrl::Ask => {
@ -75,10 +80,18 @@ pub fn keypair_util_from_path(
let mut stdin = std::io::stdin(); let mut stdin = std::io::stdin();
Ok(Box::new(read_keypair(&mut stdin)?)) Ok(Box::new(read_keypair(&mut stdin)?))
} }
KeypairUrl::Usb(path) => Ok(Box::new(generate_remote_keypair( KeypairUrl::Usb(path) => {
path, if let Some(wallet_manager) = wallet_manager {
derivation_of(matches, "derivation_path"), Ok(Box::new(generate_remote_keypair(
)?)), path,
derivation_of(matches, "derivation_path"),
wallet_manager,
matches.is_present("confirm_key"),
)?))
} else {
Err(RemoteWalletError::NoDeviceFound.into())
}
}
KeypairUrl::Pubkey(pubkey) => { KeypairUrl::Pubkey(pubkey) => {
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name) let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
.as_ref() .as_ref()

View File

@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-cli-config" name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.0.0" version = "1.0.1"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"

View File

@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-cli" name = "solana-cli"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.0.0" version = "1.0.1"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@ -26,27 +26,27 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
serde = "1.0.104" serde = "1.0.104"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.46" serde_json = "1.0.46"
solana-budget-program = { path = "../programs/budget", version = "1.0.0" } solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.0" } solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
solana-cli-config = { path = "../cli-config", version = "1.0.0" } solana-cli-config = { path = "../cli-config", version = "1.0.1" }
solana-client = { path = "../client", version = "1.0.0" } solana-client = { path = "../client", version = "1.0.1" }
solana-config-program = { path = "../programs/config", version = "1.0.0" } solana-config-program = { path = "../programs/config", version = "1.0.1" }
solana-faucet = { path = "../faucet", version = "1.0.0" } solana-faucet = { path = "../faucet", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-net-utils = { path = "../net-utils", version = "1.0.0" } solana-net-utils = { path = "../net-utils", version = "1.0.1" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.0" } solana-remote-wallet = { path = "../remote-wallet", version = "1.0.1" }
solana-runtime = { path = "../runtime", version = "1.0.0" } solana-runtime = { path = "../runtime", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
solana-stake-program = { path = "../programs/stake", version = "1.0.0" } solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
solana-storage-program = { path = "../programs/storage", version = "1.0.0" } solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
solana-vote-program = { path = "../programs/vote", version = "1.0.0" } solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.0" } solana-vote-signer = { path = "../vote-signer", version = "1.0.1" }
titlecase = "1.1.0" titlecase = "1.1.0"
url = "2.1.1" url = "2.1.1"
[dev-dependencies] [dev-dependencies]
solana-core = { path = "../core", version = "1.0.0" } solana-core = { path = "../core", version = "1.0.1" }
solana-budget-program = { path = "../programs/budget", version = "1.0.0" } solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
tempfile = "3.1.0" tempfile = "3.1.0"
[[bin]] [[bin]]

File diff suppressed because it is too large Load Diff

View File

@ -8,12 +8,13 @@ use crate::{
use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand}; use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use console::{style, Emoji}; use console::{style, Emoji};
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use solana_clap_utils::{input_parsers::*, input_validators::*}; use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_from_path};
use solana_client::{ use solana_client::{
pubsub_client::{PubsubClient, SlotInfoMessage}, pubsub_client::{PubsubClient, SlotInfoMessage},
rpc_client::RpcClient, rpc_client::RpcClient,
rpc_response::RpcVoteAccountInfo, rpc_response::RpcVoteAccountInfo,
}; };
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,
clock::{self, Slot}, clock::{self, Slot},
@ -239,11 +240,15 @@ pub fn parse_catchup(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliErro
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap(); let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Catchup { node_pubkey }, command: CliCommand::Catchup { node_pubkey },
require_keypair: false, signers: vec![],
}) })
} }
pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_cluster_ping(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let lamports = value_t_or_exit!(matches, "lamports", u64); let lamports = value_t_or_exit!(matches, "lamports", u64);
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64)); let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
let count = if matches.is_present("count") { let count = if matches.is_present("count") {
@ -265,7 +270,12 @@ pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Cl
timeout, timeout,
commitment_config, commitment_config,
}, },
require_keypair: true, signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}) })
} }
@ -273,7 +283,7 @@ pub fn parse_live_slots(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliE
let url: String = value_t_or_exit!(matches, "websocket_url", String); let url: String = value_t_or_exit!(matches, "websocket_url", String);
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::LiveSlots { url }, command: CliCommand::LiveSlots { url },
require_keypair: false, signers: vec![],
}) })
} }
@ -281,7 +291,7 @@ pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
let slot = value_t_or_exit!(matches, "slot", u64); let slot = value_t_or_exit!(matches, "slot", u64);
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::GetBlockTime { slot }, command: CliCommand::GetBlockTime { slot },
require_keypair: false, signers: vec![],
}) })
} }
@ -293,7 +303,7 @@ pub fn parse_get_epoch_info(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
}; };
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::GetEpochInfo { commitment_config }, command: CliCommand::GetEpochInfo { commitment_config },
require_keypair: false, signers: vec![],
}) })
} }
@ -305,7 +315,7 @@ pub fn parse_get_slot(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliErr
}; };
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::GetSlot { commitment_config }, command: CliCommand::GetSlot { commitment_config },
require_keypair: false, signers: vec![],
}) })
} }
@ -317,7 +327,7 @@ pub fn parse_get_transaction_count(matches: &ArgMatches<'_>) -> Result<CliComman
}; };
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::GetTransactionCount { commitment_config }, command: CliCommand::GetTransactionCount { commitment_config },
require_keypair: false, signers: vec![],
}) })
} }
@ -330,7 +340,7 @@ pub fn parse_show_stakes(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Cli
use_lamports_unit, use_lamports_unit,
vote_account_pubkeys, vote_account_pubkeys,
}, },
require_keypair: false, signers: vec![],
}) })
} }
@ -339,7 +349,7 @@ pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::ShowValidators { use_lamports_unit }, command: CliCommand::ShowValidators { use_lamports_unit },
require_keypair: false, signers: vec![],
}) })
} }
@ -531,7 +541,7 @@ pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliComman
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::ShowBlockProduction { epoch, slot_limit }, command: CliCommand::ShowBlockProduction { epoch, slot_limit },
require_keypair: false, signers: vec![],
}) })
} }
@ -726,7 +736,7 @@ pub fn process_ping(
) -> ProcessResult { ) -> ProcessResult {
let to = Keypair::new().pubkey(); let to = Keypair::new().pubkey();
println_name_value("Source Account:", &config.keypair.pubkey().to_string()); println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
println_name_value("Destination Account:", &to.to_string()); println_name_value("Destination Account:", &to.to_string());
println!(); println!();
@ -745,13 +755,13 @@ pub fn process_ping(
let (recent_blockhash, fee_calculator) = rpc_client.get_new_blockhash(&last_blockhash)?; let (recent_blockhash, fee_calculator) = rpc_client.get_new_blockhash(&last_blockhash)?;
last_blockhash = recent_blockhash; last_blockhash = recent_blockhash;
let ix = system_instruction::transfer(&config.keypair.pubkey(), &to, lamports); let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
let message = Message::new(vec![ix]); let message = Message::new(vec![ix]);
let mut transaction = Transaction::new_unsigned(message); let mut transaction = Transaction::new_unsigned(message);
transaction.try_sign(&[config.keypair.as_ref()], recent_blockhash)?; transaction.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&transaction.message, &transaction.message,
)?; )?;
@ -845,11 +855,15 @@ pub fn process_ping(
pub fn process_live_slots(url: &str) -> ProcessResult { pub fn process_live_slots(url: &str) -> ProcessResult {
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let exit_clone = exit.clone();
// Disable Ctrl+C handler as sometimes the PubsubClient shutdown can stall. Also it doesn't
// really matter that the shutdown is clean because the process is terminating.
/*
let exit_clone = exit.clone();
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
exit_clone.store(true, Ordering::Relaxed); exit_clone.store(true, Ordering::Relaxed);
})?; })?;
*/
let mut current: Option<SlotInfoMessage> = None; let mut current: Option<SlotInfoMessage> = None;
let mut message = "".to_string(); let mut message = "".to_string();
@ -859,6 +873,9 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
let (mut client, receiver) = PubsubClient::slot_subscribe(url)?; let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
slot_progress.set_message("Connected."); slot_progress.set_message("Connected.");
let mut last_root = std::u64::MAX;
let mut last_root_update = Instant::now();
let mut slots_per_second = std::f64::NAN;
loop { loop {
if exit.load(Ordering::Relaxed) { if exit.load(Ordering::Relaxed) {
eprintln!("{}", message); eprintln!("{}", message);
@ -868,7 +885,27 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
match receiver.recv() { match receiver.recv() {
Ok(new_info) => { Ok(new_info) => {
message = format!("{:?}", new_info).to_owned(); if last_root == std::u64::MAX {
last_root = new_info.root;
last_root_update = Instant::now();
}
if last_root_update.elapsed().as_secs() >= 5 {
let root = new_info.root;
slots_per_second =
(root - last_root) as f64 / last_root_update.elapsed().as_secs() as f64;
last_root_update = Instant::now();
last_root = root;
}
message = if slots_per_second.is_nan() {
format!("{:?}", new_info)
} else {
format!(
"{:?} | root slot advancing at {:.2} slots/second",
new_info, slots_per_second
)
}
.to_owned();
slot_progress.set_message(&message); slot_progress.set_message(&message);
if let Some(previous) = current { if let Some(previous) = current {
@ -1105,28 +1142,38 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
mod tests { mod tests {
use super::*; use super::*;
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_sdk::signature::{write_keypair, Keypair};
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
#[test] #[test]
fn test_parse_command() { fn test_parse_command() {
let test_commands = app("test", "desc", "version"); let test_commands = app("test", "desc", "version");
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let test_cluster_version = test_commands let test_cluster_version = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "cluster-version"]); .get_matches_from(vec!["test", "cluster-version"]);
assert_eq!( assert_eq!(
parse_command(&test_cluster_version).unwrap(), parse_command(&test_cluster_version, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClusterVersion, command: CliCommand::ClusterVersion,
require_keypair: false signers: vec![],
} }
); );
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]); let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
assert_eq!( assert_eq!(
parse_command(&test_fees).unwrap(), parse_command(&test_fees, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Fees, command: CliCommand::Fees,
require_keypair: false signers: vec![],
} }
); );
@ -1136,10 +1183,10 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "block-time", &slot.to_string()]); .get_matches_from(vec!["test", "block-time", &slot.to_string()]);
assert_eq!( assert_eq!(
parse_command(&test_get_block_time).unwrap(), parse_command(&test_get_block_time, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetBlockTime { slot }, command: CliCommand::GetBlockTime { slot },
require_keypair: false signers: vec![],
} }
); );
@ -1147,12 +1194,12 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "epoch-info"]); .get_matches_from(vec!["test", "epoch-info"]);
assert_eq!( assert_eq!(
parse_command(&test_get_epoch_info).unwrap(), parse_command(&test_get_epoch_info, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetEpochInfo { command: CliCommand::GetEpochInfo {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
}, },
require_keypair: false signers: vec![],
} }
); );
@ -1160,21 +1207,21 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "genesis-hash"]); .get_matches_from(vec!["test", "genesis-hash"]);
assert_eq!( assert_eq!(
parse_command(&test_get_genesis_hash).unwrap(), parse_command(&test_get_genesis_hash, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetGenesisHash, command: CliCommand::GetGenesisHash,
require_keypair: false signers: vec![],
} }
); );
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]); let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
assert_eq!( assert_eq!(
parse_command(&test_get_slot).unwrap(), parse_command(&test_get_slot, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetSlot { command: CliCommand::GetSlot {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
}, },
require_keypair: false signers: vec![],
} }
); );
@ -1182,12 +1229,12 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "transaction-count"]); .get_matches_from(vec!["test", "transaction-count"]);
assert_eq!( assert_eq!(
parse_command(&test_transaction_count).unwrap(), parse_command(&test_transaction_count, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetTransactionCount { command: CliCommand::GetTransactionCount {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
}, },
require_keypair: false signers: vec![],
} }
); );
@ -1203,7 +1250,7 @@ mod tests {
"--confirmed", "--confirmed",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_ping).unwrap(), parse_command(&test_ping, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Ping { command: CliCommand::Ping {
lamports: 1, lamports: 1,
@ -1212,7 +1259,7 @@ mod tests {
timeout: Duration::from_secs(3), timeout: Duration::from_secs(3),
commitment_config: CommitmentConfig::default(), commitment_config: CommitmentConfig::default(),
}, },
require_keypair: true signers: vec![default_keypair.into()],
} }
); );
} }

View File

@ -4,15 +4,15 @@ use console::style;
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::derivation_of, input_parsers::derivation_of,
input_validators::{is_derivation, is_url}, input_validators::{is_derivation, is_url},
keypair::{keypair_util_from_path, SKIP_SEED_PHRASE_VALIDATION_ARG}, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
}; };
use solana_cli::{ use solana_cli::{
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliError}, cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
display::{println_name_value, println_name_value_or}, display::{println_name_value, println_name_value_or},
}; };
use solana_cli_config::config::{Config, CONFIG_FILE}; use solana_cli_config::config::{Config, CONFIG_FILE};
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
use std::error; use std::{error, sync::Arc};
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> { fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
let parse_args = match matches.subcommand() { let parse_args = match matches.subcommand() {
@ -80,7 +80,10 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
Ok(parse_args) Ok(parse_args)
} }
pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::Error>> { pub fn parse_args<'a>(
matches: &ArgMatches<'_>,
wallet_manager: Option<Arc<RemoteWalletManager>>,
) -> Result<(CliConfig<'a>, CliSigners), Box<dyn error::Error>> {
let config = if let Some(config_file) = matches.value_of("config_file") { let config = if let Some(config_file) = matches.value_of("config_file") {
Config::load(config_file).unwrap_or_default() Config::load(config_file).unwrap_or_default()
} else { } else {
@ -95,44 +98,29 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
default.json_rpc_url default.json_rpc_url
}; };
let CliCommandInfo { let default_signer_path = if matches.is_present("keypair") {
command, matches.value_of("keypair").unwrap().to_string()
require_keypair, } else if config.keypair_path != "" {
} = parse_command(&matches)?; config.keypair_path
let (keypair, keypair_path) = if require_keypair {
let path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap().to_string()
} else if config.keypair_path != "" {
config.keypair_path
} else {
let default_keypair_path = CliConfig::default_keypair_path();
if !std::path::Path::new(&default_keypair_path).exists() {
return Err(CliError::KeypairFileNotFound(format!(
"Generate a new keypair at {} with `solana-keygen new`",
default_keypair_path
))
.into());
}
default_keypair_path
};
let keypair = keypair_util_from_path(matches, &path, "keypair")?;
(keypair, Some(path))
} else { } else {
let default = CliConfig::default(); CliConfig::default_keypair_path()
(default.keypair, None)
}; };
Ok(CliConfig { let CliCommandInfo { command, signers } =
command, parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
json_rpc_url,
keypair, Ok((
keypair_path, CliConfig {
derivation_path: derivation_of(matches, "derivation_path"), command,
rpc_client: None, json_rpc_url,
verbose: matches.is_present("verbose"), signers: vec![],
}) keypair_path: default_signer_path,
derivation_path: derivation_of(matches, "derivation_path"),
rpc_client: None,
verbose: matches.is_present("verbose"),
},
signers,
))
} }
fn main() -> Result<(), Box<dyn error::Error>> { fn main() -> Result<(), Box<dyn error::Error>> {
@ -228,7 +216,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.get_matches(); .get_matches();
if parse_settings(&matches)? { if parse_settings(&matches)? {
let config = parse_args(&matches)?; let wallet_manager = maybe_wallet_manager()?;
let (mut config, signers) = parse_args(&matches, wallet_manager)?;
config.signers = signers.iter().map(|s| s.as_ref()).collect();
let result = process_command(&config)?; let result = process_command(&config)?;
println!("{}", result); println!("{}", result);
} }

View File

@ -1,12 +1,14 @@
use crate::cli::{ use crate::cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys, build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult, log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
SignerIndex,
}; };
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant, input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant,
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut, account_utils::StateMut,
@ -14,7 +16,6 @@ use solana_sdk::{
message::Message, message::Message,
nonce_state::{Meta, NonceState}, nonce_state::{Meta, NonceState},
pubkey::Pubkey, pubkey::Pubkey,
signature::Signer,
system_instruction::{ system_instruction::{
advance_nonce_account, authorize_nonce_account, create_address_with_seed, advance_nonce_account, authorize_nonce_account, create_address_with_seed,
create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError, create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError,
@ -23,6 +24,7 @@ use solana_sdk::{
system_program, system_program,
transaction::Transaction, transaction::Transaction,
}; };
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum CliNonceError { pub enum CliNonceError {
@ -216,35 +218,61 @@ impl NonceSubCommands for App<'_, '_> {
} }
} }
pub fn parse_authorize_nonce_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_authorize_nonce_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap(); let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
let new_authority = pubkey_of(matches, "new_authority").unwrap(); let new_authority = pubkey_of(matches, "new_authority").unwrap();
let nonce_authority = signer_of(NONCE_AUTHORITY_ARG.name, matches)?; let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account, nonce_account,
nonce_authority, nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
new_authority, new_authority,
}, },
require_keypair: true, signers: signer_info.signers,
}) })
} }
pub fn parse_nonce_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_nonce_create_account(
let nonce_account = signer_of("nonce_account_keypair", matches)?.unwrap(); matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (nonce_account, nonce_account_pubkey) =
signer_of(matches, "nonce_account_keypair", wallet_manager)?;
let seed = matches.value_of("seed").map(|s| s.to_string()); let seed = matches.value_of("seed").map(|s| s.to_string());
let lamports = lamports_of_sol(matches, "amount").unwrap(); let lamports = lamports_of_sol(matches, "amount").unwrap();
let nonce_authority = pubkey_of(matches, NONCE_AUTHORITY_ARG.name); let nonce_authority = pubkey_of(matches, NONCE_AUTHORITY_ARG.name);
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_account],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: nonce_account.into(), nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
seed, seed,
nonce_authority, nonce_authority,
lamports, lamports,
}, },
require_keypair: true, signers: signer_info.signers,
}) })
} }
@ -253,20 +281,33 @@ pub fn parse_get_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliEr
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_pubkey), command: CliCommand::GetNonce(nonce_account_pubkey),
require_keypair: false, signers: vec![],
}) })
} }
pub fn parse_new_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_new_nonce(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap(); let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
let nonce_authority = signer_of(NONCE_AUTHORITY_ARG.name, matches)?; let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account, nonce_account,
nonce_authority, nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
}, },
require_keypair: true, signers: signer_info.signers,
}) })
} }
@ -279,26 +320,37 @@ pub fn parse_show_nonce_account(matches: &ArgMatches<'_>) -> Result<CliCommandIn
nonce_account_pubkey, nonce_account_pubkey,
use_lamports_unit, use_lamports_unit,
}, },
require_keypair: false, signers: vec![],
}) })
} }
pub fn parse_withdraw_from_nonce_account( pub fn parse_withdraw_from_nonce_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap(); let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap(); let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
let lamports = lamports_of_sol(matches, "amount").unwrap(); let lamports = lamports_of_sol(matches, "amount").unwrap();
let nonce_authority = signer_of(NONCE_AUTHORITY_ARG.name, matches)?; let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount { command: CliCommand::WithdrawFromNonceAccount {
nonce_account, nonce_account,
nonce_authority, nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
destination_account_pubkey, destination_account_pubkey,
lamports, lamports,
}, },
require_keypair: true, signers: signer_info.signers,
}) })
} }
@ -334,40 +386,36 @@ pub fn process_authorize_nonce_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
nonce_account: &Pubkey, nonce_account: &Pubkey,
nonce_authority: Option<&dyn Signer>, nonce_authority: SignerIndex,
new_authority: &Pubkey, new_authority: &Pubkey,
) -> ProcessResult { ) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.as_ref()); let nonce_authority = config.signers[nonce_authority];
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority); let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
let message = Message::new_with_payer(vec![ix], Some(&config.keypair.pubkey())); let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign( tx.try_sign(&config.signers, recent_blockhash)?;
&[config.keypair.as_ref(), nonce_authority],
recent_blockhash,
)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), nonce_authority]);
log_instruction_custom_error::<NonceError>(result) log_instruction_custom_error::<NonceError>(result)
} }
pub fn process_create_nonce_account( pub fn process_create_nonce_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
nonce_account: &dyn Signer, nonce_account: SignerIndex,
seed: Option<String>, seed: Option<String>,
nonce_authority: Option<Pubkey>, nonce_authority: Option<Pubkey>,
lamports: u64, lamports: u64,
) -> ProcessResult { ) -> ProcessResult {
let nonce_account_pubkey = nonce_account.pubkey(); let nonce_account_pubkey = config.signers[nonce_account].pubkey();
let nonce_account_address = if let Some(seed) = seed.clone() { let nonce_account_address = if let Some(seed) = seed.clone() {
create_address_with_seed(&nonce_account_pubkey, &seed, &system_program::id())? create_address_with_seed(&nonce_account_pubkey, &seed, &system_program::id())?
} else { } else {
@ -375,7 +423,7 @@ pub fn process_create_nonce_account(
}; };
check_unique_pubkeys( check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()), (&config.signers[0].pubkey(), "cli keypair".to_string()),
(&nonce_account_address, "nonce_account".to_string()), (&nonce_account_address, "nonce_account".to_string()),
)?; )?;
@ -402,20 +450,20 @@ pub fn process_create_nonce_account(
.into()); .into());
} }
let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.pubkey()); let nonce_authority = nonce_authority.unwrap_or_else(|| config.signers[0].pubkey());
let ixs = if let Some(seed) = seed { let ixs = if let Some(seed) = seed {
create_nonce_account_with_seed( create_nonce_account_with_seed(
&config.keypair.pubkey(), // from &config.signers[0].pubkey(), // from
&nonce_account_address, // to &nonce_account_address, // to
&nonce_account_pubkey, // base &nonce_account_pubkey, // base
&seed, // seed &seed, // seed
&nonce_authority, &nonce_authority,
lamports, lamports,
) )
} else { } else {
create_nonce_account( create_nonce_account(
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&nonce_account_pubkey, &nonce_account_pubkey,
&nonce_authority, &nonce_authority,
lamports, lamports,
@ -424,23 +472,17 @@ pub fn process_create_nonce_account(
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let signers = if nonce_account_pubkey != config.keypair.pubkey() { let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
vec![config.keypair.as_ref(), nonce_account] // both must sign if `from` and `to` differ
} else {
vec![config.keypair.as_ref()] // when stake_account == config.keypair and there's a seed, we only need one signature
};
let message = Message::new_with_payer(ixs, Some(&config.keypair.pubkey()));
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &signers); let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result) log_instruction_custom_error::<SystemError>(result)
} }
@ -468,10 +510,10 @@ pub fn process_new_nonce(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
nonce_account: &Pubkey, nonce_account: &Pubkey,
nonce_authority: Option<&dyn Signer>, nonce_authority: SignerIndex,
) -> ProcessResult { ) -> ProcessResult {
check_unique_pubkeys( check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()), (&config.signers[0].pubkey(), "cli keypair".to_string()),
(&nonce_account, "nonce_account_pubkey".to_string()), (&nonce_account, "nonce_account_pubkey".to_string()),
)?; )?;
@ -482,23 +524,20 @@ pub fn process_new_nonce(
.into()); .into());
} }
let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.as_ref()); let nonce_authority = config.signers[nonce_authority];
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey()); let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let message = Message::new_with_payer(vec![ix], Some(&config.keypair.pubkey())); let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign( tx.try_sign(&config.signers, recent_blockhash)?;
&[config.keypair.as_ref(), nonce_authority],
recent_blockhash,
)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client let result =
.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), nonce_authority]); rpc_client.send_and_confirm_transaction(&mut tx, &[config.signers[0], nonce_authority]);
log_instruction_custom_error::<SystemError>(result) log_instruction_custom_error::<SystemError>(result)
} }
@ -555,33 +594,29 @@ pub fn process_withdraw_from_nonce_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
nonce_account: &Pubkey, nonce_account: &Pubkey,
nonce_authority: Option<&dyn Signer>, nonce_authority: SignerIndex,
destination_account_pubkey: &Pubkey, destination_account_pubkey: &Pubkey,
lamports: u64, lamports: u64,
) -> ProcessResult { ) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.as_ref()); let nonce_authority = config.signers[nonce_authority];
let ix = withdraw_nonce_account( let ix = withdraw_nonce_account(
nonce_account, nonce_account,
&nonce_authority.pubkey(), &nonce_authority.pubkey(),
destination_account_pubkey, destination_account_pubkey,
lamports, lamports,
); );
let message = Message::new_with_payer(vec![ix], Some(&config.keypair.pubkey())); let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign( tx.try_sign(&config.signers, recent_blockhash)?;
&[config.keypair.as_ref(), nonce_authority],
recent_blockhash,
)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), nonce_authority]);
log_instruction_custom_error::<NonceError>(result) log_instruction_custom_error::<NonceError>(result)
} }
@ -593,10 +628,9 @@ mod tests {
account::Account, account::Account,
hash::hash, hash::hash,
nonce_state::{Meta as NonceMeta, NonceState}, nonce_state::{Meta as NonceMeta, NonceState},
signature::{read_keypair_file, write_keypair, Keypair}, signature::{read_keypair_file, write_keypair, Keypair, Signer},
system_program, system_program,
}; };
use std::rc::Rc;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) { fn make_tmp_file() -> (String, NamedTempFile) {
@ -607,6 +641,9 @@ mod tests {
#[test] #[test]
fn test_parse_command() { fn test_parse_command() {
let test_commands = app("test", "desc", "version"); let test_commands = app("test", "desc", "version");
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let (keypair_file, mut tmp_file) = make_tmp_file(); let (keypair_file, mut tmp_file) = make_tmp_file();
let nonce_account_keypair = Keypair::new(); let nonce_account_keypair = Keypair::new();
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
@ -625,14 +662,14 @@ mod tests {
&Pubkey::default().to_string(), &Pubkey::default().to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_nonce_account).unwrap(), parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey, nonce_account: nonce_account_pubkey,
nonce_authority: None, nonce_authority: 0,
new_authority: Pubkey::default(), new_authority: Pubkey::default(),
}, },
require_keypair: true, signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
} }
); );
@ -646,16 +683,17 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_nonce_account).unwrap(), parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: Some( nonce_authority: 1,
read_keypair_file(&authority_keypair_file).unwrap().into()
),
new_authority: Pubkey::default(), new_authority: Pubkey::default(),
}, },
require_keypair: true, signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authority_keypair_file).unwrap().into()
],
} }
); );
@ -667,15 +705,18 @@ mod tests {
"50", "50",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_nonce_account).unwrap(), parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: Rc::new(read_keypair_file(&keypair_file).unwrap().into()), nonce_account: 1,
seed: None, seed: None,
nonce_authority: None, nonce_authority: None,
lamports: 50_000_000_000, lamports: 50_000_000_000,
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&keypair_file).unwrap().into()
],
} }
); );
@ -689,17 +730,18 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_nonce_account).unwrap(), parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: Rc::new(read_keypair_file(&keypair_file).unwrap().into()), nonce_account: 1,
seed: None, seed: None,
nonce_authority: Some( nonce_authority: Some(nonce_authority_keypair.pubkey()),
read_keypair_file(&authority_keypair_file).unwrap().pubkey()
),
lamports: 50_000_000_000, lamports: 50_000_000_000,
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&keypair_file).unwrap().into()
],
} }
); );
@ -710,10 +752,10 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_get_nonce).unwrap(), parse_command(&test_get_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_keypair.pubkey(),), command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
require_keypair: false signers: vec![],
} }
); );
@ -724,13 +766,13 @@ mod tests {
.get_matches_from(vec!["test", "new-nonce", &keypair_file]); .get_matches_from(vec!["test", "new-nonce", &keypair_file]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce).unwrap(), parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
nonce_authority: None, nonce_authority: 0,
}, },
require_keypair: true signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
} }
); );
@ -744,15 +786,16 @@ mod tests {
]); ]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce).unwrap(), parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
nonce_authority: Some( nonce_authority: 1,
read_keypair_file(&authority_keypair_file).unwrap().into()
),
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authority_keypair_file).unwrap().into()
],
} }
); );
@ -763,13 +806,13 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_show_nonce_account).unwrap(), parse_command(&test_show_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ShowNonceAccount { command: CliCommand::ShowNonceAccount {
nonce_account_pubkey: nonce_account_keypair.pubkey(), nonce_account_pubkey: nonce_account_keypair.pubkey(),
use_lamports_unit: false, use_lamports_unit: false,
}, },
require_keypair: false signers: vec![],
} }
); );
@ -782,15 +825,20 @@ mod tests {
"42", "42",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_from_nonce_account).unwrap(), parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount { command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: None, nonce_authority: 0,
destination_account_pubkey: nonce_account_pubkey, destination_account_pubkey: nonce_account_pubkey,
lamports: 42_000_000_000 lamports: 42_000_000_000
}, },
require_keypair: true signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
} }
); );
@ -802,15 +850,20 @@ mod tests {
"42", "42",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_from_nonce_account).unwrap(), parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount { command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: None, nonce_authority: 0,
destination_account_pubkey: nonce_account_pubkey, destination_account_pubkey: nonce_account_pubkey,
lamports: 42000000000 lamports: 42000000000
}, },
require_keypair: true signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
} }
); );
@ -825,17 +878,23 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_from_nonce_account).unwrap(), parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount { command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: Some( nonce_authority: 1,
read_keypair_file(&authority_keypair_file).unwrap().into()
),
destination_account_pubkey: nonce_account_pubkey, destination_account_pubkey: nonce_account_pubkey,
lamports: 42_000_000_000 lamports: 42_000_000_000
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authority_keypair_file).unwrap().into()
],
} }
); );
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,17 @@
use crate::cli::{ use crate::cli::{
check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, CliCommand, check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, CliCommand,
CliCommandInfo, CliConfig, CliError, ProcessResult, CliCommandInfo, CliConfig, CliError, ProcessResult, SignerIndex,
}; };
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{input_parsers::*, input_validators::*}; use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_from_path};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_sdk::signature::Keypair; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, message::Message, pubkey::Pubkey, signature::Signer, account_utils::StateMut, message::Message, pubkey::Pubkey, system_instruction::SystemError,
system_instruction::SystemError, transaction::Transaction, transaction::Transaction,
}; };
use solana_storage_program::storage_instruction::{self, StorageAccountType}; use solana_storage_program::storage_instruction::{self, StorageAccountType};
use std::sync::Arc;
pub trait StorageSubCommands { pub trait StorageSubCommands {
fn storage_subcommands(self) -> Self; fn storage_subcommands(self) -> Self;
@ -99,35 +100,49 @@ impl StorageSubCommands for App<'_, '_> {
pub fn parse_storage_create_archiver_account( pub fn parse_storage_create_archiver_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap(); let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
let storage_account = keypair_of(matches, "storage_account").unwrap(); let storage_account = keypair_of(matches, "storage_account").unwrap();
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::CreateStorageAccount { command: CliCommand::CreateStorageAccount {
account_owner, account_owner,
storage_account: storage_account.into(), storage_account: 1,
account_type: StorageAccountType::Archiver, account_type: StorageAccountType::Archiver,
}, },
require_keypair: true, signers: vec![
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?,
storage_account.into(),
],
}) })
} }
pub fn parse_storage_create_validator_account( pub fn parse_storage_create_validator_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap(); let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
let storage_account = keypair_of(matches, "storage_account").unwrap(); let storage_account = keypair_of(matches, "storage_account").unwrap();
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::CreateStorageAccount { command: CliCommand::CreateStorageAccount {
account_owner, account_owner,
storage_account: storage_account.into(), storage_account: 1,
account_type: StorageAccountType::Validator, account_type: StorageAccountType::Validator,
}, },
require_keypair: true, signers: vec![
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?,
storage_account.into(),
],
}) })
} }
pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_storage_claim_reward(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let node_account_pubkey = pubkey_of(matches, "node_account_pubkey").unwrap(); let node_account_pubkey = pubkey_of(matches, "node_account_pubkey").unwrap();
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap(); let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommandInfo { Ok(CliCommandInfo {
@ -135,7 +150,12 @@ pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommand
node_account_pubkey, node_account_pubkey,
storage_account_pubkey, storage_account_pubkey,
}, },
require_keypair: true, signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}) })
} }
@ -145,20 +165,21 @@ pub fn parse_storage_get_account_command(
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap(); let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::ShowStorageAccount(storage_account_pubkey), command: CliCommand::ShowStorageAccount(storage_account_pubkey),
require_keypair: false, signers: vec![],
}) })
} }
pub fn process_create_storage_account( pub fn process_create_storage_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
storage_account: SignerIndex,
account_owner: &Pubkey, account_owner: &Pubkey,
storage_account: &Keypair,
account_type: StorageAccountType, account_type: StorageAccountType,
) -> ProcessResult { ) -> ProcessResult {
let storage_account = config.signers[storage_account];
let storage_account_pubkey = storage_account.pubkey(); let storage_account_pubkey = storage_account.pubkey();
check_unique_pubkeys( check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()), (&config.signers[0].pubkey(), "cli keypair".to_string()),
( (
&storage_account_pubkey, &storage_account_pubkey,
"storage_account_pubkey".to_string(), "storage_account_pubkey".to_string(),
@ -183,7 +204,7 @@ pub fn process_create_storage_account(
.max(1); .max(1);
let ixs = storage_instruction::create_storage_account( let ixs = storage_instruction::create_storage_account(
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&account_owner, &account_owner,
&storage_account_pubkey, &storage_account_pubkey,
required_balance, required_balance,
@ -193,18 +214,14 @@ pub fn process_create_storage_account(
let message = Message::new(ixs); let message = Message::new(ixs);
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign( tx.try_sign(&config.signers, recent_blockhash)?;
&[config.keypair.as_ref(), storage_account],
recent_blockhash,
)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), storage_account]);
log_instruction_custom_error::<SystemError>(result) log_instruction_custom_error::<SystemError>(result)
} }
@ -218,13 +235,13 @@ pub fn process_claim_storage_reward(
let instruction = let instruction =
storage_instruction::claim_reward(node_account_pubkey, storage_account_pubkey); storage_instruction::claim_reward(node_account_pubkey, storage_account_pubkey);
let signers = [config.keypair.as_ref()]; let signers = [config.signers[0]];
let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey())); let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&signers, recent_blockhash)?; tx.try_sign(&signers, recent_blockhash)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
@ -260,7 +277,7 @@ pub fn process_show_storage_account(
mod tests { mod tests {
use super::*; use super::*;
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_sdk::signature::write_keypair; use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) { fn make_tmp_file() -> (String, NamedTempFile) {
@ -274,6 +291,10 @@ mod tests {
let pubkey = Pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let pubkey_string = pubkey.to_string(); let pubkey_string = pubkey.to_string();
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let (keypair_file, mut tmp_file) = make_tmp_file(); let (keypair_file, mut tmp_file) = make_tmp_file();
let storage_account_keypair = Keypair::new(); let storage_account_keypair = Keypair::new();
write_keypair(&storage_account_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&storage_account_keypair, tmp_file.as_file_mut()).unwrap();
@ -285,14 +306,22 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_archiver_storage_account).unwrap(), parse_command(
&test_create_archiver_storage_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateStorageAccount { command: CliCommand::CreateStorageAccount {
account_owner: pubkey, account_owner: pubkey,
storage_account: storage_account_keypair.into(), storage_account: 1,
account_type: StorageAccountType::Archiver, account_type: StorageAccountType::Archiver,
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
storage_account_keypair.into()
],
} }
); );
@ -309,14 +338,22 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_validator_storage_account).unwrap(), parse_command(
&test_create_validator_storage_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateStorageAccount { command: CliCommand::CreateStorageAccount {
account_owner: pubkey, account_owner: pubkey,
storage_account: storage_account_keypair.into(), storage_account: 1,
account_type: StorageAccountType::Validator, account_type: StorageAccountType::Validator,
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
storage_account_keypair.into()
],
} }
); );
@ -327,13 +364,13 @@ mod tests {
&storage_account_string, &storage_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_claim_storage_reward).unwrap(), parse_command(&test_claim_storage_reward, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClaimStorageReward { command: CliCommand::ClaimStorageReward {
node_account_pubkey: pubkey, node_account_pubkey: pubkey,
storage_account_pubkey, storage_account_pubkey,
}, },
require_keypair: true signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
} }
); );
} }

View File

@ -11,9 +11,11 @@ use serde_json::{Map, Value};
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::pubkey_of, input_parsers::pubkey_of,
input_validators::{is_pubkey, is_url}, input_validators::{is_pubkey, is_url},
keypair::signer_from_path,
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState}; use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
@ -22,7 +24,7 @@ use solana_sdk::{
signature::{Keypair, Signer}, signature::{Keypair, Signer},
transaction::Transaction, transaction::Transaction,
}; };
use std::error; use std::{error, sync::Arc};
use titlecase::titlecase; use titlecase::titlecase;
pub const MAX_SHORT_FIELD_LENGTH: usize = 70; pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
@ -224,17 +226,26 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
} }
} }
pub fn parse_validator_info_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_validator_info_command(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey"); let info_pubkey = pubkey_of(matches, "info_pubkey");
// Prepare validator info // Prepare validator info
let validator_info = parse_args(&matches); let validator_info = parse_args(matches);
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::SetValidatorInfo { command: CliCommand::SetValidatorInfo {
validator_info, validator_info,
force_keybase: matches.is_present("force"), force_keybase: matches.is_present("force"),
info_pubkey, info_pubkey,
}, },
require_keypair: true, signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}) })
} }
@ -244,7 +255,7 @@ pub fn parse_get_validator_info_command(
let info_pubkey = pubkey_of(matches, "info_pubkey"); let info_pubkey = pubkey_of(matches, "info_pubkey");
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::GetValidatorInfo(info_pubkey), command: CliCommand::GetValidatorInfo(info_pubkey),
require_keypair: false, signers: vec![],
}) })
} }
@ -257,7 +268,7 @@ pub fn process_set_validator_info(
) -> ProcessResult { ) -> ProcessResult {
// Validate keybase username // Validate keybase username
if let Some(string) = validator_info.get("keybaseUsername") { if let Some(string) = validator_info.get("keybaseUsername") {
let result = verify_keybase(&config.keypair.pubkey(), &string); let result = verify_keybase(&config.signers[0].pubkey(), &string);
if result.is_err() { if result.is_err() {
if force_keybase { if force_keybase {
println!("--force supplied, ignoring: {:?}", result); println!("--force supplied, ignoring: {:?}", result);
@ -282,7 +293,7 @@ pub fn process_set_validator_info(
}) })
.find(|(pubkey, account)| { .find(|(pubkey, account)| {
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap(); let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
validator_pubkey == config.keypair.pubkey() validator_pubkey == config.signers[0].pubkey()
}); });
// Create validator-info keypair to use if info_pubkey not provided or does not exist // Create validator-info keypair to use if info_pubkey not provided or does not exist
@ -300,7 +311,7 @@ pub fn process_set_validator_info(
.poll_get_balance_with_commitment(&info_pubkey, CommitmentConfig::default()) .poll_get_balance_with_commitment(&info_pubkey, CommitmentConfig::default())
.unwrap_or(0); .unwrap_or(0);
let keys = vec![(id(), false), (config.keypair.pubkey(), true)]; let keys = vec![(id(), false), (config.signers[0].pubkey(), true)];
let (message, signers): (Message, Vec<&dyn Signer>) = if balance == 0 { let (message, signers): (Message, Vec<&dyn Signer>) = if balance == 0 {
if info_pubkey != info_keypair.pubkey() { if info_pubkey != info_keypair.pubkey() {
println!( println!(
@ -311,12 +322,12 @@ pub fn process_set_validator_info(
} }
println!( println!(
"Publishing info for Validator {:?}", "Publishing info for Validator {:?}",
config.keypair.pubkey() config.signers[0].pubkey()
); );
let lamports = rpc_client let lamports = rpc_client
.get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?; .get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?;
let mut instructions = config_instruction::create_account::<ValidatorInfo>( let mut instructions = config_instruction::create_account::<ValidatorInfo>(
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&info_keypair.pubkey(), &info_keypair.pubkey(),
lamports, lamports,
keys.clone(), keys.clone(),
@ -327,13 +338,13 @@ pub fn process_set_validator_info(
keys, keys,
&validator_info, &validator_info,
)]); )]);
let signers = vec![config.keypair.as_ref(), &info_keypair]; let signers = vec![config.signers[0], &info_keypair];
let message = Message::new(instructions); let message = Message::new(instructions);
(message, signers) (message, signers)
} else { } else {
println!( println!(
"Updating Validator {:?} info at: {:?}", "Updating Validator {:?} info at: {:?}",
config.keypair.pubkey(), config.signers[0].pubkey(),
info_pubkey info_pubkey
); );
let instructions = vec![config_instruction::store( let instructions = vec![config_instruction::store(
@ -342,8 +353,8 @@ pub fn process_set_validator_info(
keys, keys,
&validator_info, &validator_info,
)]; )];
let message = Message::new_with_payer(instructions, Some(&config.keypair.pubkey())); let message = Message::new_with_payer(instructions, Some(&config.signers[0].pubkey()));
let signers = vec![config.keypair.as_ref()]; let signers = vec![config.signers[0]];
(message, signers) (message, signers)
}; };
@ -353,7 +364,7 @@ pub fn process_set_validator_info(
tx.try_sign(&signers, recent_blockhash)?; tx.try_sign(&signers, recent_blockhash)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;

View File

@ -1,16 +1,16 @@
use crate::cli::{ use crate::cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys, build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult, log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, CliSignerInfo,
ProcessResult,
}; };
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand}; use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{input_parsers::*, input_validators::*}; use solana_clap_utils::{input_parsers::*, input_validators::*};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
signature::Keypair,
signature::Signer,
system_instruction::{create_address_with_seed, SystemError}, system_instruction::{create_address_with_seed, SystemError},
transaction::Transaction, transaction::Transaction,
}; };
@ -18,6 +18,7 @@ use solana_vote_program::{
vote_instruction::{self, VoteError}, vote_instruction::{self, VoteError},
vote_state::{VoteAuthorize, VoteInit, VoteState}, vote_state::{VoteAuthorize, VoteInit, VoteState},
}; };
use std::sync::Arc;
pub trait VoteSubCommands { pub trait VoteSubCommands {
fn vote_subcommands(self) -> Self; fn vote_subcommands(self) -> Self;
@ -176,56 +177,88 @@ impl VoteSubCommands for App<'_, '_> {
} }
} }
pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_vote_create_account(
let vote_account = keypair_of(matches, "vote_account").unwrap(); matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (vote_account, _) = signer_of(matches, "vote_account", wallet_manager)?;
let seed = matches.value_of("seed").map(|s| s.to_string()); let seed = matches.value_of("seed").map(|s| s.to_string());
let identity_pubkey = pubkey_of(matches, "identity_pubkey").unwrap(); let identity_pubkey = pubkey_of(matches, "identity_pubkey").unwrap();
let commission = value_t_or_exit!(matches, "commission", u8); let commission = value_t_or_exit!(matches, "commission", u8);
let authorized_voter = pubkey_of(matches, "authorized_voter"); let authorized_voter = pubkey_of(matches, "authorized_voter");
let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer"); let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer");
let payer_provided = None;
let CliSignerInfo { signers } = generate_unique_signers(
vec![payer_provided, vote_account],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: vote_account.into(),
seed, seed,
node_pubkey: identity_pubkey, node_pubkey: identity_pubkey,
authorized_voter, authorized_voter,
authorized_withdrawer, authorized_withdrawer,
commission, commission,
}, },
require_keypair: true, signers,
}) })
} }
pub fn parse_vote_authorize( pub fn parse_vote_authorize(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap(); let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap();
let authorized_voter_provided = None;
let CliSignerInfo { signers } = generate_unique_signers(
vec![authorized_voter_provided],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey, vote_account_pubkey,
new_authorized_pubkey, new_authorized_pubkey,
vote_authorize, vote_authorize,
}, },
require_keypair: true, signers,
}) })
} }
pub fn parse_vote_update_validator(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_vote_update_validator(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let new_identity_pubkey = pubkey_of(matches, "new_identity_pubkey").unwrap(); let new_identity_pubkey = pubkey_of(matches, "new_identity_pubkey").unwrap();
let authorized_voter = keypair_of(matches, "authorized_voter").unwrap(); let (authorized_voter, _) = signer_of(matches, "authorized_voter", wallet_manager)?;
let payer_provided = None;
let CliSignerInfo { signers } = generate_unique_signers(
vec![payer_provided, authorized_voter],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::VoteUpdateValidator { command: CliCommand::VoteUpdateValidator {
vote_account_pubkey, vote_account_pubkey,
new_identity_pubkey, new_identity_pubkey,
authorized_voter: authorized_voter.into(),
}, },
require_keypair: true, signers,
}) })
} }
@ -239,20 +272,20 @@ pub fn parse_vote_get_account_command(
pubkey: vote_account_pubkey, pubkey: vote_account_pubkey,
use_lamports_unit, use_lamports_unit,
}, },
require_keypair: false, signers: vec![],
}) })
} }
pub fn process_create_vote_account( pub fn process_create_vote_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
vote_account: &Keypair,
seed: &Option<String>, seed: &Option<String>,
identity_pubkey: &Pubkey, identity_pubkey: &Pubkey,
authorized_voter: &Option<Pubkey>, authorized_voter: &Option<Pubkey>,
authorized_withdrawer: &Option<Pubkey>, authorized_withdrawer: &Option<Pubkey>,
commission: u8, commission: u8,
) -> ProcessResult { ) -> ProcessResult {
let vote_account = config.signers[1];
let vote_account_pubkey = vote_account.pubkey(); let vote_account_pubkey = vote_account.pubkey();
let vote_account_address = if let Some(seed) = seed { let vote_account_address = if let Some(seed) = seed {
create_address_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())? create_address_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
@ -260,7 +293,7 @@ pub fn process_create_vote_account(
vote_account_pubkey vote_account_pubkey
}; };
check_unique_pubkeys( check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()), (&config.signers[0].pubkey(), "cli keypair".to_string()),
(&vote_account_address, "vote_account".to_string()), (&vote_account_address, "vote_account".to_string()),
)?; )?;
@ -294,16 +327,16 @@ pub fn process_create_vote_account(
let ixs = if let Some(seed) = seed { let ixs = if let Some(seed) = seed {
vote_instruction::create_account_with_seed( vote_instruction::create_account_with_seed(
&config.keypair.pubkey(), // from &config.signers[0].pubkey(), // from
&vote_account_address, // to &vote_account_address, // to
&vote_account_pubkey, // base &vote_account_pubkey, // base
seed, // seed seed, // seed
&vote_init, &vote_init,
required_balance, required_balance,
) )
} else { } else {
vote_instruction::create_account( vote_instruction::create_account(
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&vote_account_pubkey, &vote_account_pubkey,
&vote_init, &vote_init,
required_balance, required_balance,
@ -311,22 +344,16 @@ pub fn process_create_vote_account(
}; };
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let signers = if vote_account_pubkey != config.keypair.pubkey() {
vec![config.keypair.as_ref(), vote_account] // both must sign if `from` and `to` differ
} else {
vec![config.keypair.as_ref()] // when stake_account == config.keypair and there's a seed, we only need one signature
};
let message = Message::new(ixs); let message = Message::new(ixs);
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &signers); let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result) log_instruction_custom_error::<SystemError>(result)
} }
@ -343,22 +370,22 @@ pub fn process_vote_authorize(
)?; )?;
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = vec![vote_instruction::authorize( let ixs = vec![vote_instruction::authorize(
vote_account_pubkey, // vote account to update vote_account_pubkey, // vote account to update
&config.keypair.pubkey(), // current authorized voter &config.signers[0].pubkey(), // current authorized voter
new_authorized_pubkey, // new vote signer/withdrawer new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw vote_authorize, // vote or withdraw
)]; )];
let message = Message::new_with_payer(ixs, Some(&config.keypair.pubkey())); let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&[config.keypair.as_ref()], recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref()]); let result = rpc_client.send_and_confirm_transaction(&mut tx, &[config.signers[0]]);
log_instruction_custom_error::<VoteError>(result) log_instruction_custom_error::<VoteError>(result)
} }
@ -367,8 +394,8 @@ pub fn process_vote_update_validator(
config: &CliConfig, config: &CliConfig,
vote_account_pubkey: &Pubkey, vote_account_pubkey: &Pubkey,
new_identity_pubkey: &Pubkey, new_identity_pubkey: &Pubkey,
authorized_voter: &Keypair,
) -> ProcessResult { ) -> ProcessResult {
let authorized_voter = config.signers[1];
check_unique_pubkeys( check_unique_pubkeys(
(vote_account_pubkey, "vote_account_pubkey".to_string()), (vote_account_pubkey, "vote_account_pubkey".to_string()),
(new_identity_pubkey, "new_identity_pubkey".to_string()), (new_identity_pubkey, "new_identity_pubkey".to_string()),
@ -380,19 +407,16 @@ pub fn process_vote_update_validator(
new_identity_pubkey, new_identity_pubkey,
)]; )];
let message = Message::new_with_payer(ixs, Some(&config.keypair.pubkey())); let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign( tx.try_sign(&config.signers, recent_blockhash)?;
&[config.keypair.as_ref(), authorized_voter],
recent_blockhash,
)?;
check_account_for_fee( check_account_for_fee(
rpc_client, rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
&fee_calculator, &fee_calculator,
&tx.message, &tx.message,
)?; )?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref()]); let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<VoteError>(result) log_instruction_custom_error::<VoteError>(result)
} }
@ -433,7 +457,7 @@ pub fn process_show_vote_account(
build_balance_message(vote_account.lamports, use_lamports_unit, true) build_balance_message(vote_account.lamports, use_lamports_unit, true)
); );
println!("Validator Identity: {}", vote_state.node_pubkey); println!("Validator Identity: {}", vote_state.node_pubkey);
println!("Authorized Voter: {}", vote_state.authorized_voter); println!("Authorized Voter: {:?}", vote_state.authorized_voters());
println!( println!(
"Authorized Withdrawer: {}", "Authorized Withdrawer: {}",
vote_state.authorized_withdrawer vote_state.authorized_withdrawer
@ -474,7 +498,7 @@ pub fn process_show_vote_account(
mod tests { mod tests {
use super::*; use super::*;
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_sdk::signature::write_keypair; use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) { fn make_tmp_file() -> (String, NamedTempFile) {
@ -492,6 +516,10 @@ mod tests {
let pubkey2 = keypair2.pubkey(); let pubkey2 = keypair2.pubkey();
let pubkey2_string = pubkey2.to_string(); let pubkey2_string = pubkey2.to_string();
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let test_authorize_voter = test_commands.clone().get_matches_from(vec![ let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test", "test",
"vote-authorize-voter", "vote-authorize-voter",
@ -499,14 +527,14 @@ mod tests {
&pubkey2_string, &pubkey2_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_voter).unwrap(), parse_command(&test_authorize_voter, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
new_authorized_pubkey: pubkey2, new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter vote_authorize: VoteAuthorize::Voter
}, },
require_keypair: true signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
} }
); );
@ -525,17 +553,19 @@ mod tests {
"10", "10",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account).unwrap(), parse_command(&test_create_vote_account, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None, seed: None,
node_pubkey, node_pubkey,
authorized_voter: None, authorized_voter: None,
authorized_withdrawer: None, authorized_withdrawer: None,
commission: 10, commission: 10,
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
} }
); );
@ -550,17 +580,19 @@ mod tests {
&node_pubkey_string, &node_pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account2).unwrap(), parse_command(&test_create_vote_account2, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None, seed: None,
node_pubkey, node_pubkey,
authorized_voter: None, authorized_voter: None,
authorized_withdrawer: None, authorized_withdrawer: None,
commission: 100, commission: 100,
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
} }
); );
@ -579,17 +611,19 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account3).unwrap(), parse_command(&test_create_vote_account3, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None, seed: None,
node_pubkey, node_pubkey,
authorized_voter: Some(authed), authorized_voter: Some(authed),
authorized_withdrawer: None, authorized_withdrawer: None,
commission: 100 commission: 100
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
} }
); );
@ -606,17 +640,19 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account4).unwrap(), parse_command(&test_create_vote_account4, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None, seed: None,
node_pubkey, node_pubkey,
authorized_voter: None, authorized_voter: None,
authorized_withdrawer: Some(authed), authorized_withdrawer: Some(authed),
commission: 100 commission: 100
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
} }
); );
@ -628,16 +664,16 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_update_validator).unwrap(), parse_command(&test_update_validator, &default_keypair_file, None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteUpdateValidator { command: CliCommand::VoteUpdateValidator {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
new_identity_pubkey: pubkey2, new_identity_pubkey: pubkey2,
authorized_voter: solana_sdk::signature::read_keypair_file(&keypair_file)
.unwrap()
.into(),
}, },
require_keypair: true signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(read_keypair_file(&keypair_file).unwrap())
],
} }
); );
} }

View File

@ -1,9 +1,9 @@
use serde_json::Value; use serde_json::Value;
use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::new_validator_for_tests; use solana_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{bpf_loader, pubkey::Pubkey}; use solana_sdk::{bpf_loader, pubkey::Pubkey, signature::Keypair};
use std::{ use std::{
fs::{remove_dir_all, File}, fs::{remove_dir_all, File},
io::Read, io::Read,
@ -22,7 +22,13 @@ fn test_cli_deploy_program() {
pathbuf.push("noop"); pathbuf.push("noop");
pathbuf.set_extension("so"); pathbuf.set_extension("so");
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(alice, sender, None); run_local_faucet(alice, sender, None);
@ -38,6 +44,7 @@ fn test_cli_deploy_program() {
.unwrap(); .unwrap();
let mut config = CliConfig::default(); let mut config = CliConfig::default();
let keypair = Keypair::new();
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config.command = CliCommand::Airdrop { config.command = CliCommand::Airdrop {
faucet_host: None, faucet_host: None,
@ -45,6 +52,7 @@ fn test_cli_deploy_program() {
pubkey: None, pubkey: None,
lamports: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing lamports: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing
}; };
config.signers = vec![&keypair];
process_command(&config).unwrap(); process_command(&config).unwrap();
config.command = CliCommand::Deploy(pathbuf.to_str().unwrap().to_string()); config.command = CliCommand::Deploy(pathbuf.to_str().unwrap().to_string());

View File

@ -1,28 +1,15 @@
use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}; use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
hash::Hash, hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
signature::{keypair_from_seed, read_keypair_file, write_keypair, Keypair, Signer}, signature::{keypair_from_seed, Keypair, Signer},
system_instruction::create_address_with_seed, system_instruction::create_address_with_seed,
system_program, system_program,
}; };
use std::fs::remove_dir_all; use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
use std::sync::mpsc::channel;
#[cfg(test)]
use solana_core::validator::new_validator_for_tests;
use std::rc::Rc;
use std::thread::sleep;
use std::time::Duration;
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) { fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| { (0..5).for_each(|tries| {
@ -39,34 +26,21 @@ fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
#[test] #[test]
fn test_nonce() { fn test_nonce() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(alice, sender, None); run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_payer = CliConfig::default(); full_battery_tests(&rpc_client, &faucet_addr, json_rpc_url, None, false);
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
let (keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
let mut config_nonce = CliConfig::default();
config_nonce.keypair = keypair.into();
config_nonce.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
full_battery_tests(
&rpc_client,
&faucet_addr,
&mut config_payer,
&mut config_nonce,
&keypair_file,
None,
None,
);
server.close().unwrap(); server.close().unwrap();
remove_dir_all(ledger_path).unwrap(); remove_dir_all(ledger_path).unwrap();
@ -74,33 +48,26 @@ fn test_nonce() {
#[test] #[test]
fn test_nonce_with_seed() { fn test_nonce_with_seed() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(alice, sender, None); run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_payer = CliConfig::default();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
let (keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
let mut config_nonce = CliConfig::default();
config_nonce.keypair = keypair.into();
config_nonce.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
full_battery_tests( full_battery_tests(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&mut config_payer, json_rpc_url,
&mut config_nonce,
&keypair_file,
Some(String::from("seed")), Some(String::from("seed")),
None, false,
); );
server.close().unwrap(); server.close().unwrap();
@ -109,84 +76,85 @@ fn test_nonce_with_seed() {
#[test] #[test]
fn test_nonce_with_authority() { fn test_nonce_with_authority() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(alice, sender, None); run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_payer = CliConfig::default(); full_battery_tests(&rpc_client, &faucet_addr, json_rpc_url, None, true);
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&nonce_keypair, tmp_file.as_file_mut()).unwrap();
let mut config_nonce = CliConfig::default();
config_nonce.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let nonce_authority = Keypair::new();
let (authority_keypair_file, mut tmp_file2) = make_tmp_file();
write_keypair(&nonce_authority, tmp_file2.as_file_mut()).unwrap();
full_battery_tests(
&rpc_client,
&faucet_addr,
&mut config_payer,
&mut config_nonce,
&nonce_keypair_file,
None,
Some(&authority_keypair_file),
);
server.close().unwrap(); server.close().unwrap();
remove_dir_all(ledger_path).unwrap(); remove_dir_all(ledger_path).unwrap();
} }
fn read_keypair_from_option(keypair_file: &Option<&str>) -> Option<Box<dyn Signer>> {
keypair_file.map(|akf| read_keypair_file(&akf).unwrap().into())
}
fn full_battery_tests( fn full_battery_tests(
rpc_client: &RpcClient, rpc_client: &RpcClient,
faucet_addr: &std::net::SocketAddr, faucet_addr: &std::net::SocketAddr,
config_payer: &mut CliConfig, json_rpc_url: String,
config_nonce: &mut CliConfig,
nonce_keypair_file: &str,
seed: Option<String>, seed: Option<String>,
authority_keypair_file: Option<&str>, use_nonce_authority: bool,
) { ) {
let mut config_payer = CliConfig::default();
config_payer.json_rpc_url = json_rpc_url.clone();
let payer = Keypair::new();
config_payer.signers = vec![&payer];
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_payer.keypair.pubkey(), &config_payer.signers[0].pubkey(),
2000, 2000,
) )
.unwrap(); .unwrap();
check_balance(2000, &rpc_client, &config_payer.keypair.pubkey()); check_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
let mut config_nonce = CliConfig::default();
config_nonce.json_rpc_url = json_rpc_url;
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
config_nonce.signers = vec![&nonce_keypair];
let nonce_account = if let Some(seed) = seed.as_ref() { let nonce_account = if let Some(seed) = seed.as_ref() {
create_address_with_seed(&config_nonce.keypair.pubkey(), seed, &system_program::id()) create_address_with_seed(
.unwrap() &config_nonce.signers[0].pubkey(),
seed,
&system_program::id(),
)
.unwrap()
} else { } else {
read_keypair_file(&nonce_keypair_file).unwrap().pubkey() nonce_keypair.pubkey()
};
let nonce_authority = Keypair::new();
let optional_authority = if use_nonce_authority {
Some(nonce_authority.pubkey())
} else {
None
}; };
// Create nonce account // Create nonce account
config_payer.signers.push(&nonce_keypair);
config_payer.command = CliCommand::CreateNonceAccount { config_payer.command = CliCommand::CreateNonceAccount {
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()), nonce_account: 1,
seed, seed,
nonce_authority: read_keypair_from_option(&authority_keypair_file).map(|k| k.pubkey()), nonce_authority: optional_authority,
lamports: 1000, lamports: 1000,
}; };
process_command(&config_payer).unwrap(); process_command(&config_payer).unwrap();
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey()); check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
check_balance(1000, &rpc_client, &nonce_account); check_balance(1000, &rpc_client, &nonce_account);
// Get nonce // Get nonce
config_payer.signers.pop();
config_payer.command = CliCommand::GetNonce(nonce_account); config_payer.command = CliCommand::GetNonce(nonce_account);
let first_nonce_string = process_command(&config_payer).unwrap(); let first_nonce_string = process_command(&config_payer).unwrap();
let first_nonce = first_nonce_string.parse::<Hash>().unwrap(); let first_nonce = first_nonce_string.parse::<Hash>().unwrap();
@ -198,14 +166,24 @@ fn full_battery_tests(
assert_eq!(first_nonce, second_nonce); assert_eq!(first_nonce, second_nonce);
let mut authorized_signers: Vec<&dyn Signer> = vec![&payer];
let index = if use_nonce_authority {
authorized_signers.push(&nonce_authority);
1
} else {
0
};
// New nonce // New nonce
config_payer.signers = authorized_signers.clone();
config_payer.command = CliCommand::NewNonce { config_payer.command = CliCommand::NewNonce {
nonce_account, nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file), nonce_authority: index,
}; };
process_command(&config_payer).unwrap(); process_command(&config_payer).unwrap();
// Get nonce // Get nonce
config_payer.signers = vec![&payer];
config_payer.command = CliCommand::GetNonce(nonce_account); config_payer.command = CliCommand::GetNonce(nonce_account);
let third_nonce_string = process_command(&config_payer).unwrap(); let third_nonce_string = process_command(&config_payer).unwrap();
let third_nonce = third_nonce_string.parse::<Hash>().unwrap(); let third_nonce = third_nonce_string.parse::<Hash>().unwrap();
@ -214,14 +192,15 @@ fn full_battery_tests(
// Withdraw from nonce account // Withdraw from nonce account
let payee_pubkey = Pubkey::new_rand(); let payee_pubkey = Pubkey::new_rand();
config_payer.signers = authorized_signers;
config_payer.command = CliCommand::WithdrawFromNonceAccount { config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account, nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file), nonce_authority: index,
destination_account_pubkey: payee_pubkey, destination_account_pubkey: payee_pubkey,
lamports: 100, lamports: 100,
}; };
process_command(&config_payer).unwrap(); process_command(&config_payer).unwrap();
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey()); check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
check_balance(900, &rpc_client, &nonce_account); check_balance(900, &rpc_client, &nonce_account);
check_balance(100, &rpc_client, &payee_pubkey); check_balance(100, &rpc_client, &payee_pubkey);
@ -234,48 +213,37 @@ fn full_battery_tests(
// Set new authority // Set new authority
let new_authority = Keypair::new(); let new_authority = Keypair::new();
let (new_authority_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&new_authority, tmp_file.as_file_mut()).unwrap();
config_payer.command = CliCommand::AuthorizeNonceAccount { config_payer.command = CliCommand::AuthorizeNonceAccount {
nonce_account, nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file), nonce_authority: index,
new_authority: read_keypair_file(&new_authority_keypair_file) new_authority: new_authority.pubkey(),
.unwrap()
.pubkey(),
}; };
process_command(&config_payer).unwrap(); process_command(&config_payer).unwrap();
// Old authority fails now // Old authority fails now
config_payer.command = CliCommand::NewNonce { config_payer.command = CliCommand::NewNonce {
nonce_account, nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file), nonce_authority: index,
}; };
process_command(&config_payer).unwrap_err(); process_command(&config_payer).unwrap_err();
// New authority can advance nonce // New authority can advance nonce
config_payer.signers = vec![&payer, &new_authority];
config_payer.command = CliCommand::NewNonce { config_payer.command = CliCommand::NewNonce {
nonce_account, nonce_account,
nonce_authority: Some( nonce_authority: 1,
read_keypair_file(&new_authority_keypair_file)
.unwrap()
.into(),
),
}; };
process_command(&config_payer).unwrap(); process_command(&config_payer).unwrap();
// New authority can withdraw from nonce account // New authority can withdraw from nonce account
config_payer.command = CliCommand::WithdrawFromNonceAccount { config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account, nonce_account,
nonce_authority: Some( nonce_authority: 1,
read_keypair_file(&new_authority_keypair_file)
.unwrap()
.into(),
),
destination_account_pubkey: payee_pubkey, destination_account_pubkey: payee_pubkey,
lamports: 100, lamports: 100,
}; };
process_command(&config_payer).unwrap(); process_command(&config_payer).unwrap();
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey()); check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
check_balance(800, &rpc_client, &nonce_account); check_balance(800, &rpc_client, &nonce_account);
check_balance(200, &rpc_client, &payee_pubkey); check_balance(200, &rpc_client, &payee_pubkey);
} }

View File

@ -6,28 +6,16 @@ use solana_cli::{
offline::{parse_sign_only_reply_string, BlockhashQuery}, offline::{parse_sign_only_reply_string, BlockhashQuery},
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
nonce_state::NonceState, nonce_state::NonceState,
pubkey::Pubkey, pubkey::Pubkey,
signature::{read_keypair_file, write_keypair, Keypair, Signer}, signature::{Keypair, Signer},
}; };
use std::fs::remove_dir_all; use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
use std::sync::mpsc::channel;
#[cfg(test)]
use solana_core::validator::new_validator_for_tests;
use std::rc::Rc;
use std::thread::sleep;
use std::time::Duration;
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) { fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| { (0..5).for_each(|tries| {
@ -44,7 +32,13 @@ fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
#[test] #[test]
fn test_cli_timestamp_tx() { fn test_cli_timestamp_tx() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
@ -52,32 +46,36 @@ fn test_cli_timestamp_tx() {
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer0 = Keypair::new();
let default_signer1 = Keypair::new();
let mut config_payer = CliConfig::default(); let mut config_payer = CliConfig::default();
config_payer.json_rpc_url = config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_payer.signers = vec![&default_signer0];
let mut config_witness = CliConfig::default(); let mut config_witness = CliConfig::default();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!( assert_ne!(
config_payer.keypair.pubkey(), config_payer.signers[0].pubkey(),
config_witness.keypair.pubkey() config_witness.signers[0].pubkey()
); );
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_payer.keypair.pubkey(), &config_payer.signers[0].pubkey(),
50, 50,
) )
.unwrap(); .unwrap();
check_balance(50, &rpc_client, &config_payer.keypair.pubkey()); check_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_witness.keypair.pubkey(), &config_witness.signers[0].pubkey(),
1, 1,
) )
.unwrap(); .unwrap();
@ -89,7 +87,7 @@ fn test_cli_timestamp_tx() {
lamports: 10, lamports: 10,
to: bob_pubkey, to: bob_pubkey,
timestamp: Some(dt), timestamp: Some(dt),
timestamp_pubkey: Some(config_witness.keypair.pubkey()), timestamp_pubkey: Some(config_witness.signers[0].pubkey()),
..PayCommand::default() ..PayCommand::default()
}); });
let sig_response = process_command(&config_payer); let sig_response = process_command(&config_payer);
@ -101,7 +99,7 @@ fn test_cli_timestamp_tx() {
.expect("base58-encoded public key"); .expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec); let process_id = Pubkey::new(&process_id_vec);
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(10, &rpc_client, &process_id); // contract balance check_balance(10, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -109,7 +107,7 @@ fn test_cli_timestamp_tx() {
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt); config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
process_command(&config_witness).unwrap(); process_command(&config_witness).unwrap();
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(0, &rpc_client, &process_id); // contract balance check_balance(0, &rpc_client, &process_id); // contract balance
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
@ -119,7 +117,13 @@ fn test_cli_timestamp_tx() {
#[test] #[test]
fn test_cli_witness_tx() { fn test_cli_witness_tx() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
@ -127,30 +131,34 @@ fn test_cli_witness_tx() {
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer0 = Keypair::new();
let default_signer1 = Keypair::new();
let mut config_payer = CliConfig::default(); let mut config_payer = CliConfig::default();
config_payer.json_rpc_url = config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_payer.signers = vec![&default_signer0];
let mut config_witness = CliConfig::default(); let mut config_witness = CliConfig::default();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!( assert_ne!(
config_payer.keypair.pubkey(), config_payer.signers[0].pubkey(),
config_witness.keypair.pubkey() config_witness.signers[0].pubkey()
); );
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_payer.keypair.pubkey(), &config_payer.signers[0].pubkey(),
50, 50,
) )
.unwrap(); .unwrap();
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_witness.keypair.pubkey(), &config_witness.signers[0].pubkey(),
1, 1,
) )
.unwrap(); .unwrap();
@ -159,7 +167,7 @@ fn test_cli_witness_tx() {
config_payer.command = CliCommand::Pay(PayCommand { config_payer.command = CliCommand::Pay(PayCommand {
lamports: 10, lamports: 10,
to: bob_pubkey, to: bob_pubkey,
witnesses: Some(vec![config_witness.keypair.pubkey()]), witnesses: Some(vec![config_witness.signers[0].pubkey()]),
..PayCommand::default() ..PayCommand::default()
}); });
let sig_response = process_command(&config_payer); let sig_response = process_command(&config_payer);
@ -171,7 +179,7 @@ fn test_cli_witness_tx() {
.expect("base58-encoded public key"); .expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec); let process_id = Pubkey::new(&process_id_vec);
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(10, &rpc_client, &process_id); // contract balance check_balance(10, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -179,7 +187,7 @@ fn test_cli_witness_tx() {
config_witness.command = CliCommand::Witness(bob_pubkey, process_id); config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
process_command(&config_witness).unwrap(); process_command(&config_witness).unwrap();
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(0, &rpc_client, &process_id); // contract balance check_balance(0, &rpc_client, &process_id); // contract balance
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
@ -189,7 +197,13 @@ fn test_cli_witness_tx() {
#[test] #[test]
fn test_cli_cancel_tx() { fn test_cli_cancel_tx() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
@ -197,23 +211,27 @@ fn test_cli_cancel_tx() {
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer0 = Keypair::new();
let default_signer1 = Keypair::new();
let mut config_payer = CliConfig::default(); let mut config_payer = CliConfig::default();
config_payer.json_rpc_url = config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_payer.signers = vec![&default_signer0];
let mut config_witness = CliConfig::default(); let mut config_witness = CliConfig::default();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!( assert_ne!(
config_payer.keypair.pubkey(), config_payer.signers[0].pubkey(),
config_witness.keypair.pubkey() config_witness.signers[0].pubkey()
); );
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_payer.keypair.pubkey(), &config_payer.signers[0].pubkey(),
50, 50,
) )
.unwrap(); .unwrap();
@ -222,7 +240,7 @@ fn test_cli_cancel_tx() {
config_payer.command = CliCommand::Pay(PayCommand { config_payer.command = CliCommand::Pay(PayCommand {
lamports: 10, lamports: 10,
to: bob_pubkey, to: bob_pubkey,
witnesses: Some(vec![config_witness.keypair.pubkey()]), witnesses: Some(vec![config_witness.signers[0].pubkey()]),
cancelable: true, cancelable: true,
..PayCommand::default() ..PayCommand::default()
}); });
@ -235,7 +253,7 @@ fn test_cli_cancel_tx() {
.expect("base58-encoded public key"); .expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec); let process_id = Pubkey::new(&process_id_vec);
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(10, &rpc_client, &process_id); // contract balance check_balance(10, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -243,7 +261,7 @@ fn test_cli_cancel_tx() {
config_payer.command = CliCommand::Cancel(process_id); config_payer.command = CliCommand::Cancel(process_id);
process_command(&config_payer).unwrap(); process_command(&config_payer).unwrap();
check_balance(50, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance check_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(0, &rpc_client, &process_id); // contract balance check_balance(0, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -253,7 +271,13 @@ fn test_cli_cancel_tx() {
#[test] #[test]
fn test_offline_pay_tx() { fn test_offline_pay_tx() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
@ -261,22 +285,26 @@ fn test_offline_pay_tx() {
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer = Keypair::new();
let default_offline_signer = Keypair::new();
let mut config_offline = CliConfig::default(); let mut config_offline = CliConfig::default();
config_offline.json_rpc_url = config_offline.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_offline.signers = vec![&default_offline_signer];
let mut config_online = CliConfig::default(); let mut config_online = CliConfig::default();
config_online.json_rpc_url = config_online.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_online.signers = vec![&default_signer];
assert_ne!( assert_ne!(
config_offline.keypair.pubkey(), config_offline.signers[0].pubkey(),
config_online.keypair.pubkey() config_online.signers[0].pubkey()
); );
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_offline.keypair.pubkey(), &config_offline.signers[0].pubkey(),
50, 50,
) )
.unwrap(); .unwrap();
@ -284,12 +312,12 @@ fn test_offline_pay_tx() {
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config_online.keypair.pubkey(), &config_online.signers[0].pubkey(),
50, 50,
) )
.unwrap(); .unwrap();
check_balance(50, &rpc_client, &config_offline.keypair.pubkey()); check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
check_balance(50, &rpc_client, &config_online.keypair.pubkey()); check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
config_offline.command = CliCommand::Pay(PayCommand { config_offline.command = CliCommand::Pay(PayCommand {
@ -301,15 +329,15 @@ fn test_offline_pay_tx() {
}); });
let sig_response = process_command(&config_offline).unwrap(); let sig_response = process_command(&config_offline).unwrap();
check_balance(50, &rpc_client, &config_offline.keypair.pubkey()); check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
check_balance(50, &rpc_client, &config_online.keypair.pubkey()); check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
check_balance(0, &rpc_client, &bob_pubkey); check_balance(0, &rpc_client, &bob_pubkey);
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response); let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner = let offline_presigner =
presigner_from_pubkey_sigs(&config_offline.keypair.pubkey(), &signers).unwrap(); presigner_from_pubkey_sigs(&config_offline.signers[0].pubkey(), &signers).unwrap();
let online_pubkey = config_online.keypair.pubkey(); let online_pubkey = config_online.signers[0].pubkey();
config_online.keypair = offline_presigner.into(); config_online.signers = vec![&offline_presigner];
config_online.command = CliCommand::Pay(PayCommand { config_online.command = CliCommand::Pay(PayCommand {
lamports: 10, lamports: 10,
to: bob_pubkey, to: bob_pubkey,
@ -318,7 +346,7 @@ fn test_offline_pay_tx() {
}); });
process_command(&config_online).unwrap(); process_command(&config_online).unwrap();
check_balance(40, &rpc_client, &config_offline.keypair.pubkey()); check_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
check_balance(50, &rpc_client, &online_pubkey); check_balance(50, &rpc_client, &online_pubkey);
check_balance(10, &rpc_client, &bob_pubkey); check_balance(10, &rpc_client, &bob_pubkey);
@ -330,15 +358,23 @@ fn test_offline_pay_tx() {
fn test_nonced_pay_tx() { fn test_nonced_pay_tx() {
solana_logger::setup(); solana_logger::setup();
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(alice, sender, None); run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer = Keypair::new();
let mut config = CliConfig::default(); let mut config = CliConfig::default();
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config.signers = vec![&default_signer];
let minimum_nonce_balance = rpc_client let minimum_nonce_balance = rpc_client
.get_minimum_balance_for_rent_exemption(NonceState::size()) .get_minimum_balance_for_rent_exemption(NonceState::size())
@ -347,29 +383,28 @@ fn test_nonced_pay_tx() {
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
50 + minimum_nonce_balance, 50 + minimum_nonce_balance,
) )
.unwrap(); .unwrap();
check_balance( check_balance(
50 + minimum_nonce_balance, 50 + minimum_nonce_balance,
&rpc_client, &rpc_client,
&config.keypair.pubkey(), &config.signers[0].pubkey(),
); );
// Create nonce account // Create nonce account
let nonce_account = Keypair::new(); let nonce_account = Keypair::new();
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
config.command = CliCommand::CreateNonceAccount { config.command = CliCommand::CreateNonceAccount {
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()), nonce_account: 1,
seed: None, seed: None,
nonce_authority: Some(config.keypair.pubkey()), nonce_authority: Some(config.signers[0].pubkey()),
lamports: minimum_nonce_balance, lamports: minimum_nonce_balance,
}; };
config.signers.push(&nonce_account);
process_command(&config).unwrap(); process_command(&config).unwrap();
check_balance(50, &rpc_client, &config.keypair.pubkey()); check_balance(50, &rpc_client, &config.signers[0].pubkey());
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey()); check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash // Fetch nonce hash
@ -381,6 +416,7 @@ fn test_nonced_pay_tx() {
}; };
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
config.signers = vec![&default_signer];
config.command = CliCommand::Pay(PayCommand { config.command = CliCommand::Pay(PayCommand {
lamports: 10, lamports: 10,
to: bob_pubkey, to: bob_pubkey,
@ -390,7 +426,7 @@ fn test_nonced_pay_tx() {
}); });
process_command(&config).expect("failed to process pay command"); process_command(&config).expect("failed to process pay command");
check_balance(40, &rpc_client, &config.keypair.pubkey()); check_balance(40, &rpc_client, &config.signers[0].pubkey());
check_balance(10, &rpc_client, &bob_pubkey); check_balance(10, &rpc_client, &bob_pubkey);
// Verify that nonce has been used // Verify that nonce has been used

View File

@ -1,13 +1,19 @@
use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::new_validator_for_tests; use solana_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use std::fs::remove_dir_all; use solana_sdk::signature::Keypair;
use std::sync::mpsc::channel; use std::{fs::remove_dir_all, sync::mpsc::channel};
#[test] #[test]
fn test_cli_request_airdrop() { fn test_cli_request_airdrop() {
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(alice, sender, None); run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
@ -20,6 +26,8 @@ fn test_cli_request_airdrop() {
pubkey: None, pubkey: None,
lamports: 50, lamports: 50,
}; };
let keypair = Keypair::new();
bob_config.signers = vec![&keypair];
let sig_response = process_command(&bob_config); let sig_response = process_command(&bob_config);
sig_response.unwrap(); sig_response.unwrap();
@ -27,7 +35,7 @@ fn test_cli_request_airdrop() {
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let balance = rpc_client let balance = rpc_client
.retry_get_balance(&bob_config.keypair.pubkey(), 1) .retry_get_balance(&bob_config.signers[0].pubkey(), 1)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(balance, 50); assert_eq!(balance, 50);

File diff suppressed because it is too large Load Diff

View File

@ -4,28 +4,16 @@ use solana_cli::{
offline::{parse_sign_only_reply_string, BlockhashQuery}, offline::{parse_sign_only_reply_string, BlockhashQuery},
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
nonce_state::NonceState, nonce_state::NonceState,
pubkey::Pubkey, pubkey::Pubkey,
signature::{keypair_from_seed, read_keypair_file, write_keypair, Signer}, signature::{keypair_from_seed, Keypair, Signer},
}; };
use std::fs::remove_dir_all; use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
use std::sync::mpsc::channel;
#[cfg(test)]
use solana_core::validator::new_validator_for_tests_ex;
use std::rc::Rc;
use std::thread::sleep;
use std::time::Duration;
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) { fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| { (0..5).for_each(|tries| {
@ -42,7 +30,16 @@ fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
#[test] #[test]
fn test_transfer() { fn test_transfer() {
let (server, leader_data, mint_keypair, ledger_path, _) = new_validator_for_tests_ex(1, 42_000); let TestValidator {
server,
leader_data,
alice: mint_keypair,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
});
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None); run_local_faucet(mint_keypair, sender, None);
@ -50,13 +47,15 @@ fn test_transfer() {
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer = Keypair::new();
let default_offline_signer = Keypair::new();
let mut config = CliConfig::default(); let mut config = CliConfig::default();
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config.signers = vec![&default_signer];
let sender_pubkey = config.keypair.pubkey(); let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]); let recipient_pubkey = Pubkey::new(&[1u8; 32]);
println!("sender: {:?}", sender_pubkey);
println!("recipient: {:?}", recipient_pubkey);
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000).unwrap(); request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000).unwrap();
check_balance(50_000, &rpc_client, &sender_pubkey); check_balance(50_000, &rpc_client, &sender_pubkey);
@ -66,12 +65,12 @@ fn test_transfer() {
config.command = CliCommand::Transfer { config.command = CliCommand::Transfer {
lamports: 10, lamports: 10,
to: recipient_pubkey, to: recipient_pubkey,
from: None, from: 0,
sign_only: false, sign_only: false,
blockhash_query: BlockhashQuery::All, blockhash_query: BlockhashQuery::All,
nonce_account: None, nonce_account: None,
nonce_authority: None, nonce_authority: 0,
fee_payer: None, fee_payer: 0,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
check_balance(49_989, &rpc_client, &sender_pubkey); check_balance(49_989, &rpc_client, &sender_pubkey);
@ -79,12 +78,12 @@ fn test_transfer() {
let mut offline = CliConfig::default(); let mut offline = CliConfig::default();
offline.json_rpc_url = String::default(); offline.json_rpc_url = String::default();
offline.signers = vec![&default_offline_signer];
// Verify we cannot contact the cluster // Verify we cannot contact the cluster
offline.command = CliCommand::ClusterVersion; offline.command = CliCommand::ClusterVersion;
process_command(&offline).unwrap_err(); process_command(&offline).unwrap_err();
let offline_pubkey = offline.keypair.pubkey(); let offline_pubkey = offline.signers[0].pubkey();
println!("offline: {:?}", offline_pubkey);
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50).unwrap(); request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50).unwrap();
check_balance(50, &rpc_client, &offline_pubkey); check_balance(50, &rpc_client, &offline_pubkey);
@ -93,25 +92,26 @@ fn test_transfer() {
offline.command = CliCommand::Transfer { offline.command = CliCommand::Transfer {
lamports: 10, lamports: 10,
to: recipient_pubkey, to: recipient_pubkey,
from: None, from: 0,
sign_only: true, sign_only: true,
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()), blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
nonce_account: None, nonce_account: None,
nonce_authority: None, nonce_authority: 0,
fee_payer: None, fee_payer: 0,
}; };
let sign_only_reply = process_command(&offline).unwrap(); let sign_only_reply = process_command(&offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply); let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap(); let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::Transfer { config.command = CliCommand::Transfer {
lamports: 10, lamports: 10,
to: recipient_pubkey, to: recipient_pubkey,
from: Some(offline_presigner.clone().into()), from: 0,
sign_only: false, sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash), blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
nonce_account: None, nonce_account: None,
nonce_authority: None, nonce_authority: 0,
fee_payer: Some(offline_presigner.clone().into()), fee_payer: 0,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
check_balance(39, &rpc_client, &offline_pubkey); check_balance(39, &rpc_client, &offline_pubkey);
@ -119,13 +119,12 @@ fn test_transfer() {
// Create nonce account // Create nonce account
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap(); let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
let (nonce_account_file, mut tmp_file) = make_tmp_file();
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
let minimum_nonce_balance = rpc_client let minimum_nonce_balance = rpc_client
.get_minimum_balance_for_rent_exemption(NonceState::size()) .get_minimum_balance_for_rent_exemption(NonceState::size())
.unwrap(); .unwrap();
config.signers = vec![&default_signer, &nonce_account];
config.command = CliCommand::CreateNonceAccount { config.command = CliCommand::CreateNonceAccount {
nonce_account: Rc::new(read_keypair_file(&nonce_account_file).unwrap().into()), nonce_account: 1,
seed: None, seed: None,
nonce_authority: None, nonce_authority: None,
lamports: minimum_nonce_balance, lamports: minimum_nonce_balance,
@ -142,15 +141,16 @@ fn test_transfer() {
}; };
// Nonced transfer // Nonced transfer
config.signers = vec![&default_signer];
config.command = CliCommand::Transfer { config.command = CliCommand::Transfer {
lamports: 10, lamports: 10,
to: recipient_pubkey, to: recipient_pubkey,
from: None, from: 0,
sign_only: false, sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash), blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
nonce_account: Some(nonce_account.pubkey()), nonce_account: Some(nonce_account.pubkey()),
nonce_authority: None, nonce_authority: 0,
fee_payer: None, fee_payer: 0,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey); check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
@ -164,9 +164,10 @@ fn test_transfer() {
assert_ne!(nonce_hash, new_nonce_hash); assert_ne!(nonce_hash, new_nonce_hash);
// Assign nonce authority to offline // Assign nonce authority to offline
config.signers = vec![&default_signer];
config.command = CliCommand::AuthorizeNonceAccount { config.command = CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
nonce_authority: None, nonce_authority: 0,
new_authority: offline_pubkey, new_authority: offline_pubkey,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
@ -181,28 +182,30 @@ fn test_transfer() {
}; };
// Offline, nonced transfer // Offline, nonced transfer
offline.signers = vec![&default_offline_signer];
offline.command = CliCommand::Transfer { offline.command = CliCommand::Transfer {
lamports: 10, lamports: 10,
to: recipient_pubkey, to: recipient_pubkey,
from: None, from: 0,
sign_only: true, sign_only: true,
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()), blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
nonce_account: Some(nonce_account.pubkey()), nonce_account: Some(nonce_account.pubkey()),
nonce_authority: None, nonce_authority: 0,
fee_payer: None, fee_payer: 0,
}; };
let sign_only_reply = process_command(&offline).unwrap(); let sign_only_reply = process_command(&offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply); let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap(); let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::Transfer { config.command = CliCommand::Transfer {
lamports: 10, lamports: 10,
to: recipient_pubkey, to: recipient_pubkey,
from: Some(offline_presigner.clone().into()), from: 0,
sign_only: false, sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash), blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
nonce_account: Some(nonce_account.pubkey()), nonce_account: Some(nonce_account.pubkey()),
nonce_authority: Some(offline_presigner.clone().into()), nonce_authority: 0,
fee_payer: Some(offline_presigner.clone().into()), fee_payer: 0,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
check_balance(28, &rpc_client, &offline_pubkey); check_balance(28, &rpc_client, &offline_pubkey);

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-client" name = "solana-client"
version = "1.0.0" version = "1.0.1"
description = "Solana Client" description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@ -18,8 +18,8 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
serde = "1.0.104" serde = "1.0.104"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.46" serde_json = "1.0.46"
solana-net-utils = { path = "../net-utils", version = "1.0.0" } solana-net-utils = { path = "../net-utils", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
thiserror = "1.0" thiserror = "1.0"
tungstenite = "0.10.1" tungstenite = "0.10.1"
url = "2.1.1" url = "2.1.1"
@ -28,4 +28,4 @@ url = "2.1.1"
assert_matches = "1.3.0" assert_matches = "1.3.0"
jsonrpc-core = "14.0.5" jsonrpc-core = "14.0.5"
jsonrpc-http-server = "14.0.6" jsonrpc-http-server = "14.0.6"
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "solana-core" name = "solana-core"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.0.0" version = "1.0.1"
documentation = "https://docs.rs/solana" documentation = "https://docs.rs/solana"
homepage = "https://solana.com/" homepage = "https://solana.com/"
readme = "../README.md" readme = "../README.md"
@ -25,6 +25,7 @@ fs_extra = "1.1.0"
indexmap = "1.3" indexmap = "1.3"
itertools = "0.8.2" itertools = "0.8.2"
jsonrpc-core = "14.0.5" jsonrpc-core = "14.0.5"
jsonrpc-core-client = { version = "14.0.5", features = ["ws"] }
jsonrpc-derive = "14.0.5" jsonrpc-derive = "14.0.5"
jsonrpc-http-server = "14.0.6" jsonrpc-http-server = "14.0.6"
jsonrpc-pubsub = "14.0.6" jsonrpc-pubsub = "14.0.6"
@ -36,29 +37,30 @@ num-traits = "0.2"
rand = "0.6.5" rand = "0.6.5"
rand_chacha = "0.1.1" rand_chacha = "0.1.1"
rayon = "1.2.0" rayon = "1.2.0"
regex = "1.3.4"
serde = "1.0.104" serde = "1.0.104"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.46" serde_json = "1.0.46"
solana-budget-program = { path = "../programs/budget", version = "1.0.0" } solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.0" } solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
solana-client = { path = "../client", version = "1.0.0" } solana-client = { path = "../client", version = "1.0.1" }
solana-faucet = { path = "../faucet", version = "1.0.0" } solana-faucet = { path = "../faucet", version = "1.0.1" }
ed25519-dalek = "=1.0.0-pre.1" ed25519-dalek = "=1.0.0-pre.1"
solana-ledger = { path = "../ledger", version = "1.0.0" } solana-ledger = { path = "../ledger", version = "1.0.1" }
solana-logger = { path = "../logger", version = "1.0.0" } solana-logger = { path = "../logger", version = "1.0.1" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.0" } solana-merkle-tree = { path = "../merkle-tree", version = "1.0.1" }
solana-metrics = { path = "../metrics", version = "1.0.0" } solana-metrics = { path = "../metrics", version = "1.0.1" }
solana-measure = { path = "../measure", version = "1.0.0" } solana-measure = { path = "../measure", version = "1.0.1" }
solana-net-utils = { path = "../net-utils", version = "1.0.0" } solana-net-utils = { path = "../net-utils", version = "1.0.1" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.0.0" } solana-chacha-cuda = { path = "../chacha-cuda", version = "1.0.1" }
solana-perf = { path = "../perf", version = "1.0.0" } solana-perf = { path = "../perf", version = "1.0.1" }
solana-runtime = { path = "../runtime", version = "1.0.0" } solana-runtime = { path = "../runtime", version = "1.0.1" }
solana-sdk = { path = "../sdk", version = "1.0.0" } solana-sdk = { path = "../sdk", version = "1.0.1" }
solana-stake-program = { path = "../programs/stake", version = "1.0.0" } solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
solana-storage-program = { path = "../programs/storage", version = "1.0.0" } solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
solana-vote-program = { path = "../programs/vote", version = "1.0.0" } solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.0" } solana-vote-signer = { path = "../vote-signer", version = "1.0.1" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.0.0" } solana-sys-tuner = { path = "../sys-tuner", version = "1.0.1" }
sys-info = "0.5.9" sys-info = "0.5.9"
tempfile = "3.1.0" tempfile = "3.1.0"
thiserror = "1.0" thiserror = "1.0"
@ -66,7 +68,7 @@ tokio = "0.1"
tokio-codec = "0.1" tokio-codec = "0.1"
tokio-fs = "0.1" tokio-fs = "0.1"
tokio-io = "0.1" tokio-io = "0.1"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.0" } solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.1" }
trees = "0.2.1" trees = "0.2.1"
[dev-dependencies] [dev-dependencies]

View File

@ -84,6 +84,9 @@ const MAX_GOSSIP_TRAFFIC: usize = 128_000_000 / PACKET_DATA_SIZE;
const NUM_BITS_PER_BYTE: u64 = 8; const NUM_BITS_PER_BYTE: u64 = 8;
const MIN_SIZE_TO_COMPRESS_GZIP: u64 = 64; const MIN_SIZE_TO_COMPRESS_GZIP: u64 = 64;
/// Keep the number of snapshot hashes a node publishes under MAX_PROTOCOL_PAYLOAD_SIZE
pub const MAX_SNAPSHOT_HASHES: usize = 16;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum ClusterInfoError { pub enum ClusterInfoError {
NoPeers, NoPeers,
@ -441,6 +444,14 @@ impl ClusterInfo {
} }
pub fn push_snapshot_hashes(&mut self, snapshot_hashes: Vec<(Slot, Hash)>) { pub fn push_snapshot_hashes(&mut self, snapshot_hashes: Vec<(Slot, Hash)>) {
if snapshot_hashes.len() > MAX_SNAPSHOT_HASHES {
warn!(
"snapshot_hashes too large, ignored: {}",
snapshot_hashes.len()
);
return;
}
let now = timestamp(); let now = timestamp();
let entry = CrdsValue::new_signed( let entry = CrdsValue::new_signed(
CrdsData::SnapshotHash(SnapshotHash::new(self.id(), snapshot_hashes, now)), CrdsData::SnapshotHash(SnapshotHash::new(self.id(), snapshot_hashes, now)),
@ -1059,7 +1070,7 @@ impl ClusterInfo {
} }
/// Splits a Vec of CrdsValues into a nested Vec, trying to make sure that /// Splits a Vec of CrdsValues into a nested Vec, trying to make sure that
/// each Vec is no larger than `PROTOCOL_PAYLOAD_SIZE` /// each Vec is no larger than `MAX_PROTOCOL_PAYLOAD_SIZE`
/// Note: some messages cannot be contained within that size so in the worst case this returns /// Note: some messages cannot be contained within that size so in the worst case this returns
/// N nested Vecs with 1 item each. /// N nested Vecs with 1 item each.
fn split_gossip_messages(msgs: Vec<CrdsValue>) -> Vec<Vec<CrdsValue>> { fn split_gossip_messages(msgs: Vec<CrdsValue>) -> Vec<Vec<CrdsValue>> {

View File

@ -233,7 +233,7 @@ mod tests {
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo}; use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_stake_program::stake_state; use solana_stake_program::stake_state;
use solana_vote_program::vote_state; use solana_vote_program::vote_state::{self, VoteStateVersions};
#[test] #[test]
fn test_block_commitment() { fn test_block_commitment() {
@ -446,13 +446,15 @@ mod tests {
let mut vote_state1 = VoteState::from(&vote_account1).unwrap(); let mut vote_state1 = VoteState::from(&vote_account1).unwrap();
vote_state1.process_slot_vote_unchecked(3); vote_state1.process_slot_vote_unchecked(3);
vote_state1.process_slot_vote_unchecked(5); vote_state1.process_slot_vote_unchecked(5);
vote_state1.to(&mut vote_account1).unwrap(); let versioned = VoteStateVersions::Current(Box::new(vote_state1));
VoteState::to(&versioned, &mut vote_account1).unwrap();
bank.store_account(&pk1, &vote_account1); bank.store_account(&pk1, &vote_account1);
let mut vote_state2 = VoteState::from(&vote_account2).unwrap(); let mut vote_state2 = VoteState::from(&vote_account2).unwrap();
vote_state2.process_slot_vote_unchecked(9); vote_state2.process_slot_vote_unchecked(9);
vote_state2.process_slot_vote_unchecked(10); vote_state2.process_slot_vote_unchecked(10);
vote_state2.to(&mut vote_account2).unwrap(); let versioned = VoteStateVersions::Current(Box::new(vote_state2));
VoteState::to(&versioned, &mut vote_account2).unwrap();
bank.store_account(&pk2, &vote_account2); bank.store_account(&pk2, &vote_account2);
let commitment = AggregateCommitmentService::aggregate_commitment(&ancestors, &bank); let commitment = AggregateCommitmentService::aggregate_commitment(&ancestors, &bank);

View File

@ -483,7 +483,10 @@ pub mod test {
signature::{Keypair, Signer}, signature::{Keypair, Signer},
transaction::Transaction, transaction::Transaction,
}; };
use solana_vote_program::{vote_instruction, vote_state::Vote}; use solana_vote_program::{
vote_instruction,
vote_state::{Vote, VoteStateVersions},
};
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use std::sync::RwLock; use std::sync::RwLock;
use std::{thread::sleep, time::Duration}; use std::{thread::sleep, time::Duration};
@ -510,7 +513,7 @@ pub mod test {
my_keypairs: &ValidatorVoteKeypairs, my_keypairs: &ValidatorVoteKeypairs,
progress: &mut HashMap<u64, ForkProgress>, progress: &mut HashMap<u64, ForkProgress>,
tower: &mut Tower, tower: &mut Tower,
) -> VoteResult { ) -> Vec<VoteFailures> {
let node = self let node = self
.find_node_and_update_simulation(vote_slot) .find_node_and_update_simulation(vote_slot)
.expect("Vote to simulate must be for a slot in the tree"); .expect("Vote to simulate must be for a slot in the tree");
@ -585,6 +588,7 @@ pub mod test {
.values() .values()
.cloned() .cloned()
.collect(); .collect();
ReplayStage::compute_bank_stats( ReplayStage::compute_bank_stats(
&my_pubkey, &my_pubkey,
&ancestors, &ancestors,
@ -592,7 +596,6 @@ pub mod test {
tower, tower,
progress, progress,
); );
ReplayStage::select_fork(&frozen_banks, tower, progress);
let bank = bank_forks let bank = bank_forks
.read() .read()
@ -606,12 +609,15 @@ pub mod test {
.expect("Slot for vote must exist in progress map"); .expect("Slot for vote must exist in progress map");
info!("Checking vote: {}", vote_slot); info!("Checking vote: {}", vote_slot);
info!("lockouts: {:?}", fork_progress.fork_stats.stake_lockouts); info!("lockouts: {:?}", fork_progress.fork_stats.stake_lockouts);
if fork_progress.fork_stats.is_locked_out && !fork_progress.fork_stats.vote_threshold { let mut failures = vec![];
return VoteResult::FailedAllChecks(vote_slot); if fork_progress.fork_stats.is_locked_out {
} else if fork_progress.fork_stats.is_locked_out { failures.push(VoteFailures::LockedOut(vote_slot));
return VoteResult::LockedOut(vote_slot); }
} else if !fork_progress.fork_stats.vote_threshold { if !fork_progress.fork_stats.vote_threshold {
return VoteResult::FailedThreshold(vote_slot); failures.push(VoteFailures::FailedThreshold(vote_slot));
}
if !failures.is_empty() {
return failures;
} }
let vote = tower.new_vote_from_bank(&bank, &my_vote_pubkey).0; let vote = tower.new_vote_from_bank(&bank, &my_vote_pubkey).0;
if let Some(new_root) = tower.record_bank_vote(vote) { if let Some(new_root) = tower.record_bank_vote(vote) {
@ -621,7 +627,7 @@ pub mod test {
// Mark the vote for this bank under this node's pubkey so it will be // Mark the vote for this bank under this node's pubkey so it will be
// integrated into any future child banks // integrated into any future child banks
cluster_votes.entry(my_pubkey).or_default().push(vote_slot); cluster_votes.entry(my_pubkey).or_default().push(vote_slot);
VoteResult::Ok vec![]
} }
// Find a node representing the given slot // Find a node representing the given slot
@ -666,11 +672,9 @@ pub mod test {
} }
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub(crate) enum VoteResult { pub(crate) enum VoteFailures {
LockedOut(u64), LockedOut(u64),
FailedThreshold(u64), FailedThreshold(u64),
FailedAllChecks(u64),
Ok,
} }
// Setup BankForks with bank 0 and all the validator accounts // Setup BankForks with bank 0 and all the validator accounts
@ -706,9 +710,11 @@ pub mod test {
for slot in *votes { for slot in *votes {
vote_state.process_slot_vote_unchecked(*slot); vote_state.process_slot_vote_unchecked(*slot);
} }
vote_state VoteState::serialize(
.serialize(&mut account.data) &VoteStateVersions::Current(Box::new(vote_state)),
.expect("serialize state"); &mut account.data,
)
.expect("serialize state");
stakes.push((Pubkey::new_rand(), (*lamports, account))); stakes.push((Pubkey::new_rand(), (*lamports, account)));
} }
stakes stakes
@ -782,9 +788,8 @@ pub mod test {
let mut cluster_votes = HashMap::new(); let mut cluster_votes = HashMap::new();
for vote in votes { for vote in votes {
assert_eq!( assert!(voting_simulator
VoteResult::Ok, .simulate_vote(
voting_simulator.simulate_vote(
vote, vote,
&bank_forks, &bank_forks,
&mut cluster_votes, &mut cluster_votes,
@ -793,7 +798,7 @@ pub mod test {
&mut progress, &mut progress,
&mut tower, &mut tower,
) )
); .is_empty());
} }
for i in 0..5 { for i in 0..5 {
@ -856,8 +861,8 @@ pub mod test {
let mut tower = Tower::new_with_key(&node_pubkey); let mut tower = Tower::new_with_key(&node_pubkey);
for vote in &votes { for vote in &votes {
// All these votes should be ok // All these votes should be ok
assert_eq!( assert!(voting_simulator
voting_simulator.simulate_vote( .simulate_vote(
*vote, *vote,
&bank_forks, &bank_forks,
&mut cluster_votes, &mut cluster_votes,
@ -865,15 +870,14 @@ pub mod test {
keypairs.get(&node_pubkey).unwrap(), keypairs.get(&node_pubkey).unwrap(),
&mut progress, &mut progress,
&mut tower, &mut tower,
), )
VoteResult::Ok .is_empty());
);
} }
// Try to come back to main fork // Try to come back to main fork
let next_unlocked_slot = 110; let next_unlocked_slot = 110;
assert_eq!( assert!(voting_simulator
voting_simulator.simulate_vote( .simulate_vote(
next_unlocked_slot, next_unlocked_slot,
&bank_forks, &bank_forks,
&mut cluster_votes, &mut cluster_votes,
@ -881,9 +885,8 @@ pub mod test {
keypairs.get(&node_pubkey).unwrap(), keypairs.get(&node_pubkey).unwrap(),
&mut progress, &mut progress,
&mut tower, &mut tower,
), )
VoteResult::Ok .is_empty());
);
info!("local tower: {:#?}", tower.lockouts.votes); info!("local tower: {:#?}", tower.lockouts.votes);
let vote_accounts = bank_forks let vote_accounts = bank_forks

View File

@ -28,8 +28,8 @@ use std::sync::{Arc, Mutex};
use std::time::Instant; use std::time::Instant;
use thiserror::Error; use thiserror::Error;
const GRACE_TICKS_FACTOR: u64 = 2; pub const GRACE_TICKS_FACTOR: u64 = 2;
const MAX_GRACE_SLOTS: u64 = 2; pub const MAX_GRACE_SLOTS: u64 = 2;
#[derive(Error, Debug, Clone)] #[derive(Error, Debug, Clone)]
pub enum PohRecorderError { pub enum PohRecorderError {
@ -85,6 +85,7 @@ impl PohRecorder {
bank.slot(), bank.slot(),
&bank, &bank,
Some(&self.blockstore), Some(&self.blockstore),
GRACE_TICKS_FACTOR * MAX_GRACE_SLOTS,
); );
assert_eq!(self.ticks_per_slot, bank.ticks_per_slot()); assert_eq!(self.ticks_per_slot, bank.ticks_per_slot());
let (leader_first_tick_height, leader_last_tick_height, grace_ticks) = let (leader_first_tick_height, leader_last_tick_height, grace_ticks) =
@ -151,6 +152,17 @@ impl PohRecorder {
}) })
} }
fn prev_slot_was_mine(&self, current_slot: Slot) -> bool {
if let Some(leader_id) = self
.leader_schedule_cache
.slot_leader_at(current_slot.saturating_sub(1), None)
{
leader_id == self.id
} else {
false
}
}
fn reached_leader_tick(&self, leader_first_tick_height: u64) -> bool { fn reached_leader_tick(&self, leader_first_tick_height: u64) -> bool {
let target_tick_height = leader_first_tick_height.saturating_sub(1); let target_tick_height = leader_first_tick_height.saturating_sub(1);
let ideal_target_tick_height = target_tick_height.saturating_sub(self.grace_ticks); let ideal_target_tick_height = target_tick_height.saturating_sub(self.grace_ticks);
@ -160,7 +172,8 @@ impl PohRecorder {
self.tick_height >= target_tick_height self.tick_height >= target_tick_height
|| self.start_tick_height + self.grace_ticks == leader_first_tick_height || self.start_tick_height + self.grace_ticks == leader_first_tick_height
|| (self.tick_height >= ideal_target_tick_height || (self.tick_height >= ideal_target_tick_height
&& !self.received_any_previous_leader_data(current_slot)) && (self.prev_slot_was_mine(current_slot)
|| !self.received_any_previous_leader_data(current_slot)))
} }
/// returns if leader slot has been reached, how many grace ticks were afforded, /// returns if leader slot has been reached, how many grace ticks were afforded,
@ -1130,6 +1143,7 @@ mod tests {
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2); let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new(&genesis_config)); let bank = Arc::new(Bank::new(&genesis_config));
let prev_hash = bank.last_blockhash(); let prev_hash = bank.last_blockhash();
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
let (mut poh_recorder, _entry_receiver) = PohRecorder::new( let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
0, 0,
prev_hash, prev_hash,
@ -1138,10 +1152,12 @@ mod tests {
bank.ticks_per_slot(), bank.ticks_per_slot(),
&Pubkey::default(), &Pubkey::default(),
&Arc::new(blockstore), &Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)), &leader_schedule_cache,
&Arc::new(PohConfig::default()), &Arc::new(PohConfig::default()),
); );
let bootstrap_validator_id = leader_schedule_cache.slot_leader_at(0, None).unwrap();
assert_eq!(poh_recorder.reached_leader_tick(0), true); assert_eq!(poh_recorder.reached_leader_tick(0), true);
let grace_ticks = bank.ticks_per_slot() * MAX_GRACE_SLOTS; let grace_ticks = bank.ticks_per_slot() * MAX_GRACE_SLOTS;
@ -1170,6 +1186,11 @@ mod tests {
poh_recorder.reached_leader_tick(new_tick_height + grace_ticks), poh_recorder.reached_leader_tick(new_tick_height + grace_ticks),
false false
); );
// From the bootstrap validator's perspective, it should have reached
// the tick
poh_recorder.id = bootstrap_validator_id;
assert!(poh_recorder.reached_leader_tick(new_tick_height + grace_ticks));
} }
} }

View File

@ -317,7 +317,7 @@ impl RepairService {
); );
} }
// Update the gossiped structure used for the "Repairmen" repair protocol. See book // Update the gossiped structure used for the "Repairmen" repair protocol. See docs
// for details. // for details.
fn update_epoch_slots( fn update_epoch_slots(
id: Pubkey, id: Pubkey,

View File

@ -4,7 +4,7 @@ use crate::{
cluster_info::ClusterInfo, cluster_info::ClusterInfo,
commitment::{AggregateCommitmentService, BlockCommitmentCache, CommitmentAggregationData}, commitment::{AggregateCommitmentService, BlockCommitmentCache, CommitmentAggregationData},
consensus::{StakeLockout, Tower}, consensus::{StakeLockout, Tower},
poh_recorder::PohRecorder, poh_recorder::{PohRecorder, GRACE_TICKS_FACTOR, MAX_GRACE_SLOTS},
result::Result, result::Result,
rewards_recorder_service::RewardsRecorderSender, rewards_recorder_service::RewardsRecorderSender,
rpc_subscriptions::RpcSubscriptions, rpc_subscriptions::RpcSubscriptions,
@ -252,146 +252,138 @@ impl ReplayStage {
); );
let ancestors = Arc::new(bank_forks.read().unwrap().ancestors()); let ancestors = Arc::new(bank_forks.read().unwrap().ancestors());
loop { let start = allocated.get();
let start = allocated.get(); let mut frozen_banks: Vec<_> = bank_forks
let mut frozen_banks: Vec<_> = bank_forks .read()
.read() .unwrap()
.unwrap() .frozen_banks()
.frozen_banks() .values()
.values() .cloned()
.cloned() .collect();
.collect(); let newly_computed_slot_stats = Self::compute_bank_stats(
let newly_computed_slot_stats = Self::compute_bank_stats( &my_pubkey,
&my_pubkey, &ancestors,
&ancestors, &mut frozen_banks,
&mut frozen_banks, &tower,
&mut progress,
);
for slot in newly_computed_slot_stats {
let fork_stats = &progress.get(&slot).unwrap().fork_stats;
let confirmed_forks = Self::confirm_forks(
&tower, &tower,
&mut progress, &fork_stats.stake_lockouts,
fork_stats.total_staked,
&progress,
&bank_forks,
); );
for slot in newly_computed_slot_stats {
let fork_stats = &progress.get(&slot).unwrap().fork_stats;
let confirmed_forks = Self::confirm_forks(
&tower,
&fork_stats.stake_lockouts,
fork_stats.total_staked,
&progress,
&bank_forks,
);
for slot in confirmed_forks { for slot in confirmed_forks {
progress progress
.get_mut(&slot) .get_mut(&slot)
.unwrap() .unwrap()
.fork_stats .fork_stats
.confirmation_reported = true; .confirmation_reported = true;
}
}
let vote_bank = Self::select_fork(&frozen_banks, &tower, &mut progress);
datapoint_debug!(
"replay_stage-memory",
("select_fork", (allocated.get() - start) as i64, i64),
);
if vote_bank.is_none() {
break;
}
let bank = vote_bank.unwrap();
let (is_locked_out, vote_threshold, fork_weight, total_staked) = {
let fork_stats = &progress.get(&bank.slot()).unwrap().fork_stats;
(
fork_stats.is_locked_out,
fork_stats.vote_threshold,
fork_stats.weight,
fork_stats.total_staked,
)
};
let mut done = false;
let mut vote_bank_slot = None;
let start = allocated.get();
if !is_locked_out && vote_threshold {
info!("voting: {} {}", bank.slot(), fork_weight);
subscriptions.notify_subscribers(bank.slot(), &bank_forks);
if let Some(votable_leader) =
leader_schedule_cache.slot_leader_at(bank.slot(), Some(&bank))
{
Self::log_leader_change(
&my_pubkey,
bank.slot(),
&mut current_leader,
&votable_leader,
);
}
vote_bank_slot = Some(bank.slot());
Self::handle_votable_bank(
&bank,
&bank_forks,
&mut tower,
&mut progress,
&vote_account,
&voting_keypair,
&cluster_info,
&blockstore,
&leader_schedule_cache,
&root_bank_sender,
total_staked,
&lockouts_sender,
&snapshot_package_sender,
&latest_root_senders,
)?;
}
datapoint_debug!(
"replay_stage-memory",
("votable_bank", (allocated.get() - start) as i64, i64),
);
let start = allocated.get();
if last_reset != bank.last_blockhash() {
Self::reset_poh_recorder(
&my_pubkey,
&blockstore,
&bank,
&poh_recorder,
&leader_schedule_cache,
);
last_reset = bank.last_blockhash();
tpu_has_bank = false;
info!(
"vote bank: {:?} reset bank: {}",
vote_bank_slot,
bank.slot()
);
if !partition && vote_bank_slot != Some(bank.slot()) {
warn!(
"PARTITION DETECTED waiting to join fork: {} last vote: {:?}",
bank.slot(),
tower.last_vote()
);
inc_new_counter_info!("replay_stage-partition_detected", 1);
datapoint_info!(
"replay_stage-partition",
("slot", bank.slot() as i64, i64)
);
partition = true;
} else if partition && vote_bank_slot == Some(bank.slot()) {
warn!(
"PARTITION resolved fork: {} last vote: {:?}",
bank.slot(),
tower.last_vote()
);
partition = false;
inc_new_counter_info!("replay_stage-partition_resolved", 1);
}
} else {
done = true;
}
datapoint_debug!(
"replay_stage-memory",
("reset_bank", (allocated.get() - start) as i64, i64),
);
if done {
break;
} }
} }
let vote_bank = Self::select_fork(&frozen_banks, &tower, &progress);
datapoint_debug!(
"replay_stage-memory",
("select_fork", (allocated.get() - start) as i64, i64),
);
if vote_bank.is_none() {
break;
}
let bank = vote_bank.unwrap();
let (is_locked_out, vote_threshold, fork_weight, total_staked) = {
let fork_stats = &progress.get(&bank.slot()).unwrap().fork_stats;
(
fork_stats.is_locked_out,
fork_stats.vote_threshold,
fork_stats.weight,
fork_stats.total_staked,
)
};
let mut vote_bank_slot = None;
let start = allocated.get();
if !is_locked_out && vote_threshold {
info!("voting: {} {}", bank.slot(), fork_weight);
subscriptions.notify_subscribers(bank.slot(), &bank_forks);
if let Some(votable_leader) =
leader_schedule_cache.slot_leader_at(bank.slot(), Some(&bank))
{
Self::log_leader_change(
&my_pubkey,
bank.slot(),
&mut current_leader,
&votable_leader,
);
}
vote_bank_slot = Some(bank.slot());
Self::handle_votable_bank(
&bank,
&bank_forks,
&mut tower,
&mut progress,
&vote_account,
&voting_keypair,
&cluster_info,
&blockstore,
&leader_schedule_cache,
&root_bank_sender,
total_staked,
&lockouts_sender,
&snapshot_package_sender,
&latest_root_senders,
)?;
}
datapoint_debug!(
"replay_stage-memory",
("votable_bank", (allocated.get() - start) as i64, i64),
);
let start = allocated.get();
if last_reset != bank.last_blockhash() {
Self::reset_poh_recorder(
&my_pubkey,
&blockstore,
&bank,
&poh_recorder,
&leader_schedule_cache,
);
last_reset = bank.last_blockhash();
tpu_has_bank = false;
info!(
"vote bank: {:?} reset bank: {}",
vote_bank_slot,
bank.slot()
);
if !partition && vote_bank_slot != Some(bank.slot()) {
warn!(
"PARTITION DETECTED waiting to join fork: {} last vote: {:?}",
bank.slot(),
tower.last_vote()
);
inc_new_counter_info!("replay_stage-partition_detected", 1);
datapoint_info!(
"replay_stage-partition",
("slot", bank.slot() as i64, i64)
);
partition = true;
} else if partition && vote_bank_slot == Some(bank.slot()) {
warn!(
"PARTITION resolved fork: {} last vote: {:?}",
bank.slot(),
tower.last_vote()
);
partition = false;
inc_new_counter_info!("replay_stage-partition_resolved", 1);
}
}
datapoint_debug!(
"replay_stage-memory",
("reset_bank", (allocated.get() - start) as i64, i64),
);
let start = allocated.get(); let start = allocated.get();
if !tpu_has_bank { if !tpu_has_bank {
Self::maybe_start_leader( Self::maybe_start_leader(
@ -690,6 +682,7 @@ impl ReplayStage {
bank.slot(), bank.slot(),
&bank, &bank,
Some(blockstore), Some(blockstore),
GRACE_TICKS_FACTOR * MAX_GRACE_SLOTS,
); );
poh_recorder poh_recorder
.lock() .lock()
@ -848,7 +841,7 @@ impl ReplayStage {
pub(crate) fn select_fork( pub(crate) fn select_fork(
frozen_banks: &[Arc<Bank>], frozen_banks: &[Arc<Bank>],
tower: &Tower, tower: &Tower,
progress: &mut HashMap<u64, ForkProgress>, progress: &HashMap<u64, ForkProgress>,
) -> Option<Arc<Bank>> { ) -> Option<Arc<Bank>> {
let tower_start = Instant::now(); let tower_start = Instant::now();
let num_frozen_banks = frozen_banks.len(); let num_frozen_banks = frozen_banks.len();
@ -1055,7 +1048,7 @@ pub(crate) mod tests {
use super::*; use super::*;
use crate::{ use crate::{
commitment::BlockCommitment, commitment::BlockCommitment,
consensus::test::{initialize_state, VoteResult, VoteSimulator}, consensus::test::{initialize_state, VoteSimulator},
consensus::Tower, consensus::Tower,
genesis_utils::{create_genesis_config, create_genesis_config_with_leader}, genesis_utils::{create_genesis_config, create_genesis_config_with_leader},
replay_stage::ReplayStage, replay_stage::ReplayStage,
@ -1087,7 +1080,7 @@ pub(crate) mod tests {
transaction::TransactionError, transaction::TransactionError,
}; };
use solana_stake_program::stake_state; use solana_stake_program::stake_state;
use solana_vote_program::vote_state::{self, Vote, VoteState}; use solana_vote_program::vote_state::{self, Vote, VoteState, VoteStateVersions};
use std::{ use std::{
fs::remove_dir_all, fs::remove_dir_all,
iter, iter,
@ -1122,7 +1115,8 @@ pub(crate) mod tests {
let mut vote_account = bank.get_account(&pubkey).unwrap(); let mut vote_account = bank.get_account(&pubkey).unwrap();
let mut vote_state = VoteState::from(&vote_account).unwrap(); let mut vote_state = VoteState::from(&vote_account).unwrap();
vote_state.process_slot_vote_unchecked(slot); vote_state.process_slot_vote_unchecked(slot);
vote_state.to(&mut vote_account).unwrap(); let versioned = VoteStateVersions::Current(Box::new(vote_state));
VoteState::to(&versioned, &mut vote_account).unwrap();
bank.store_account(&pubkey, &vote_account); bank.store_account(&pubkey, &vote_account);
} }
@ -1298,7 +1292,7 @@ pub(crate) mod tests {
&mut fork_progresses[i], &mut fork_progresses[i],
); );
let response = let response =
ReplayStage::select_fork(&frozen_banks, &towers[i], &mut fork_progresses[i]); ReplayStage::select_fork(&frozen_banks, &towers[i], &fork_progresses[i]);
if response.is_none() { if response.is_none() {
None None
@ -1706,7 +1700,8 @@ pub(crate) mod tests {
let mut leader_vote_account = bank.get_account(&pubkey).unwrap(); let mut leader_vote_account = bank.get_account(&pubkey).unwrap();
let mut vote_state = VoteState::from(&leader_vote_account).unwrap(); let mut vote_state = VoteState::from(&leader_vote_account).unwrap();
vote_state.process_slot_vote_unchecked(bank.slot()); vote_state.process_slot_vote_unchecked(bank.slot());
vote_state.to(&mut leader_vote_account).unwrap(); let versioned = VoteStateVersions::Current(Box::new(vote_state));
VoteState::to(&versioned, &mut leader_vote_account).unwrap();
bank.store_account(&pubkey, &leader_vote_account); bank.store_account(&pubkey, &leader_vote_account);
} }
@ -1937,8 +1932,8 @@ pub(crate) mod tests {
let mut cluster_votes: HashMap<Pubkey, Vec<Slot>> = HashMap::new(); let mut cluster_votes: HashMap<Pubkey, Vec<Slot>> = HashMap::new();
let votes: Vec<Slot> = vec![0, 2]; let votes: Vec<Slot> = vec![0, 2];
for vote in &votes { for vote in &votes {
assert_eq!( assert!(voting_simulator
voting_simulator.simulate_vote( .simulate_vote(
*vote, *vote,
&bank_forks, &bank_forks,
&mut cluster_votes, &mut cluster_votes,
@ -1946,9 +1941,8 @@ pub(crate) mod tests {
keypairs.get(&node_pubkey).unwrap(), keypairs.get(&node_pubkey).unwrap(),
&mut progress, &mut progress,
&mut tower, &mut tower,
), )
VoteResult::Ok .is_empty());
);
} }
let mut frozen_banks: Vec<_> = bank_forks let mut frozen_banks: Vec<_> = bank_forks

View File

@ -12,7 +12,7 @@ use std::sync::{atomic, Arc};
// https://github.com/paritytech/jsonrpc/blob/2d38e6424d8461cdf72e78425ce67d51af9c6586/derive/src/lib.rs#L204 // https://github.com/paritytech/jsonrpc/blob/2d38e6424d8461cdf72e78425ce67d51af9c6586/derive/src/lib.rs#L204
// Once https://github.com/paritytech/jsonrpc/issues/418 is resolved, try to remove this clippy allow // Once https://github.com/paritytech/jsonrpc/issues/418 is resolved, try to remove this clippy allow
#[allow(clippy::needless_return)] #[allow(clippy::needless_return)]
#[rpc(server)] #[rpc]
pub trait RpcSolPubSub { pub trait RpcSolPubSub {
type Metadata; type Metadata;

View File

@ -37,6 +37,8 @@ impl PubSubService {
}); });
session session
}) })
.max_connections(1000) // Arbitrary, default of 100 is too low
.max_payload(10 * 1024 * 1024 + 1024) // max account size (10MB) + extra (1K)
.start(&pubsub_addr); .start(&pubsub_addr);
if let Err(e) = server { if let Err(e) = server {

View File

@ -9,7 +9,12 @@ use jsonrpc_http_server::{
hyper, AccessControlAllowOrigin, CloseHandle, DomainsValidation, RequestMiddleware, hyper, AccessControlAllowOrigin, CloseHandle, DomainsValidation, RequestMiddleware,
RequestMiddlewareAction, ServerBuilder, RequestMiddlewareAction, ServerBuilder,
}; };
use solana_ledger::{bank_forks::BankForks, blockstore::Blockstore}; use regex::Regex;
use solana_ledger::{
bank_forks::{BankForks, SnapshotConfig},
blockstore::Blockstore,
snapshot_utils,
};
use solana_sdk::hash::Hash; use solana_sdk::hash::Hash;
use std::{ use std::{
net::SocketAddr, net::SocketAddr,
@ -28,13 +33,28 @@ pub struct JsonRpcService {
close_handle: Option<CloseHandle>, close_handle: Option<CloseHandle>,
} }
#[derive(Default)]
struct RpcRequestMiddleware { struct RpcRequestMiddleware {
ledger_path: PathBuf, ledger_path: PathBuf,
snapshot_archive_path_regex: Regex,
snapshot_config: Option<SnapshotConfig>,
} }
impl RpcRequestMiddleware { impl RpcRequestMiddleware {
pub fn new(ledger_path: PathBuf) -> Self { pub fn new(ledger_path: PathBuf, snapshot_config: Option<SnapshotConfig>) -> Self {
Self { ledger_path } Self {
ledger_path,
snapshot_archive_path_regex: Regex::new(r"/snapshot-\d+-[[:alnum:]]+\.tar\.bz2$")
.unwrap(),
snapshot_config,
}
}
fn redirect(location: &str) -> hyper::Response<hyper::Body> {
hyper::Response::builder()
.status(hyper::StatusCode::SEE_OTHER)
.header(hyper::header::LOCATION, location)
.body(hyper::Body::from(String::from(location)))
.unwrap()
} }
fn not_found() -> hyper::Response<hyper::Body> { fn not_found() -> hyper::Response<hyper::Body> {
@ -51,9 +71,25 @@ impl RpcRequestMiddleware {
.unwrap() .unwrap()
} }
fn get(&self, filename: &str) -> RequestMiddlewareAction { fn is_get_path(&self, path: &str) -> bool {
info!("get {}", filename); match path {
let filename = self.ledger_path.join(filename); "/genesis.tar.bz2" => true,
_ => {
if self.snapshot_config.is_some() {
self.snapshot_archive_path_regex.is_match(path)
} else {
false
}
}
}
}
fn get(&self, path: &str) -> RequestMiddlewareAction {
let filename = self.ledger_path.join(
path.split_at(1).1, // Drop leading '/' from path
);
info!("get {} -> {:?}", path, filename);
RequestMiddlewareAction::Respond { RequestMiddlewareAction::Respond {
should_validate_hosts: true, should_validate_hosts: true,
response: Box::new( response: Box::new(
@ -73,13 +109,40 @@ impl RpcRequestMiddleware {
impl RequestMiddleware for RpcRequestMiddleware { impl RequestMiddleware for RpcRequestMiddleware {
fn on_request(&self, request: hyper::Request<hyper::Body>) -> RequestMiddlewareAction { fn on_request(&self, request: hyper::Request<hyper::Body>) -> RequestMiddlewareAction {
trace!("request uri: {}", request.uri()); trace!("request uri: {}", request.uri());
match request.uri().path() {
"/snapshot.tar.bz2" => self.get("snapshot.tar.bz2"), if let Some(ref snapshot_config) = self.snapshot_config {
"/genesis.tar.bz2" => self.get("genesis.tar.bz2"), if request.uri().path() == "/snapshot.tar.bz2" {
_ => RequestMiddlewareAction::Proceed { // Convenience redirect to the latest snapshot
return RequestMiddlewareAction::Respond {
should_validate_hosts: true,
response: Box::new(jsonrpc_core::futures::future::ok(
if let Some((snapshot_archive, _)) =
snapshot_utils::get_highest_snapshot_archive_path(
&snapshot_config.snapshot_package_output_path,
)
{
RpcRequestMiddleware::redirect(&format!(
"/{}",
snapshot_archive
.file_name()
.unwrap_or_else(|| std::ffi::OsStr::new(""))
.to_str()
.unwrap_or(&"")
))
} else {
RpcRequestMiddleware::not_found()
},
)),
};
}
}
if self.is_get_path(request.uri().path()) {
self.get(request.uri().path())
} else {
RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: false, should_continue_on_invalid_cors: false,
request, request,
}, }
} }
} }
} }
@ -89,6 +152,7 @@ impl JsonRpcService {
pub fn new( pub fn new(
rpc_addr: SocketAddr, rpc_addr: SocketAddr,
config: JsonRpcConfig, config: JsonRpcConfig,
snapshot_config: Option<SnapshotConfig>,
bank_forks: Arc<RwLock<BankForks>>, bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>, block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
blockstore: Arc<Blockstore>, blockstore: Arc<Blockstore>,
@ -132,7 +196,7 @@ impl JsonRpcService {
AccessControlAllowOrigin::Any, AccessControlAllowOrigin::Any,
])) ]))
.cors_max_age(86400) .cors_max_age(86400)
.request_middleware(RpcRequestMiddleware::new(ledger_path)) .request_middleware(RpcRequestMiddleware::new(ledger_path, snapshot_config))
.start_http(&rpc_addr); .start_http(&rpc_addr);
if let Err(e) = server { if let Err(e) = server {
warn!("JSON RPC service unavailable error: {:?}. \nAlso, check that port {} is not already in use by another application", e, rpc_addr.port()); warn!("JSON RPC service unavailable error: {:?}. \nAlso, check that port {} is not already in use by another application", e, rpc_addr.port());
@ -209,6 +273,7 @@ mod tests {
let mut rpc_service = JsonRpcService::new( let mut rpc_service = JsonRpcService::new(
rpc_addr, rpc_addr,
JsonRpcConfig::default(), JsonRpcConfig::default(),
None,
bank_forks, bank_forks,
block_commitment_cache, block_commitment_cache,
Arc::new(blockstore), Arc::new(blockstore),
@ -234,4 +299,36 @@ mod tests {
rpc_service.exit(); rpc_service.exit();
rpc_service.join().unwrap(); rpc_service.join().unwrap();
} }
#[test]
fn test_is_get_path() {
let rrm = RpcRequestMiddleware::new(PathBuf::from("/"), None);
let rrm_with_snapshot_config = RpcRequestMiddleware::new(
PathBuf::from("/"),
Some(SnapshotConfig {
snapshot_interval_slots: 0,
snapshot_package_output_path: PathBuf::from("/"),
snapshot_path: PathBuf::from("/"),
}),
);
assert!(rrm.is_get_path("/genesis.tar.bz2"));
assert!(!rrm.is_get_path("genesis.tar.bz2"));
assert!(!rrm.is_get_path("/snapshot.tar.bz2")); // This is a redirect
assert!(
!rrm.is_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2")
);
assert!(rrm_with_snapshot_config
.is_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2"));
assert!(!rrm.is_get_path(
"/snapshot-notaslotnumber-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2"
));
assert!(!rrm.is_get_path("/"));
assert!(!rrm.is_get_path(".."));
assert!(!rrm.is_get_path("🎣"));
}
} }

View File

@ -17,14 +17,16 @@ use std::thread::{Builder, JoinHandle};
use std::time::Duration; use std::time::Duration;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
iter,
sync::{Arc, Mutex, RwLock}, sync::{Arc, Mutex, RwLock},
}; };
use tokio::runtime::{Builder as RuntimeBuilder, Runtime, TaskExecutor};
const RECEIVE_DELAY_MILLIS: u64 = 100; const RECEIVE_DELAY_MILLIS: u64 = 100;
pub type Confirmations = usize; pub type Confirmations = usize;
#[derive(Serialize, Clone, Copy, Debug)] #[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub struct SlotInfo { pub struct SlotInfo {
pub slot: Slot, pub slot: Slot,
pub parent: Slot, pub parent: Slot,
@ -103,19 +105,20 @@ where
found found
} }
fn check_confirmations_and_notify<K, S, F, N, X>( fn check_confirmations_and_notify<K, S, B, F, X>(
subscriptions: &HashMap<K, HashMap<SubscriptionId, (Sink<S>, Confirmations)>>, subscriptions: &HashMap<K, HashMap<SubscriptionId, (Sink<S>, Confirmations)>>,
hashmap_key: &K, hashmap_key: &K,
current_slot: Slot, current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
bank_method: F, bank_method: B,
notify: N, filter_results: F,
notifier: &RpcNotifier,
) -> HashSet<SubscriptionId> ) -> HashSet<SubscriptionId>
where where
K: Eq + Hash + Clone + Copy, K: Eq + Hash + Clone + Copy,
S: Clone + Serialize, S: Clone + Serialize,
F: Fn(&Bank, &K) -> X, B: Fn(&Bank, &K) -> X,
N: Fn(X, &Sink<S>, u64) -> bool, F: Fn(X, u64) -> Box<dyn Iterator<Item = S>>,
X: Clone + Serialize, X: Clone + Serialize,
{ {
let current_ancestors = bank_forks let current_ancestors = bank_forks
@ -149,8 +152,9 @@ where
.get(desired_slot[0]) .get(desired_slot[0])
.unwrap() .unwrap()
.clone(); .clone();
let result = bank_method(&desired_bank, hashmap_key); let results = bank_method(&desired_bank, hashmap_key);
if notify(result, &sink, root) { for result in filter_results(results, root) {
notifier.notify(result, sink);
notified_set.insert(bank_sub_id.clone()); notified_set.insert(bank_sub_id.clone());
} }
} }
@ -159,41 +163,49 @@ where
notified_set notified_set
} }
fn notify_account(result: Option<(Account, Slot)>, sink: &Sink<RpcAccount>, root: Slot) -> bool { struct RpcNotifier(TaskExecutor);
impl RpcNotifier {
fn notify<T>(&self, value: T, sink: &Sink<T>)
where
T: serde::Serialize,
{
self.0
.spawn(sink.notify(Ok(value)).map(|_| ()).map_err(|_| ()));
}
}
fn filter_account_result(
result: Option<(Account, Slot)>,
root: Slot,
) -> Box<dyn Iterator<Item = RpcAccount>> {
if let Some((account, fork)) = result { if let Some((account, fork)) = result {
if fork >= root { if fork >= root {
sink.notify(Ok(RpcAccount::encode(account))).wait().unwrap(); return Box::new(iter::once(RpcAccount::encode(account)));
return true;
} }
} }
false Box::new(iter::empty())
} }
fn notify_signature<S>(result: Option<S>, sink: &Sink<S>, _root: Slot) -> bool fn filter_signature_result<S>(result: Option<S>, _root: Slot) -> Box<dyn Iterator<Item = S>>
where where
S: Clone + Serialize, S: 'static + Clone + Serialize,
{ {
if let Some(result) = result { Box::new(result.into_iter())
sink.notify(Ok(result)).wait().unwrap();
return true;
}
false
} }
fn notify_program( fn filter_program_results(
accounts: Vec<(Pubkey, Account)>, accounts: Vec<(Pubkey, Account)>,
sink: &Sink<RpcKeyedAccount>,
_root: Slot, _root: Slot,
) -> bool { ) -> Box<dyn Iterator<Item = RpcKeyedAccount>> {
for (pubkey, account) in accounts.iter() { Box::new(
sink.notify(Ok(RpcKeyedAccount { accounts
pubkey: pubkey.to_string(), .into_iter()
account: RpcAccount::encode(account.clone()), .map(|(pubkey, account)| RpcKeyedAccount {
})) pubkey: pubkey.to_string(),
.wait() account: RpcAccount::encode(account),
.unwrap(); }),
} )
!accounts.is_empty()
} }
pub struct RpcSubscriptions { pub struct RpcSubscriptions {
@ -203,6 +215,7 @@ pub struct RpcSubscriptions {
slot_subscriptions: Arc<RpcSlotSubscriptions>, slot_subscriptions: Arc<RpcSlotSubscriptions>,
notification_sender: Arc<Mutex<Sender<NotificationEntry>>>, notification_sender: Arc<Mutex<Sender<NotificationEntry>>>,
t_cleanup: Option<JoinHandle<()>>, t_cleanup: Option<JoinHandle<()>>,
notifier_runtime: Option<Runtime>,
exit: Arc<AtomicBool>, exit: Arc<AtomicBool>,
} }
@ -239,11 +252,19 @@ impl RpcSubscriptions {
let signature_subscriptions_clone = signature_subscriptions.clone(); let signature_subscriptions_clone = signature_subscriptions.clone();
let slot_subscriptions_clone = slot_subscriptions.clone(); let slot_subscriptions_clone = slot_subscriptions.clone();
let notifier_runtime = RuntimeBuilder::new()
.core_threads(1)
.name_prefix("solana-rpc-notifier-")
.build()
.unwrap();
let notifier = RpcNotifier(notifier_runtime.executor());
let t_cleanup = Builder::new() let t_cleanup = Builder::new()
.name("solana-rpc-notifications".to_string()) .name("solana-rpc-notifications".to_string())
.spawn(move || { .spawn(move || {
Self::process_notifications( Self::process_notifications(
exit_clone, exit_clone,
notifier,
notification_receiver, notification_receiver,
account_subscriptions_clone, account_subscriptions_clone,
program_subscriptions_clone, program_subscriptions_clone,
@ -259,6 +280,7 @@ impl RpcSubscriptions {
signature_subscriptions, signature_subscriptions,
slot_subscriptions, slot_subscriptions,
notification_sender, notification_sender,
notifier_runtime: Some(notifier_runtime),
t_cleanup: Some(t_cleanup), t_cleanup: Some(t_cleanup),
exit: exit.clone(), exit: exit.clone(),
} }
@ -269,6 +291,7 @@ impl RpcSubscriptions {
current_slot: Slot, current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
account_subscriptions: Arc<RpcAccountSubscriptions>, account_subscriptions: Arc<RpcAccountSubscriptions>,
notifier: &RpcNotifier,
) { ) {
let subscriptions = account_subscriptions.read().unwrap(); let subscriptions = account_subscriptions.read().unwrap();
check_confirmations_and_notify( check_confirmations_and_notify(
@ -277,7 +300,8 @@ impl RpcSubscriptions {
current_slot, current_slot,
bank_forks, bank_forks,
Bank::get_account_modified_since_parent, Bank::get_account_modified_since_parent,
notify_account, filter_account_result,
notifier,
); );
} }
@ -286,6 +310,7 @@ impl RpcSubscriptions {
current_slot: Slot, current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
program_subscriptions: Arc<RpcProgramSubscriptions>, program_subscriptions: Arc<RpcProgramSubscriptions>,
notifier: &RpcNotifier,
) { ) {
let subscriptions = program_subscriptions.read().unwrap(); let subscriptions = program_subscriptions.read().unwrap();
check_confirmations_and_notify( check_confirmations_and_notify(
@ -294,7 +319,8 @@ impl RpcSubscriptions {
current_slot, current_slot,
bank_forks, bank_forks,
Bank::get_program_accounts_modified_since_parent, Bank::get_program_accounts_modified_since_parent,
notify_program, filter_program_results,
notifier,
); );
} }
@ -303,6 +329,7 @@ impl RpcSubscriptions {
current_slot: Slot, current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
signature_subscriptions: Arc<RpcSignatureSubscriptions>, signature_subscriptions: Arc<RpcSignatureSubscriptions>,
notifier: &RpcNotifier,
) { ) {
let mut subscriptions = signature_subscriptions.write().unwrap(); let mut subscriptions = signature_subscriptions.write().unwrap();
let notified_ids = check_confirmations_and_notify( let notified_ids = check_confirmations_and_notify(
@ -311,7 +338,8 @@ impl RpcSubscriptions {
current_slot, current_slot,
bank_forks, bank_forks,
Bank::get_signature_status, Bank::get_signature_status,
notify_signature, filter_signature_result,
notifier,
); );
if let Some(subscription_ids) = subscriptions.get_mut(signature) { if let Some(subscription_ids) = subscriptions.get_mut(signature) {
subscription_ids.retain(|k, _| !notified_ids.contains(k)); subscription_ids.retain(|k, _| !notified_ids.contains(k));
@ -408,6 +436,7 @@ impl RpcSubscriptions {
fn process_notifications( fn process_notifications(
exit: Arc<AtomicBool>, exit: Arc<AtomicBool>,
notifier: RpcNotifier,
notification_receiver: Receiver<NotificationEntry>, notification_receiver: Receiver<NotificationEntry>,
account_subscriptions: Arc<RpcAccountSubscriptions>, account_subscriptions: Arc<RpcAccountSubscriptions>,
program_subscriptions: Arc<RpcProgramSubscriptions>, program_subscriptions: Arc<RpcProgramSubscriptions>,
@ -423,7 +452,7 @@ impl RpcSubscriptions {
NotificationEntry::Slot(slot_info) => { NotificationEntry::Slot(slot_info) => {
let subscriptions = slot_subscriptions.read().unwrap(); let subscriptions = slot_subscriptions.read().unwrap();
for (_, sink) in subscriptions.iter() { for (_, sink) in subscriptions.iter() {
sink.notify(Ok(slot_info)).wait().unwrap(); notifier.notify(slot_info, sink);
} }
} }
NotificationEntry::Bank((current_slot, bank_forks)) => { NotificationEntry::Bank((current_slot, bank_forks)) => {
@ -437,6 +466,7 @@ impl RpcSubscriptions {
current_slot, current_slot,
&bank_forks, &bank_forks,
account_subscriptions.clone(), account_subscriptions.clone(),
&notifier,
); );
} }
@ -450,6 +480,7 @@ impl RpcSubscriptions {
current_slot, current_slot,
&bank_forks, &bank_forks,
program_subscriptions.clone(), program_subscriptions.clone(),
&notifier,
); );
} }
@ -463,6 +494,7 @@ impl RpcSubscriptions {
current_slot, current_slot,
&bank_forks, &bank_forks,
signature_subscriptions.clone(), signature_subscriptions.clone(),
&notifier,
); );
} }
} }
@ -479,6 +511,12 @@ impl RpcSubscriptions {
} }
fn shutdown(&mut self) -> std::thread::Result<()> { fn shutdown(&mut self) -> std::thread::Result<()> {
if let Some(runtime) = self.notifier_runtime.take() {
info!("RPC Notifier runtime - shutting down");
let _ = runtime.shutdown_now().wait();
info!("RPC Notifier runtime - shut down");
}
if self.t_cleanup.is_some() { if self.t_cleanup.is_some() {
info!("RPC Notification thread - shutting down"); info!("RPC Notification thread - shutting down");
self.exit.store(true, Ordering::Relaxed); self.exit.store(true, Ordering::Relaxed);

View File

@ -1,4 +1,4 @@
use crate::cluster_info::ClusterInfo; use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
use solana_ledger::{ use solana_ledger::{
snapshot_package::SnapshotPackageReceiver, snapshot_utils::archive_snapshot_package, snapshot_package::SnapshotPackageReceiver, snapshot_utils::archive_snapshot_package,
}; };
@ -16,8 +16,6 @@ pub struct SnapshotPackagerService {
t_snapshot_packager: JoinHandle<()>, t_snapshot_packager: JoinHandle<()>,
} }
const MAX_SNAPSHOT_HASHES: usize = 24;
impl SnapshotPackagerService { impl SnapshotPackagerService {
pub fn new( pub fn new(
snapshot_package_receiver: SnapshotPackageReceiver, snapshot_package_receiver: SnapshotPackageReceiver,
@ -154,8 +152,10 @@ mod tests {
} }
// Create a packageable snapshot // Create a packageable snapshot
let output_tar_path = let output_tar_path = snapshot_utils::get_snapshot_archive_path(
snapshot_utils::get_snapshot_archive_path(&snapshot_package_output_path); &snapshot_package_output_path,
&(42, Hash::default()),
);
let snapshot_package = SnapshotPackage::new( let snapshot_package = SnapshotPackage::new(
5, 5,
vec![], vec![],

View File

@ -6,7 +6,7 @@ use crate::{
commitment::BlockCommitmentCache, commitment::BlockCommitmentCache,
contact_info::ContactInfo, contact_info::ContactInfo,
gossip_service::{discover_cluster, GossipService}, gossip_service::{discover_cluster, GossipService},
poh_recorder::PohRecorder, poh_recorder::{PohRecorder, GRACE_TICKS_FACTOR, MAX_GRACE_SLOTS},
poh_service::PohService, poh_service::PohService,
rewards_recorder_service::RewardsRecorderService, rewards_recorder_service::RewardsRecorderService,
rpc::JsonRpcConfig, rpc::JsonRpcConfig,
@ -30,19 +30,21 @@ use solana_ledger::{
create_new_tmp_ledger, create_new_tmp_ledger,
leader_schedule::FixedSchedule, leader_schedule::FixedSchedule,
leader_schedule_cache::LeaderScheduleCache, leader_schedule_cache::LeaderScheduleCache,
shred_version::compute_shred_version,
}; };
use solana_metrics::datapoint_info; use solana_metrics::datapoint_info;
use solana_runtime::bank::Bank; use solana_runtime::bank::Bank;
use solana_sdk::{ use solana_sdk::{
clock::{Slot, DEFAULT_SLOTS_PER_TURN}, clock::{Slot, DEFAULT_SLOTS_PER_TURN},
epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
genesis_config::GenesisConfig, genesis_config::GenesisConfig,
hash::Hash, hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
shred_version::compute_shred_version,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
timing::timestamp, timing::timestamp,
}; };
use std::{ use std::{
collections::HashSet,
net::{IpAddr, Ipv4Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, SocketAddr},
path::{Path, PathBuf}, path::{Path, PathBuf},
process, process,
@ -72,6 +74,7 @@ pub struct ValidatorConfig {
pub fixed_leader_schedule: Option<FixedSchedule>, pub fixed_leader_schedule: Option<FixedSchedule>,
pub wait_for_supermajority: bool, pub wait_for_supermajority: bool,
pub new_hard_forks: Option<Vec<Slot>>, pub new_hard_forks: Option<Vec<Slot>>,
pub trusted_validators: Option<HashSet<Pubkey>>, // None = trust all
} }
impl Default for ValidatorConfig { impl Default for ValidatorConfig {
@ -94,6 +97,7 @@ impl Default for ValidatorConfig {
fixed_leader_schedule: None, fixed_leader_schedule: None,
wait_for_supermajority: false, wait_for_supermajority: false,
new_hard_forks: None, new_hard_forks: None,
trusted_validators: None,
} }
} }
} }
@ -238,6 +242,7 @@ impl Validator {
JsonRpcService::new( JsonRpcService::new(
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), rpc_port), SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), rpc_port),
config.rpc_config.clone(), config.rpc_config.clone(),
config.snapshot_config.clone(),
bank_forks.clone(), bank_forks.clone(),
block_commitment_cache.clone(), block_commitment_cache.clone(),
blockstore.clone(), blockstore.clone(),
@ -305,7 +310,13 @@ impl Validator {
bank.tick_height(), bank.tick_height(),
bank.last_blockhash(), bank.last_blockhash(),
bank.slot(), bank.slot(),
leader_schedule_cache.next_leader_slot(&id, bank.slot(), &bank, Some(&blockstore)), leader_schedule_cache.next_leader_slot(
&id,
bank.slot(),
&bank,
Some(&blockstore),
GRACE_TICKS_FACTOR * MAX_GRACE_SLOTS,
),
bank.ticks_per_slot(), bank.ticks_per_slot(),
&id, &id,
&blockstore, &blockstore,
@ -345,9 +356,7 @@ impl Validator {
} }
if let Some(snapshot_hash) = snapshot_hash { if let Some(snapshot_hash) = snapshot_hash {
if let Some(ref trusted_validators) = if let Some(ref trusted_validators) = config.trusted_validators {
config.snapshot_config.as_ref().unwrap().trusted_validators
{
let mut trusted = false; let mut trusted = false;
for _ in 0..10 { for _ in 0..10 {
trusted = cluster_info trusted = cluster_info
@ -562,6 +571,14 @@ fn new_banks_from_blockstore(
error!("Failed to load genesis from {:?}: {}", blockstore_path, err); error!("Failed to load genesis from {:?}: {}", blockstore_path, err);
process::exit(1); process::exit(1);
}); });
// This needs to be limited otherwise the state in the VoteAccount data
// grows too large
let leader_schedule_slot_offset = genesis_config.epoch_schedule.leader_schedule_slot_offset;
let slots_per_epoch = genesis_config.epoch_schedule.slots_per_epoch;
let leader_epoch_offset = (leader_schedule_slot_offset + slots_per_epoch - 1) / slots_per_epoch;
assert!(leader_epoch_offset <= MAX_LEADER_SCHEDULE_EPOCH_OFFSET);
let genesis_hash = genesis_config.hash(); let genesis_hash = genesis_config.hash();
info!("genesis hash: {}", genesis_hash); info!("genesis hash: {}", genesis_hash);
@ -639,74 +656,94 @@ fn wait_for_supermajority(
} }
} }
pub fn new_validator_for_tests() -> (Validator, ContactInfo, Keypair, PathBuf) { pub struct TestValidator {
let (node, contact_info, mint_keypair, ledger_path, _vote_pubkey) = pub server: Validator,
new_validator_for_tests_with_vote_pubkey(); pub leader_data: ContactInfo,
(node, contact_info, mint_keypair, ledger_path) pub alice: Keypair,
pub ledger_path: PathBuf,
pub genesis_hash: Hash,
pub vote_pubkey: Pubkey,
} }
pub fn new_validator_for_tests_with_vote_pubkey( pub struct TestValidatorOptions {
) -> (Validator, ContactInfo, Keypair, PathBuf, Pubkey) { pub fees: u64,
use crate::genesis_utils::BOOTSTRAP_VALIDATOR_LAMPORTS; pub bootstrap_validator_lamports: u64,
new_validator_for_tests_ex(0, BOOTSTRAP_VALIDATOR_LAMPORTS)
} }
pub fn new_validator_for_tests_ex( impl Default for TestValidatorOptions {
fees: u64, fn default() -> Self {
bootstrap_validator_lamports: u64, use crate::genesis_utils::BOOTSTRAP_VALIDATOR_LAMPORTS;
) -> (Validator, ContactInfo, Keypair, PathBuf, Pubkey) { TestValidatorOptions {
use crate::genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo}; fees: 0,
use solana_sdk::fee_calculator::FeeCalculator; bootstrap_validator_lamports: BOOTSTRAP_VALIDATOR_LAMPORTS,
}
}
}
let node_keypair = Arc::new(Keypair::new()); impl TestValidator {
let node = Node::new_localhost_with_pubkey(&node_keypair.pubkey()); pub fn run() -> Self {
let contact_info = node.info.clone(); Self::run_with_options(TestValidatorOptions::default())
}
let GenesisConfigInfo { pub fn run_with_options(options: TestValidatorOptions) -> Self {
mut genesis_config, use crate::genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo};
mint_keypair, use solana_sdk::fee_calculator::FeeCalculator;
voting_keypair,
} = create_genesis_config_with_leader_ex(
1_000_000,
&contact_info.id,
42,
bootstrap_validator_lamports,
);
genesis_config
.native_instruction_processors
.push(solana_budget_program!());
genesis_config.rent.lamports_per_byte_year = 1; let TestValidatorOptions {
genesis_config.rent.exemption_threshold = 1.0; fees,
genesis_config.fee_calculator = FeeCalculator::new(fees, 0); bootstrap_validator_lamports,
} = options;
let node_keypair = Arc::new(Keypair::new());
let node = Node::new_localhost_with_pubkey(&node_keypair.pubkey());
let contact_info = node.info.clone();
let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config); let GenesisConfigInfo {
mut genesis_config,
mint_keypair,
voting_keypair,
} = create_genesis_config_with_leader_ex(
1_000_000,
&contact_info.id,
42,
bootstrap_validator_lamports,
);
genesis_config
.native_instruction_processors
.push(solana_budget_program!());
let leader_voting_keypair = Arc::new(voting_keypair); genesis_config.rent.lamports_per_byte_year = 1;
let storage_keypair = Arc::new(Keypair::new()); genesis_config.rent.exemption_threshold = 1.0;
let config = ValidatorConfig { genesis_config.fee_calculator = FeeCalculator::new(fees, 0);
rpc_ports: Some((node.info.rpc.port(), node.info.rpc_pubsub.port())),
..ValidatorConfig::default() let (ledger_path, blockhash) = create_new_tmp_ledger!(&genesis_config);
};
let node = Validator::new( let leader_voting_keypair = Arc::new(voting_keypair);
node, let storage_keypair = Arc::new(Keypair::new());
&node_keypair, let config = ValidatorConfig {
&ledger_path, rpc_ports: Some((node.info.rpc.port(), node.info.rpc_pubsub.port())),
&leader_voting_keypair.pubkey(), ..ValidatorConfig::default()
&leader_voting_keypair, };
&storage_keypair, let node = Validator::new(
None, node,
true, &node_keypair,
&config, &ledger_path,
); &leader_voting_keypair.pubkey(),
discover_cluster(&contact_info.gossip, 1).expect("Node startup failed"); &leader_voting_keypair,
( &storage_keypair,
node, None,
contact_info, true,
mint_keypair, &config,
ledger_path, );
leader_voting_keypair.pubkey(), discover_cluster(&contact_info.gossip, 1).expect("Node startup failed");
) TestValidator {
server: node,
leader_data: contact_info,
alice: mint_keypair,
ledger_path,
genesis_hash: blockhash,
vote_pubkey: leader_voting_keypair.pubkey(),
}
}
} }
fn report_target_features() { fn report_target_features() {

View File

@ -55,7 +55,6 @@ mod tests {
snapshot_interval_slots, snapshot_interval_slots,
snapshot_package_output_path: PathBuf::from(snapshot_output_path.path()), snapshot_package_output_path: PathBuf::from(snapshot_output_path.path()),
snapshot_path: PathBuf::from(snapshot_dir.path()), snapshot_path: PathBuf::from(snapshot_dir.path()),
trusted_validators: None,
}; };
bank_forks.set_snapshot_config(Some(snapshot_config.clone())); bank_forks.set_snapshot_config(Some(snapshot_config.clone()));
SnapshotTestConfig { SnapshotTestConfig {
@ -68,13 +67,19 @@ mod tests {
} }
} }
fn restore_from_snapshot(old_bank_forks: &BankForks, account_paths: Vec<PathBuf>) { fn restore_from_snapshot(
old_bank_forks: &BankForks,
old_last_slot: Slot,
account_paths: Vec<PathBuf>,
) {
let (snapshot_path, snapshot_package_output_path) = old_bank_forks let (snapshot_path, snapshot_package_output_path) = old_bank_forks
.snapshot_config .snapshot_config
.as_ref() .as_ref()
.map(|c| (&c.snapshot_path, &c.snapshot_package_output_path)) .map(|c| (&c.snapshot_path, &c.snapshot_package_output_path))
.unwrap(); .unwrap();
let old_last_bank = old_bank_forks.get(old_last_slot).unwrap();
let deserialized_bank = snapshot_utils::bank_from_archive( let deserialized_bank = snapshot_utils::bank_from_archive(
&account_paths, &account_paths,
&old_bank_forks &old_bank_forks
@ -82,7 +87,10 @@ mod tests {
.as_ref() .as_ref()
.unwrap() .unwrap()
.snapshot_path, .snapshot_path,
snapshot_utils::get_snapshot_archive_path(snapshot_package_output_path), snapshot_utils::get_snapshot_archive_path(
snapshot_package_output_path,
&(old_last_bank.slot(), old_last_bank.get_accounts_hash()),
),
) )
.unwrap(); .unwrap();
@ -140,18 +148,20 @@ mod tests {
slot_snapshot_paths slot_snapshot_paths
.last() .last()
.expect("no snapshots found in path"), .expect("no snapshots found in path"),
snapshot_utils::get_snapshot_archive_path(
&snapshot_config.snapshot_package_output_path,
),
&snapshot_config.snapshot_path, &snapshot_config.snapshot_path,
&last_bank.src.roots(), &last_bank.src.roots(),
&snapshot_config.snapshot_package_output_path,
storages, storages,
) )
.unwrap(); .unwrap();
snapshot_utils::archive_snapshot_package(&snapshot_package).unwrap(); snapshot_utils::archive_snapshot_package(&snapshot_package).unwrap();
restore_from_snapshot(bank_forks, vec![accounts_dir.path().to_path_buf()]); restore_from_snapshot(
bank_forks,
last_slot,
vec![accounts_dir.path().to_path_buf()],
);
} }
#[test] #[test]
@ -222,9 +232,8 @@ mod tests {
let saved_snapshots_dir = TempDir::new().unwrap(); let saved_snapshots_dir = TempDir::new().unwrap();
let saved_accounts_dir = TempDir::new().unwrap(); let saved_accounts_dir = TempDir::new().unwrap();
let saved_slot = 4; let saved_slot = 4;
let saved_tar = snapshot_config let mut saved_archive_path = None;
.snapshot_package_output_path
.join(saved_slot.to_string());
for forks in 0..MAX_CACHE_ENTRIES + 2 { for forks in 0..MAX_CACHE_ENTRIES + 2 {
let bank = Bank::new_from_parent( let bank = Bank::new_from_parent(
&bank_forks[forks as u64], &bank_forks[forks as u64],
@ -236,6 +245,7 @@ mod tests {
let tx = system_transaction::transfer(&mint_keypair, &key1, 1, genesis_config.hash()); let tx = system_transaction::transfer(&mint_keypair, &key1, 1, genesis_config.hash());
assert_eq!(bank.process_transaction(&tx), Ok(())); assert_eq!(bank.process_transaction(&tx), Ok(()));
bank.squash(); bank.squash();
let accounts_hash = bank.update_accounts_hash();
bank_forks.insert(bank); bank_forks.insert(bank);
let package_sender = { let package_sender = {
@ -250,14 +260,7 @@ mod tests {
}; };
bank_forks bank_forks
.generate_snapshot( .generate_snapshot(slot, &vec![], &package_sender)
slot,
&vec![],
&package_sender,
snapshot_config
.snapshot_package_output_path
.join(slot.to_string()),
)
.unwrap(); .unwrap();
if slot == saved_slot as u64 { if slot == saved_slot as u64 {
@ -283,6 +286,11 @@ mod tests {
&options, &options,
) )
.unwrap(); .unwrap();
saved_archive_path = Some(snapshot_utils::get_snapshot_archive_path(
&snapshot_config.snapshot_package_output_path,
&(slot, accounts_hash),
));
} }
} }
@ -320,7 +328,7 @@ mod tests {
.join() .join()
.expect("SnapshotPackagerService exited with error"); .expect("SnapshotPackagerService exited with error");
// Check the tar we cached the state for earlier was generated correctly // Check the archive we cached the state for earlier was generated correctly
// before we compare, stick an empty status_cache in this dir so that the package comparision works // before we compare, stick an empty status_cache in this dir so that the package comparision works
// This is needed since the status_cache is added by the packager and is not collected from // This is needed since the status_cache is added by the packager and is not collected from
@ -339,7 +347,7 @@ mod tests {
.unwrap(); .unwrap();
snapshot_utils::verify_snapshot_archive( snapshot_utils::verify_snapshot_archive(
saved_tar, saved_archive_path.unwrap(),
saved_snapshots_dir.path(), saved_snapshots_dir.path(),
saved_accounts_dir saved_accounts_dir
.path() .path()

View File

@ -4,7 +4,7 @@ use solana_client::{
}; };
use solana_core::{ use solana_core::{
rpc_pubsub_service::PubSubService, rpc_subscriptions::RpcSubscriptions, rpc_pubsub_service::PubSubService, rpc_subscriptions::RpcSubscriptions,
validator::new_validator_for_tests, validator::TestValidator,
}; };
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, pubkey::Pubkey, rpc_port, signature::Signer, commitment_config::CommitmentConfig, pubkey::Pubkey, rpc_port, signature::Signer,
@ -26,7 +26,13 @@ use systemstat::Ipv4Addr;
fn test_rpc_client() { fn test_rpc_client() {
solana_logger::setup(); solana_logger::setup();
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let client = RpcClient::new_socket(leader_data.rpc); let client = RpcClient::new_socket(leader_data.rpc);

View File

@ -1,6 +1,6 @@
//! Fork Selection Simulation //! Fork Selection Simulation
//! //!
//! Description of the algorithm can be found in [book/src/fork-selection.md](book/src/fork-selection.md). //! Description of the algorithm can be found in [docs/src/fork-selection.md](docs/src/fork-selection.md).
//! //!
//! A test library function exists for configuring networks. //! A test library function exists for configuring networks.
//! ``` //! ```

View File

@ -1,21 +1,38 @@
use bincode::serialize; use bincode::serialize;
use jsonrpc_core::futures::{
future::{self, Future},
stream::Stream,
};
use jsonrpc_core_client::transports::ws;
use log::*; use log::*;
use reqwest::{self, header::CONTENT_TYPE}; use reqwest::{self, header::CONTENT_TYPE};
use serde_json::{json, Value}; use serde_json::{json, Value};
use solana_client::rpc_client::get_rpc_request_str; use solana_client::rpc_client::get_rpc_request_str;
use solana_core::validator::new_validator_for_tests; use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, validator::TestValidator};
use solana_sdk::hash::Hash; use solana_sdk::{hash::Hash, pubkey::Pubkey, system_transaction, transaction};
use solana_sdk::pubkey::Pubkey; use std::{
use solana_sdk::system_transaction; collections::HashSet,
use std::fs::remove_dir_all; fs::remove_dir_all,
use std::thread::sleep; net::UdpSocket,
use std::time::Duration; sync::mpsc::channel,
sync::{Arc, Mutex},
thread::sleep,
time::Duration,
time::SystemTime,
};
use tokio::runtime::Runtime;
#[test] #[test]
fn test_rpc_send_tx() { fn test_rpc_send_tx() {
solana_logger::setup(); solana_logger::setup();
let (server, leader_data, alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
@ -100,7 +117,12 @@ fn test_rpc_send_tx() {
fn test_rpc_invalid_requests() { fn test_rpc_invalid_requests() {
solana_logger::setup(); solana_logger::setup();
let (server, leader_data, _alice, ledger_path) = new_validator_for_tests(); let TestValidator {
server,
leader_data,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
// test invalid get_balance request // test invalid get_balance request
@ -166,3 +188,82 @@ fn test_rpc_invalid_requests() {
server.close().unwrap(); server.close().unwrap();
remove_dir_all(ledger_path).unwrap(); remove_dir_all(ledger_path).unwrap();
} }
#[test]
fn test_rpc_subscriptions() {
solana_logger::setup();
let TestValidator {
server,
leader_data,
alice,
ledger_path,
genesis_hash,
..
} = TestValidator::run();
// Create transaction signatures to subscribe to
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut signature_set: HashSet<String> = (0..1000)
.map(|_| {
let tx = system_transaction::transfer(&alice, &Pubkey::new_rand(), 1, genesis_hash);
transactions_socket
.send_to(&bincode::serialize(&tx).unwrap(), leader_data.tpu)
.unwrap();
tx.signatures[0].to_string()
})
.collect();
// Create the pub sub runtime
let mut rt = Runtime::new().unwrap();
let rpc_pubsub_url = format!("ws://{}/", leader_data.rpc_pubsub);
let (sender, receiver) = channel::<(String, transaction::Result<()>)>();
let sender = Arc::new(Mutex::new(sender));
rt.spawn({
let connect = ws::try_connect::<PubsubClient>(&rpc_pubsub_url).unwrap();
let signature_set = signature_set.clone();
connect
.and_then(move |client| {
for sig in signature_set {
let sender = sender.clone();
tokio::spawn(
client
.signature_subscribe(sig.clone(), None)
.and_then(move |sig_stream| {
sig_stream.for_each(move |result| {
sender.lock().unwrap().send((sig.clone(), result)).unwrap();
future::ok(())
})
})
.map_err(|err| {
eprintln!("sig sub err: {:#?}", err);
}),
);
}
future::ok(())
})
.map_err(|_| ())
});
// Wait for all signature subscriptions
let now = SystemTime::now();
let timeout = Duration::from_secs(5);
while !signature_set.is_empty() {
assert!(now.elapsed().unwrap() < timeout);
match receiver.recv_timeout(Duration::from_secs(1)) {
Ok((sig, result)) => {
assert!(result.is_ok());
assert!(signature_set.remove(&sig));
}
Err(_err) => {
eprintln!("unexpected receive timeout");
assert!(false)
}
}
}
rt.shutdown_now().wait().unwrap();
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-crate-features" name = "solana-crate-features"
version = "1.0.0" version = "1.0.1"
description = "Solana Crate Features" description = "Solana Crate Features"
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"

View File

@ -1,7 +1,7 @@
Building the Solana book Building the Solana book
--- ---
Install the book's dependencies, build, and test the book: Install dependencies, build, and test the docs:
```bash ```bash
$ ./build.sh $ ./build.sh
@ -19,7 +19,7 @@ Render markdown as HTML:
$ make build $ make build
``` ```
Render and view the book: Render and view the docs:
```bash ```bash
$ make open $ make open

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 542 KiB

After

Width:  |  Height:  |  Size: 542 KiB

View File

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

View File

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

View File

@ -3,6 +3,8 @@
* [Introduction](introduction.md) * [Introduction](introduction.md)
* [Using Solana from the Command-line](cli/README.md) * [Using Solana from the Command-line](cli/README.md)
* [Command-line Usage](cli/usage.md) * [Command-line Usage](cli/usage.md)
* [Remote Wallet](remote-wallet/README.md)
* [Ledger Hardware Wallet](remote-wallet/ledger.md)
* [Paper Wallet](paper-wallet/README.md) * [Paper Wallet](paper-wallet/README.md)
* [Installation](paper-wallet/installation.md) * [Installation](paper-wallet/installation.md)
* [Paper Wallet Usage](paper-wallet/usage.md) * [Paper Wallet Usage](paper-wallet/usage.md)
@ -15,6 +17,27 @@
* [Anatomy of a Transaction](transaction.md) * [Anatomy of a Transaction](transaction.md)
* [JSON RPC API](apps/jsonrpc-api.md) * [JSON RPC API](apps/jsonrpc-api.md)
* [JavaScript API](apps/javascript-api.md) * [JavaScript API](apps/javascript-api.md)
* [Participating in Tour de SOL](tour-de-sol/README.md)
* [Useful Links & Discussion](tour-de-sol/useful-links.md)
* [Registration](tour-de-sol/registration/README.md)
* [How To Register](tour-de-sol/registration/how-to-register.md)
* [Terms of Participation](tour-de-sol/registration/terms-of-participation.md)
* [Rewards](tour-de-sol/registration/rewards.md)
* [Confidentiality](tour-de-sol/registration/confidentiality.md)
* [Registration FAQ](tour-de-sol/registration/validator-registration-and-rewards-faq.md)
* [Participation](tour-de-sol/participation/README.md)
* [Requirements to run a validator](tour-de-sol/participation/validator-technical-requirements.md)
* [Steps to create a validator](tour-de-sol/participation/steps-to-create-a-validator/README.md)
* [Install the Solana software](tour-de-sol/participation/steps-to-create-a-validator/install-the-solana-software.md)
* [Create a validator public key](tour-de-sol/participation/steps-to-create-a-validator/validator-public-key-registration.md)
* [Create and configure a Solana validator](tour-de-sol/participation/steps-to-create-a-validator/connecting-your-validator.md)
* [Confirm the Solana network is running](tour-de-sol/participation/steps-to-create-a-validator/confirm-the-solana-network-is-running.md)
* [Connect to the Solana network](tour-de-sol/participation/steps-to-create-a-validator/connect-to-the-solana-network.md)
* [Validator catch-up](tour-de-sol/participation/steps-to-create-a-validator/monitoring-your-validator.md)
* [Staking](tour-de-sol/participation/steps-to-create-a-validator/delegating-stake.md)
* [Publish information about your validator](tour-de-sol/participation/steps-to-create-a-validator/publishing-information-about-your-validator.md)
* [Dry Run 6](tour-de-sol/participation/dry-run-6.md)
* [Submitting Bugs](tour-de-sol/submitting-bugs.md)
* [Running a Validator](running-validator/README.md) * [Running a Validator](running-validator/README.md)
* [Validator Requirements](running-validator/validator-reqs.md) * [Validator Requirements](running-validator/validator-reqs.md)
* [Choosing a Testnet](running-validator/validator-testnet.md) * [Choosing a Testnet](running-validator/validator-testnet.md)

View File

@ -1,6 +1,6 @@
# Drones # Drones
This chapter defines an off-chain service called a _drone_, which acts as custodian of a user's private key. In its simplest form, it can be used to create _airdrop_ transactions, a token transfer from the drone's account to a client's account. This section defines an off-chain service called a _drone_, which acts as custodian of a user's private key. In its simplest form, it can be used to create _airdrop_ transactions, a token transfer from the drone's account to a client's account.
## Signing Service ## Signing Service

View File

@ -314,13 +314,13 @@ The result field will be an object with the following fields:
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "json"]}' localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "json"]}' localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1} {"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"fee":1.0.1,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
// Request // Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "binary"]}' localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "binary"]}' localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1} {"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"fee":1.0.1,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
``` ```
### getConfirmedBlocks ### getConfirmedBlocks
@ -579,7 +579,7 @@ An RpcResponse containing a JSON object consisting of a string blockhash and Fee
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"}' http://localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"}' http://localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"burnPercent":50,"lamportsPerSignature":5000,"maxLamportsPerSignature":100000,"minLamportsPerSignature":5000,"targetLamportsPerSignature":10000,"targetSignaturesPerSlot":20000}}},"id":1} {"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"burnPercent":50,"lamportsPerSignature":5000,"maxLamportsPerSignature":1.0.10,"minLamportsPerSignature":5000,"targetLamportsPerSignature":1.0.1,"targetSignaturesPerSlot":20000}}},"id":1}
``` ```
### getSignatureConfirmation ### getSignatureConfirmation
@ -830,7 +830,7 @@ The result field will be a JSON object with the following fields:
// Request // Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"solana-core": "1.0.0"},"id":1} {"jsonrpc":"2.0","result":{"solana-core": "1.0.1"},"id":1}
``` ```
### getVoteAccounts ### getVoteAccounts

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