Compare commits
358 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cb84099b2e | ||
|
c89b35545c | ||
|
370716edd3 | ||
|
1dbcd5c298 | ||
|
ca770d5e74 | ||
|
51a8d0356f | ||
|
79e340c499 | ||
|
00f92f520f | ||
|
b90049aafb | ||
|
c1d66b46fa | ||
|
c377d1cbbd | ||
|
bbdb4129cf | ||
|
0ecf823986 | ||
|
43ac961637 | ||
|
57e6213528 | ||
|
acafb89ff2 | ||
|
ec319a6043 | ||
|
f389d434f8 | ||
|
1b600a7f37 | ||
|
798b457b27 | ||
|
132d012842 | ||
|
e16f9ad961 | ||
|
66f006108c | ||
|
47f887bda0 | ||
|
bb64c73aa2 | ||
|
1f30d1e77a | ||
|
04dab9b274 | ||
|
fb4e102670 | ||
|
67e0ba0356 | ||
|
22bb4e6462 | ||
|
79035bdbed | ||
|
70089a5258 | ||
|
34238d5f1e | ||
|
cab6917cbd | ||
|
2951ee5b1d | ||
|
fb16a15900 | ||
|
76b52f4c5d | ||
|
21a2e643c2 | ||
|
733d9cb026 | ||
|
2f54f57b7a | ||
|
7bd95019ef | ||
|
33557c3271 | ||
|
c65b9cd88d | ||
|
038db8167f | ||
|
030498ced5 | ||
|
28eb8b662a | ||
|
de752eaf80 | ||
|
9c5ef19d80 | ||
|
235bd0a46b | ||
|
465d71a3a3 | ||
|
14e6029fae | ||
|
75434158ee | ||
|
1cae9fd893 | ||
|
bea34a812c | ||
|
41a28d7322 | ||
|
235158d2bc | ||
|
521238f7d7 | ||
|
384f52a607 | ||
|
49f2d912ab | ||
|
8652fe30ce | ||
|
899a14ba51 | ||
|
466c7dafb3 | ||
|
293bb63ed8 | ||
|
8f8fb720af | ||
|
19f414d843 | ||
|
eaca1c3170 | ||
|
9fc75925f9 | ||
|
b5098ac87c | ||
|
e23aec9728 | ||
|
57d490c84f | ||
|
aa8c9f6a98 | ||
|
57772dc73d | ||
|
21706108e8 | ||
|
50d0caf00f | ||
|
2739332306 | ||
|
c85c4699aa | ||
|
81add4d6bf | ||
|
8e31eeb696 | ||
|
e1ce8b37ff | ||
|
3f831c05f5 | ||
|
f0d7ce6bb6 | ||
|
6ba95b2545 | ||
|
6818e68542 | ||
|
43659d7deb | ||
|
f24d8e7d2d | ||
|
e10fe5e125 | ||
|
0f8c9ab1c4 | ||
|
8a9a9cb991 | ||
|
44208ffa67 | ||
|
5df0478fa3 | ||
|
d52567933e | ||
|
a32cdb9f4d | ||
|
eacd8d986c | ||
|
1d32603b49 | ||
|
8c6f7ee5a4 | ||
|
be482eed3f | ||
|
6e1c53cb0f | ||
|
af92f205cf | ||
|
87047b08c8 | ||
|
e282161872 | ||
|
01b1e287ed | ||
|
d7fd1fa467 | ||
|
bfa34cd494 | ||
|
915835e224 | ||
|
659332e7ac | ||
|
272986c6ac | ||
|
4d8ab45c56 | ||
|
932ae86d47 | ||
|
756e6334b0 | ||
|
4e6eca9748 | ||
|
d9e37eb30c | ||
|
04d1b35926 | ||
|
d13d609050 | ||
|
20426cf251 | ||
|
4a220d7c8e | ||
|
436eab41ca | ||
|
c8472d0a96 | ||
|
1a7db9c17e | ||
|
b468d9f17c | ||
|
41cf1d7d23 | ||
|
e2570c98ee | ||
|
b5125479ec | ||
|
989355e885 | ||
|
a2f2c46f87 | ||
|
605623baf5 | ||
|
fdc452c536 | ||
|
1b391dd36b | ||
|
917067741a | ||
|
34ed93d57c | ||
|
d400a64b9a | ||
|
2c7447b73e | ||
|
c0f0fa24f8 | ||
|
bda5f949bb | ||
|
992e985972 | ||
|
afaa359b0d | ||
|
3c17db41dc | ||
|
d62ed4f6b3 | ||
|
79f3194d0c | ||
|
b045f9a50d | ||
|
ce231602dc | ||
|
6f5e0cd161 | ||
|
1269a79a4d | ||
|
1b3424ff61 | ||
|
8b8033c72b | ||
|
7ca0109732 | ||
|
6b5172d002 | ||
|
9e19a635bb | ||
|
15193d0e1f | ||
|
f1c5c72e62 | ||
|
25dfed207c | ||
|
006cbee88a | ||
|
c95e5346a4 | ||
|
e54bf563b5 | ||
|
8f79327190 | ||
|
a197ac092a | ||
|
1e2b55c0d7 | ||
|
964ff522be | ||
|
934c32cbc6 | ||
|
9bd6be779f | ||
|
ce70d6eedc | ||
|
3a0d13aa77 | ||
|
f9323c5273 | ||
|
7587656cf6 | ||
|
023074650f | ||
|
d854e90c23 | ||
|
3aabeb2b81 | ||
|
65f5885bce | ||
|
7a132eabb4 | ||
|
7e1b380f01 | ||
|
1a2d9b8eed | ||
|
6eefa0b72d | ||
|
44372db955 | ||
|
e24cce4aed | ||
|
a8595c0418 | ||
|
340424e03a | ||
|
93036bec01 | ||
|
663e98969d | ||
|
37d1daf58e | ||
|
1a18f0ca55 | ||
|
bb950ec93e | ||
|
39ab3557a3 | ||
|
dcdc46b97c | ||
|
da3ed0dfb3 | ||
|
e391b9fb90 | ||
|
e346cdad26 | ||
|
7e4c6ff218 | ||
|
356f246a74 | ||
|
80da552834 | ||
|
2dd8ab197d | ||
|
1fe11e9ae2 | ||
|
21d5fe6272 | ||
|
52bc4a3598 | ||
|
cccaacee36 | ||
|
ebf6e1c0e9 | ||
|
5cf090c896 | ||
|
cc299053cc | ||
|
82b75796f9 | ||
|
a560d94a9f | ||
|
0827d52c6f | ||
|
a8d33c9950 | ||
|
43c32ea280 | ||
|
30d40e9a32 | ||
|
e28508ad56 | ||
|
182e4cec86 | ||
|
a32de96ab1 | ||
|
0de35fdd1f | ||
|
470d9cd752 | ||
|
87598c7612 | ||
|
57bf618627 | ||
|
c576a707b0 | ||
|
b78b1bbfa9 | ||
|
e710964d05 | ||
|
2d00657756 | ||
|
0526d4ff21 | ||
|
76e20015a4 | ||
|
f5e797e3aa | ||
|
787e36a28f | ||
|
8572b57834 | ||
|
ed0129f881 | ||
|
78836a9e22 | ||
|
4c08184379 | ||
|
da165d6943 | ||
|
8ffccfbaff | ||
|
a6d083d69d | ||
|
91bae9d510 | ||
|
f0f185509f | ||
|
5947ef7706 | ||
|
4f663a2a86 | ||
|
1d01777a13 | ||
|
6d3b8b6d7d | ||
|
50c1c08235 | ||
|
b16c30b4c6 | ||
|
ff1ca1e0d3 | ||
|
721c4378c1 | ||
|
5f4e0c7e3e | ||
|
e6af4511a8 | ||
|
965ad778dd | ||
|
3b78be83cf | ||
|
564cd4e09d | ||
|
699ca5fec1 | ||
|
f91ffbbfdf | ||
|
156292e408 | ||
|
81ae44f858 | ||
|
c948814eae | ||
|
b5dba77056 | ||
|
ef06d165b4 | ||
|
5cb23c814d | ||
|
8f7ded33e0 | ||
|
a17d5795fb | ||
|
ad4d41e602 | ||
|
9754fc789e | ||
|
fd3c6eb320 | ||
|
b7b68ecdba | ||
|
08ba27627d | ||
|
27d2c0aaf3 | ||
|
b714a4be63 | ||
|
2356b25c58 | ||
|
05cad05505 | ||
|
1e3082fbc0 | ||
|
80d2573b10 | ||
|
6adcdc41f4 | ||
|
2d08dddfc8 | ||
|
6da8f49d8b | ||
|
bcd072c5e8 | ||
|
e90a31781c | ||
|
2e89ec9105 | ||
|
865c42465a | ||
|
73c93cc345 | ||
|
cf32fdf672 | ||
|
c33b54794c | ||
|
6775e83420 | ||
|
719785a8d3 | ||
|
287995ffdf | ||
|
0e506a53b5 | ||
|
70e1a15973 | ||
|
09cff5e4cc | ||
|
57858b8015 | ||
|
07855e3125 | ||
|
2f5f8e7afd | ||
|
43897de12e | ||
|
4b577aa77b | ||
|
85c3d64f29 | ||
|
47dd293904 | ||
|
c4220a4853 | ||
|
48ab88a2af | ||
|
d9cf9709d2 | ||
|
9720c894f1 | ||
|
8dad3af36d | ||
|
e5425d4a27 | ||
|
58e6d4aabb | ||
|
9ce142606c | ||
|
e75a64a8a2 | ||
|
bc71e1b612 | ||
|
580ca36a62 | ||
|
447fe48d2a | ||
|
e8a6c8cd6d | ||
|
a8fd42c1df | ||
|
e782c26908 | ||
|
cd65a1e172 | ||
|
6e51c5685e | ||
|
84a37a2c0c | ||
|
7e94cc2cc3 | ||
|
7002ccb866 | ||
|
4fe0b116ae | ||
|
a0fb9de515 | ||
|
5d42dcc9ec | ||
|
96e88c90e8 | ||
|
75d94240ed | ||
|
6c544708e1 | ||
|
078e7246ac | ||
|
06cff1fb9f | ||
|
2e8bbed75b | ||
|
a707c9410e | ||
|
a956bb08d8 | ||
|
db52cc6749 | ||
|
73c6224a95 | ||
|
a217920561 | ||
|
48a36f59a6 | ||
|
965b132664 | ||
|
63f185f9bf | ||
|
e97b0088f2 | ||
|
374c17a0d9 | ||
|
4b3bc587ab | ||
|
06c63f2026 | ||
|
6b7d9942a7 | ||
|
760a56964f | ||
|
6ca575b5a3 | ||
|
ce1d36cacb | ||
|
87b2525e03 | ||
|
faa77aca2e | ||
|
5d2158792c | ||
|
e1ebaa902b | ||
|
e0564f628e | ||
|
44e45aa090 | ||
|
89f5f336af | ||
|
727be309b2 | ||
|
ce2d7a2d5a | ||
|
fad6c7201e | ||
|
8f0e1f3349 | ||
|
6f7d0c6928 | ||
|
120c8f244c | ||
|
352a367570 | ||
|
9f65d22909 | ||
|
141131f3a6 | ||
|
488420fdf2 | ||
|
10e6b8f769 | ||
|
419da18405 | ||
|
7329d4bf3a | ||
|
c8fe4043b6 | ||
|
3d133d61ca | ||
|
d51e42c707 | ||
|
79e39d6f0b | ||
|
7dec934bb3 | ||
|
83f866df01 | ||
|
d88d8e2dbb | ||
|
3a40dff999 | ||
|
3f69d58498 | ||
|
ca10cf081f |
16
.mergify.yml
16
.mergify.yml
@@ -19,14 +19,6 @@ pull_request_rules:
|
|||||||
label:
|
label:
|
||||||
add:
|
add:
|
||||||
- automerge
|
- automerge
|
||||||
- name: v0.21 backport
|
|
||||||
conditions:
|
|
||||||
- base=master
|
|
||||||
- label=v0.21
|
|
||||||
actions:
|
|
||||||
backport:
|
|
||||||
branches:
|
|
||||||
- v0.21
|
|
||||||
- name: v0.22 backport
|
- name: v0.22 backport
|
||||||
conditions:
|
conditions:
|
||||||
- base=master
|
- base=master
|
||||||
@@ -43,3 +35,11 @@ pull_request_rules:
|
|||||||
backport:
|
backport:
|
||||||
branches:
|
branches:
|
||||||
- v0.23
|
- v0.23
|
||||||
|
- name: v0.24 backport
|
||||||
|
conditions:
|
||||||
|
- base=master
|
||||||
|
- label=v0.24
|
||||||
|
actions:
|
||||||
|
backport:
|
||||||
|
branches:
|
||||||
|
- v0.24
|
||||||
|
1521
Cargo.lock
generated
1521
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,10 @@ members = [
|
|||||||
"bench-streamer",
|
"bench-streamer",
|
||||||
"bench-tps",
|
"bench-tps",
|
||||||
"banking-bench",
|
"banking-bench",
|
||||||
|
"chacha",
|
||||||
|
"chacha-cuda",
|
||||||
"chacha-sys",
|
"chacha-sys",
|
||||||
|
"cli-config",
|
||||||
"client",
|
"client",
|
||||||
"core",
|
"core",
|
||||||
"faucet",
|
"faucet",
|
||||||
@@ -38,6 +41,9 @@ members = [
|
|||||||
"programs/vest",
|
"programs/vest",
|
||||||
"programs/vote",
|
"programs/vote",
|
||||||
"archiver",
|
"archiver",
|
||||||
|
"archiver-lib",
|
||||||
|
"archiver-utils",
|
||||||
|
"remote-wallet",
|
||||||
"runtime",
|
"runtime",
|
||||||
"sdk",
|
"sdk",
|
||||||
"sdk-c",
|
"sdk-c",
|
||||||
@@ -45,7 +51,6 @@ members = [
|
|||||||
"sys-tuner",
|
"sys-tuner",
|
||||||
"upload-perf",
|
"upload-perf",
|
||||||
"net-utils",
|
"net-utils",
|
||||||
"fixed-buf",
|
|
||||||
"vote-signer",
|
"vote-signer",
|
||||||
"cli",
|
"cli",
|
||||||
"rayon-threadlimit",
|
"rayon-threadlimit",
|
||||||
|
19
RELEASE.md
19
RELEASE.md
@@ -140,25 +140,6 @@ TODO: Documentation update procedure is WIP as we move to gitbook
|
|||||||
|
|
||||||
Document the new recommended version by updating `book/src/running-archiver.md` and `book/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
|
Document the new recommended version by updating `book/src/running-archiver.md` and `book/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
|
||||||
|
|
||||||
#### Publish updated Book
|
|
||||||
We maintain three copies of the "book" as official documentation:
|
|
||||||
|
|
||||||
1) "Book" is the documentation for the latest official release. This should get manually updated whenever a new release is made. It is published here:
|
|
||||||
https://solana-labs.github.io/book/
|
|
||||||
|
|
||||||
2) "Book-edge" tracks the tip of the master branch and updates automatically.
|
|
||||||
https://solana-labs.github.io/book-edge/
|
|
||||||
|
|
||||||
3) "Book-beta" tracks the tip of the beta branch and updates automatically.
|
|
||||||
https://solana-labs.github.io/book-beta/
|
|
||||||
|
|
||||||
To manually trigger an update of the "Book", create a new job of the manual-update-book pipeline.
|
|
||||||
Set the tag of the latest release as the PUBLISH_BOOK_TAG environment variable.
|
|
||||||
```bash
|
|
||||||
PUBLISH_BOOK_TAG=v0.16.6
|
|
||||||
```
|
|
||||||
https://buildkite.com/solana-labs/manual-update-book
|
|
||||||
|
|
||||||
### Update software on testnet.solana.com
|
### Update software on testnet.solana.com
|
||||||
|
|
||||||
The testnet running on testnet.solana.com is set to use a fixed release tag
|
The testnet running on testnet.solana.com is set to use a fixed release tag
|
||||||
|
39
archiver-lib/Cargo.toml
Normal file
39
archiver-lib/Cargo.toml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
[package]
|
||||||
|
name = "solana-archiver-lib"
|
||||||
|
version = "0.23.5"
|
||||||
|
description = "Solana Archiver Library"
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
homepage = "https://solana.com/"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bincode = "1.2.1"
|
||||||
|
crossbeam-channel = "0.3"
|
||||||
|
ed25519-dalek = "=1.0.0-pre.1"
|
||||||
|
log = "0.4.8"
|
||||||
|
rand = "0.6.5"
|
||||||
|
rand_chacha = "0.1.1"
|
||||||
|
solana-client = { path = "../client", version = "0.23.5" }
|
||||||
|
solana-storage-program = { path = "../programs/storage", version = "0.23.5" }
|
||||||
|
thiserror = "1.0"
|
||||||
|
serde = "1.0.104"
|
||||||
|
serde_json = "1.0.44"
|
||||||
|
serde_derive = "1.0.103"
|
||||||
|
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||||
|
solana-chacha = { path = "../chacha", version = "0.23.5" }
|
||||||
|
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.5" }
|
||||||
|
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||||
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
|
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||||
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
|
solana-core = { path = "../core", version = "0.23.5" }
|
||||||
|
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.5" }
|
||||||
|
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex = "0.4.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "solana_archiver_lib"
|
@@ -1,26 +1,27 @@
|
|||||||
use crate::{
|
use crate::result::ArchiverError;
|
||||||
chacha::{chacha_cbc_encrypt_ledger, CHACHA_BLOCK_SIZE},
|
use crossbeam_channel::unbounded;
|
||||||
|
use rand::{thread_rng, Rng, SeedableRng};
|
||||||
|
use rand_chacha::ChaChaRng;
|
||||||
|
use solana_archiver_utils::sample_file;
|
||||||
|
use solana_chacha::chacha::{chacha_cbc_encrypt_ledger, CHACHA_BLOCK_SIZE};
|
||||||
|
use solana_client::{
|
||||||
|
rpc_client::RpcClient, rpc_request::RpcRequest, rpc_response::RpcStorageTurn,
|
||||||
|
thin_client::ThinClient,
|
||||||
|
};
|
||||||
|
use solana_core::{
|
||||||
cluster_info::{ClusterInfo, Node, VALIDATOR_PORT_RANGE},
|
cluster_info::{ClusterInfo, Node, VALIDATOR_PORT_RANGE},
|
||||||
contact_info::ContactInfo,
|
contact_info::ContactInfo,
|
||||||
gossip_service::GossipService,
|
gossip_service::GossipService,
|
||||||
packet::{limited_deserialize, PACKET_DATA_SIZE},
|
packet::{limited_deserialize, PACKET_DATA_SIZE},
|
||||||
repair_service,
|
repair_service,
|
||||||
repair_service::{RepairService, RepairSlotRange, RepairStrategy},
|
repair_service::{RepairService, RepairSlotRange, RepairStrategy},
|
||||||
result::{Error, Result},
|
serve_repair::ServeRepair,
|
||||||
shred_fetch_stage::ShredFetchStage,
|
shred_fetch_stage::ShredFetchStage,
|
||||||
sigverify_stage::{DisabledSigVerifier, SigVerifyStage},
|
sigverify_stage::{DisabledSigVerifier, SigVerifyStage},
|
||||||
storage_stage::NUM_STORAGE_SAMPLES,
|
storage_stage::NUM_STORAGE_SAMPLES,
|
||||||
streamer::{receiver, responder, PacketReceiver},
|
streamer::{receiver, responder, PacketReceiver},
|
||||||
window_service::WindowService,
|
window_service::WindowService,
|
||||||
};
|
};
|
||||||
use crossbeam_channel::unbounded;
|
|
||||||
use ed25519_dalek;
|
|
||||||
use rand::{thread_rng, Rng, SeedableRng};
|
|
||||||
use rand_chacha::ChaChaRng;
|
|
||||||
use solana_client::{
|
|
||||||
rpc_client::RpcClient, rpc_request::RpcRequest, rpc_response::RpcStorageTurn,
|
|
||||||
thin_client::ThinClient,
|
|
||||||
};
|
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
blockstore::Blockstore, leader_schedule_cache::LeaderScheduleCache, shred::Shred,
|
blockstore::Blockstore, leader_schedule_cache::LeaderScheduleCache, shred::Shred,
|
||||||
};
|
};
|
||||||
@@ -29,11 +30,11 @@ use solana_perf::packet::Packets;
|
|||||||
use solana_perf::recycler::Recycler;
|
use solana_perf::recycler::Recycler;
|
||||||
use solana_sdk::packet::Packet;
|
use solana_sdk::packet::Packet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::State,
|
account_utils::StateMut,
|
||||||
client::{AsyncClient, SyncClient},
|
client::{AsyncClient, SyncClient},
|
||||||
clock::{get_complete_segment_from_slot, get_segment_from_slot, Slot},
|
clock::{get_complete_segment_from_slot, get_segment_from_slot, Slot},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
hash::{Hash, Hasher},
|
hash::Hash,
|
||||||
message::Message,
|
message::Message,
|
||||||
signature::{Keypair, KeypairUtil, Signature},
|
signature::{Keypair, KeypairUtil, Signature},
|
||||||
timing::timestamp,
|
timing::timestamp,
|
||||||
@@ -45,9 +46,7 @@ use solana_storage_program::{
|
|||||||
storage_instruction::{self, StorageAccountType},
|
storage_instruction::{self, StorageAccountType},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
io::{self, ErrorKind},
|
||||||
io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom},
|
|
||||||
mem::size_of,
|
|
||||||
net::{SocketAddr, UdpSocket},
|
net::{SocketAddr, UdpSocket},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
result,
|
result,
|
||||||
@@ -58,6 +57,8 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, ArchiverError>;
|
||||||
|
|
||||||
static ENCRYPTED_FILENAME: &str = "ledger.enc";
|
static ENCRYPTED_FILENAME: &str = "ledger.enc";
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -85,47 +86,12 @@ struct ArchiverMeta {
|
|||||||
client_commitment: CommitmentConfig,
|
client_commitment: CommitmentConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> {
|
|
||||||
let in_file = File::open(in_path)?;
|
|
||||||
let metadata = in_file.metadata()?;
|
|
||||||
let mut buffer_file = BufReader::new(in_file);
|
|
||||||
|
|
||||||
let mut hasher = Hasher::default();
|
|
||||||
let sample_size = size_of::<Hash>();
|
|
||||||
let sample_size64 = sample_size as u64;
|
|
||||||
let mut buf = vec![0; sample_size];
|
|
||||||
|
|
||||||
let file_len = metadata.len();
|
|
||||||
if file_len < sample_size64 {
|
|
||||||
return Err(io::Error::new(ErrorKind::Other, "file too short!"));
|
|
||||||
}
|
|
||||||
for offset in sample_offsets {
|
|
||||||
if *offset > (file_len - sample_size64) / sample_size64 {
|
|
||||||
return Err(io::Error::new(ErrorKind::Other, "offset too large"));
|
|
||||||
}
|
|
||||||
buffer_file.seek(SeekFrom::Start(*offset * sample_size64))?;
|
|
||||||
trace!("sampling @ {} ", *offset);
|
|
||||||
match buffer_file.read(&mut buf) {
|
|
||||||
Ok(size) => {
|
|
||||||
assert_eq!(size, buf.len());
|
|
||||||
hasher.hash(&buf);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error sampling file");
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(hasher.result())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_slot_from_signature(
|
fn get_slot_from_signature(
|
||||||
signature: &ed25519_dalek::Signature,
|
signature: &Signature,
|
||||||
storage_turn: u64,
|
storage_turn: u64,
|
||||||
slots_per_segment: u64,
|
slots_per_segment: u64,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let signature_vec = signature.to_bytes();
|
let signature_vec = signature.as_ref();
|
||||||
let mut segment_index = u64::from(signature_vec[0])
|
let mut segment_index = u64::from(signature_vec[0])
|
||||||
| (u64::from(signature_vec[1]) << 8)
|
| (u64::from(signature_vec[1]) << 8)
|
||||||
| (u64::from(signature_vec[1]) << 16)
|
| (u64::from(signature_vec[1]) << 16)
|
||||||
@@ -229,26 +195,20 @@ impl Archiver {
|
|||||||
Blockstore::open(ledger_path).expect("Expected to be able to open database ledger"),
|
Blockstore::open(ledger_path).expect("Expected to be able to open database ledger"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gossip_service = GossipService::new(
|
let gossip_service = GossipService::new(&cluster_info, None, node.sockets.gossip, &exit);
|
||||||
&cluster_info,
|
|
||||||
Some(blockstore.clone()),
|
|
||||||
None,
|
|
||||||
node.sockets.gossip,
|
|
||||||
&exit,
|
|
||||||
);
|
|
||||||
|
|
||||||
info!("Connecting to the cluster via {:?}", cluster_entrypoint);
|
info!("Connecting to the cluster via {:?}", cluster_entrypoint);
|
||||||
let (nodes, _) =
|
let (nodes, _) =
|
||||||
match crate::gossip_service::discover_cluster(&cluster_entrypoint.gossip, 1) {
|
match solana_core::gossip_service::discover_cluster(&cluster_entrypoint.gossip, 1) {
|
||||||
Ok(nodes_and_archivers) => nodes_and_archivers,
|
Ok(nodes_and_archivers) => nodes_and_archivers,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
//shutdown services before exiting
|
//shutdown services before exiting
|
||||||
exit.store(true, Ordering::Relaxed);
|
exit.store(true, Ordering::Relaxed);
|
||||||
gossip_service.join()?;
|
gossip_service.join()?;
|
||||||
return Err(Error::from(e));
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let client = crate::gossip_service::get_client(&nodes);
|
let client = solana_core::gossip_service::get_client(&nodes);
|
||||||
|
|
||||||
info!("Setting up mining account...");
|
info!("Setting up mining account...");
|
||||||
if let Err(e) = Self::setup_mining_account(
|
if let Err(e) = Self::setup_mining_account(
|
||||||
@@ -411,7 +371,7 @@ impl Archiver {
|
|||||||
client_commitment: CommitmentConfig,
|
client_commitment: CommitmentConfig,
|
||||||
) {
|
) {
|
||||||
let nodes = cluster_info.read().unwrap().tvu_peers();
|
let nodes = cluster_info.read().unwrap().tvu_peers();
|
||||||
let client = crate::gossip_service::get_client(&nodes);
|
let client = solana_core::gossip_service::get_client(&nodes);
|
||||||
|
|
||||||
if let Ok(Some(account)) =
|
if let Ok(Some(account)) =
|
||||||
client.get_account_with_commitment(&storage_keypair.pubkey(), client_commitment.clone())
|
client.get_account_with_commitment(&storage_keypair.pubkey(), client_commitment.clone())
|
||||||
@@ -477,13 +437,13 @@ impl Archiver {
|
|||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let signature = storage_keypair.sign(segment_blockhash.as_ref());
|
let signature = storage_keypair.sign_message(segment_blockhash.as_ref());
|
||||||
let slot = get_slot_from_signature(&signature, segment_slot, slots_per_segment);
|
let slot = get_slot_from_signature(&signature, segment_slot, slots_per_segment);
|
||||||
info!("replicating slot: {}", slot);
|
info!("replicating slot: {}", slot);
|
||||||
slot_sender.send(slot)?;
|
slot_sender.send(slot)?;
|
||||||
meta.slot = slot;
|
meta.slot = slot;
|
||||||
meta.slots_per_segment = slots_per_segment;
|
meta.slots_per_segment = slots_per_segment;
|
||||||
meta.signature = Signature::new(&signature.to_bytes());
|
meta.signature = signature;
|
||||||
meta.blockhash = segment_blockhash;
|
meta.blockhash = segment_blockhash;
|
||||||
|
|
||||||
let mut repair_slot_range = RepairSlotRange::default();
|
let mut repair_slot_range = RepairSlotRange::default();
|
||||||
@@ -624,9 +584,7 @@ impl Archiver {
|
|||||||
client_commitment.clone(),
|
client_commitment.clone(),
|
||||||
)? == 0
|
)? == 0
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(ArchiverError::EmptyStorageAccountBalance);
|
||||||
io::Error::new(io::ErrorKind::Other, "keypair account has no balance").into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("checking storage account keypair...");
|
info!("checking storage account keypair...");
|
||||||
@@ -637,11 +595,8 @@ impl Archiver {
|
|||||||
let blockhash =
|
let blockhash =
|
||||||
match client.get_recent_blockhash_with_commitment(client_commitment.clone()) {
|
match client.get_recent_blockhash_with_commitment(client_commitment.clone()) {
|
||||||
Ok((blockhash, _)) => blockhash,
|
Ok((blockhash, _)) => blockhash,
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
return Err(Error::IO(<io::Error>::new(
|
return Err(ArchiverError::TransportError(e));
|
||||||
io::ErrorKind::Other,
|
|
||||||
"unable to get recent blockhash, can't submit proof",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -675,7 +630,7 @@ impl Archiver {
|
|||||||
) {
|
) {
|
||||||
// No point if we've got no storage account...
|
// No point if we've got no storage account...
|
||||||
let nodes = cluster_info.read().unwrap().tvu_peers();
|
let nodes = cluster_info.read().unwrap().tvu_peers();
|
||||||
let client = crate::gossip_service::get_client(&nodes);
|
let client = solana_core::gossip_service::get_client(&nodes);
|
||||||
let storage_balance = client.poll_get_balance_with_commitment(
|
let storage_balance = client.poll_get_balance_with_commitment(
|
||||||
&storage_keypair.pubkey(),
|
&storage_keypair.pubkey(),
|
||||||
meta.client_commitment.clone(),
|
meta.client_commitment.clone(),
|
||||||
@@ -739,7 +694,7 @@ impl Archiver {
|
|||||||
fn get_segment_config(
|
fn get_segment_config(
|
||||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||||
client_commitment: CommitmentConfig,
|
client_commitment: CommitmentConfig,
|
||||||
) -> result::Result<u64, Error> {
|
) -> Result<u64> {
|
||||||
let rpc_peers = {
|
let rpc_peers = {
|
||||||
let cluster_info = cluster_info.read().unwrap();
|
let cluster_info = cluster_info.read().unwrap();
|
||||||
cluster_info.all_rpc_peers()
|
cluster_info.all_rpc_peers()
|
||||||
@@ -758,12 +713,12 @@ impl Archiver {
|
|||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("Error while making rpc request {:?}", err);
|
warn!("Error while making rpc request {:?}", err);
|
||||||
Error::IO(io::Error::new(ErrorKind::Other, "rpc error"))
|
ArchiverError::ClientError(err)
|
||||||
})?
|
})?
|
||||||
.as_u64()
|
.as_u64()
|
||||||
.unwrap())
|
.unwrap())
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "No RPC peers...".to_string()).into())
|
Err(ArchiverError::NoRpcPeers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,7 +728,7 @@ impl Archiver {
|
|||||||
slots_per_segment: u64,
|
slots_per_segment: u64,
|
||||||
previous_blockhash: &Hash,
|
previous_blockhash: &Hash,
|
||||||
exit: &Arc<AtomicBool>,
|
exit: &Arc<AtomicBool>,
|
||||||
) -> result::Result<(Hash, u64), Error> {
|
) -> Result<(Hash, u64)> {
|
||||||
loop {
|
loop {
|
||||||
let (blockhash, turn_slot) = Self::poll_for_blockhash_and_slot(
|
let (blockhash, turn_slot) = Self::poll_for_blockhash_and_slot(
|
||||||
cluster_info,
|
cluster_info,
|
||||||
@@ -793,7 +748,7 @@ impl Archiver {
|
|||||||
slots_per_segment: u64,
|
slots_per_segment: u64,
|
||||||
previous_blockhash: &Hash,
|
previous_blockhash: &Hash,
|
||||||
exit: &Arc<AtomicBool>,
|
exit: &Arc<AtomicBool>,
|
||||||
) -> result::Result<(Hash, u64), Error> {
|
) -> Result<(Hash, u64)> {
|
||||||
info!("waiting for the next turn...");
|
info!("waiting for the next turn...");
|
||||||
loop {
|
loop {
|
||||||
let rpc_peers = {
|
let rpc_peers = {
|
||||||
@@ -814,17 +769,13 @@ impl Archiver {
|
|||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("Error while making rpc request {:?}", err);
|
warn!("Error while making rpc request {:?}", err);
|
||||||
Error::IO(io::Error::new(ErrorKind::Other, "rpc error"))
|
ArchiverError::ClientError(err)
|
||||||
})?;
|
})?;
|
||||||
let RpcStorageTurn {
|
let RpcStorageTurn {
|
||||||
blockhash: storage_blockhash,
|
blockhash: storage_blockhash,
|
||||||
slot: turn_slot,
|
slot: turn_slot,
|
||||||
} = serde_json::from_value::<RpcStorageTurn>(response).map_err(|err| {
|
} = serde_json::from_value::<RpcStorageTurn>(response)
|
||||||
io::Error::new(
|
.map_err(ArchiverError::JsonError)?;
|
||||||
io::ErrorKind::Other,
|
|
||||||
format!("Couldn't parse response: {:?}", err),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let turn_blockhash = storage_blockhash.parse().map_err(|err| {
|
let turn_blockhash = storage_blockhash.parse().map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
@@ -842,7 +793,7 @@ impl Archiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if exit.load(Ordering::Relaxed) {
|
if exit.load(Ordering::Relaxed) {
|
||||||
return Err(Error::IO(io::Error::new(
|
return Err(ArchiverError::IO(io::Error::new(
|
||||||
ErrorKind::Other,
|
ErrorKind::Other,
|
||||||
"exit signalled...",
|
"exit signalled...",
|
||||||
)));
|
)));
|
||||||
@@ -857,7 +808,7 @@ impl Archiver {
|
|||||||
/// It is recommended to use a temporary blockstore for this since the download will not verify
|
/// It is recommended to use a temporary blockstore for this since the download will not verify
|
||||||
/// shreds received and might impact the chaining of shreds across slots
|
/// shreds received and might impact the chaining of shreds across slots
|
||||||
pub fn download_from_archiver(
|
pub fn download_from_archiver(
|
||||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
serve_repair: &ServeRepair,
|
||||||
archiver_info: &ContactInfo,
|
archiver_info: &ContactInfo,
|
||||||
blockstore: &Arc<Blockstore>,
|
blockstore: &Arc<Blockstore>,
|
||||||
slots_per_segment: u64,
|
slots_per_segment: u64,
|
||||||
@@ -877,10 +828,10 @@ impl Archiver {
|
|||||||
Recycler::default(),
|
Recycler::default(),
|
||||||
"archiver_reeciver",
|
"archiver_reeciver",
|
||||||
);
|
);
|
||||||
let id = cluster_info.read().unwrap().id();
|
let id = serve_repair.keypair().pubkey();
|
||||||
info!(
|
info!(
|
||||||
"Sending repair requests from: {} to: {}",
|
"Sending repair requests from: {} to: {}",
|
||||||
cluster_info.read().unwrap().my_data().id,
|
serve_repair.my_info().id,
|
||||||
archiver_info.gossip
|
archiver_info.gossip
|
||||||
);
|
);
|
||||||
let repair_slot_range = RepairSlotRange {
|
let repair_slot_range = RepairSlotRange {
|
||||||
@@ -900,9 +851,7 @@ impl Archiver {
|
|||||||
let reqs: Vec<_> = repairs
|
let reqs: Vec<_> = repairs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|repair_request| {
|
.filter_map(|repair_request| {
|
||||||
cluster_info
|
serve_repair
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.map_repair_request(&repair_request)
|
.map_repair_request(&repair_request)
|
||||||
.map(|result| ((archiver_info.gossip, result), repair_request))
|
.map(|result| ((archiver_info.gossip, result), repair_request))
|
||||||
.ok()
|
.ok()
|
||||||
@@ -950,9 +899,7 @@ impl Archiver {
|
|||||||
|
|
||||||
// check if all the slots in the segment are complete
|
// check if all the slots in the segment are complete
|
||||||
if !Self::segment_complete(start_slot, slots_per_segment, blockstore) {
|
if !Self::segment_complete(start_slot, slots_per_segment, blockstore) {
|
||||||
return Err(
|
return Err(ArchiverError::SegmentDownloadError);
|
||||||
io::Error::new(ErrorKind::Other, "Unable to download the full segment").into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(start_slot)
|
Ok(start_slot)
|
||||||
}
|
}
|
||||||
@@ -995,74 +942,3 @@ impl Archiver {
|
|||||||
panic!("Couldn't get segment slot from archiver!");
|
panic!("Couldn't get segment slot from archiver!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::fs::{create_dir_all, remove_file};
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
fn tmp_file_path(name: &str) -> PathBuf {
|
|
||||||
use std::env;
|
|
||||||
let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
|
|
||||||
let keypair = Keypair::new();
|
|
||||||
|
|
||||||
let mut path = PathBuf::new();
|
|
||||||
path.push(out_dir);
|
|
||||||
path.push("tmp");
|
|
||||||
create_dir_all(&path).unwrap();
|
|
||||||
|
|
||||||
path.push(format!("{}-{}", name, keypair.pubkey()));
|
|
||||||
path
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sample_file() {
|
|
||||||
solana_logger::setup();
|
|
||||||
let in_path = tmp_file_path("test_sample_file_input.txt");
|
|
||||||
let num_strings = 4096;
|
|
||||||
let string = "12foobar";
|
|
||||||
{
|
|
||||||
let mut in_file = File::create(&in_path).unwrap();
|
|
||||||
for _ in 0..num_strings {
|
|
||||||
in_file.write(string.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let num_samples = (string.len() * num_strings / size_of::<Hash>()) as u64;
|
|
||||||
let samples: Vec<_> = (0..num_samples).collect();
|
|
||||||
let res = sample_file(&in_path, samples.as_slice());
|
|
||||||
let ref_hash: Hash = Hash::new(&[
|
|
||||||
173, 251, 182, 165, 10, 54, 33, 150, 133, 226, 106, 150, 99, 192, 179, 1, 230, 144,
|
|
||||||
151, 126, 18, 191, 54, 67, 249, 140, 230, 160, 56, 30, 170, 52,
|
|
||||||
]);
|
|
||||||
let res = res.unwrap();
|
|
||||||
assert_eq!(res, ref_hash);
|
|
||||||
|
|
||||||
// Sample just past the end
|
|
||||||
assert!(sample_file(&in_path, &[num_samples]).is_err());
|
|
||||||
remove_file(&in_path).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sample_file_invalid_offset() {
|
|
||||||
let in_path = tmp_file_path("test_sample_file_invalid_offset_input.txt");
|
|
||||||
{
|
|
||||||
let mut in_file = File::create(&in_path).unwrap();
|
|
||||||
for _ in 0..4096 {
|
|
||||||
in_file.write("123456foobar".as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let samples = [0, 200000];
|
|
||||||
let res = sample_file(&in_path, &samples);
|
|
||||||
assert!(res.is_err());
|
|
||||||
remove_file(in_path).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sample_file_missing_file() {
|
|
||||||
let in_path = tmp_file_path("test_sample_file_that_doesnt_exist.txt");
|
|
||||||
let samples = [0, 5];
|
|
||||||
let res = sample_file(&in_path, &samples);
|
|
||||||
assert!(res.is_err());
|
|
||||||
}
|
|
||||||
}
|
|
11
archiver-lib/src/lib.rs
Normal file
11
archiver-lib/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate solana_metrics;
|
||||||
|
|
||||||
|
pub mod archiver;
|
||||||
|
mod result;
|
48
archiver-lib/src/result.rs
Normal file
48
archiver-lib/src/result.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use serde_json;
|
||||||
|
use solana_client::client_error;
|
||||||
|
use solana_ledger::blockstore;
|
||||||
|
use solana_sdk::transport;
|
||||||
|
use std::any::Any;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ArchiverError {
|
||||||
|
#[error("IO error")]
|
||||||
|
IO(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("blockstore error")]
|
||||||
|
BlockstoreError(#[from] blockstore::BlockstoreError),
|
||||||
|
|
||||||
|
#[error("crossbeam error")]
|
||||||
|
CrossbeamSendError(#[from] crossbeam_channel::SendError<u64>),
|
||||||
|
|
||||||
|
#[error("send error")]
|
||||||
|
SendError(#[from] std::sync::mpsc::SendError<u64>),
|
||||||
|
|
||||||
|
#[error("join error")]
|
||||||
|
JoinError(Box<dyn Any + Send + 'static>),
|
||||||
|
|
||||||
|
#[error("transport error")]
|
||||||
|
TransportError(#[from] transport::TransportError),
|
||||||
|
|
||||||
|
#[error("client error")]
|
||||||
|
ClientError(#[from] client_error::ClientError),
|
||||||
|
|
||||||
|
#[error("Json parsing error")]
|
||||||
|
JsonError(#[from] serde_json::error::Error),
|
||||||
|
|
||||||
|
#[error("Storage account has no balance")]
|
||||||
|
EmptyStorageAccountBalance,
|
||||||
|
|
||||||
|
#[error("No RPC peers..")]
|
||||||
|
NoRpcPeers,
|
||||||
|
|
||||||
|
#[error("Couldn't download full segment")]
|
||||||
|
SegmentDownloadError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<Box<dyn Any + Send + 'static>> for ArchiverError {
|
||||||
|
fn from(e: Box<dyn Any + Send + 'static>) -> ArchiverError {
|
||||||
|
ArchiverError::JoinError(e)
|
||||||
|
}
|
||||||
|
}
|
26
archiver-utils/Cargo.toml
Normal file
26
archiver-utils/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "solana-archiver-utils"
|
||||||
|
version = "0.23.5"
|
||||||
|
description = "Solana Archiver Utils"
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
homepage = "https://solana.com/"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4.8"
|
||||||
|
rand = "0.6.5"
|
||||||
|
rand_chacha = "0.1.1"
|
||||||
|
solana-chacha = { path = "../chacha", version = "0.23.5" }
|
||||||
|
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.5" }
|
||||||
|
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||||
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
|
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||||
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex = "0.4.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "solana_archiver_utils"
|
120
archiver-utils/src/lib.rs
Normal file
120
archiver-utils/src/lib.rs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
use solana_sdk::hash::{Hash, Hasher};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom};
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> {
|
||||||
|
let in_file = File::open(in_path)?;
|
||||||
|
let metadata = in_file.metadata()?;
|
||||||
|
let mut buffer_file = BufReader::new(in_file);
|
||||||
|
|
||||||
|
let mut hasher = Hasher::default();
|
||||||
|
let sample_size = size_of::<Hash>();
|
||||||
|
let sample_size64 = sample_size as u64;
|
||||||
|
let mut buf = vec![0; sample_size];
|
||||||
|
|
||||||
|
let file_len = metadata.len();
|
||||||
|
if file_len < sample_size64 {
|
||||||
|
return Err(io::Error::new(ErrorKind::Other, "file too short!"));
|
||||||
|
}
|
||||||
|
for offset in sample_offsets {
|
||||||
|
if *offset > (file_len - sample_size64) / sample_size64 {
|
||||||
|
return Err(io::Error::new(ErrorKind::Other, "offset too large"));
|
||||||
|
}
|
||||||
|
buffer_file.seek(SeekFrom::Start(*offset * sample_size64))?;
|
||||||
|
trace!("sampling @ {} ", *offset);
|
||||||
|
match buffer_file.read(&mut buf) {
|
||||||
|
Ok(size) => {
|
||||||
|
assert_eq!(size, buf.len());
|
||||||
|
hasher.hash(&buf);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error sampling file");
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(hasher.result())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use std::fs::{create_dir_all, remove_file};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
extern crate hex;
|
||||||
|
|
||||||
|
fn tmp_file_path(name: &str) -> PathBuf {
|
||||||
|
use std::env;
|
||||||
|
let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
|
||||||
|
let mut rand_bits = [0u8; 32];
|
||||||
|
thread_rng().fill(&mut rand_bits[..]);
|
||||||
|
|
||||||
|
let mut path = PathBuf::new();
|
||||||
|
path.push(out_dir);
|
||||||
|
path.push("tmp");
|
||||||
|
create_dir_all(&path).unwrap();
|
||||||
|
|
||||||
|
path.push(format!("{}-{:?}", name, hex::encode(rand_bits)));
|
||||||
|
println!("path: {:?}", path);
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sample_file() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let in_path = tmp_file_path("test_sample_file_input.txt");
|
||||||
|
let num_strings = 4096;
|
||||||
|
let string = "12foobar";
|
||||||
|
{
|
||||||
|
let mut in_file = File::create(&in_path).unwrap();
|
||||||
|
for _ in 0..num_strings {
|
||||||
|
in_file.write(string.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let num_samples = (string.len() * num_strings / size_of::<Hash>()) as u64;
|
||||||
|
let samples: Vec<_> = (0..num_samples).collect();
|
||||||
|
let res = sample_file(&in_path, samples.as_slice());
|
||||||
|
let ref_hash: Hash = Hash::new(&[
|
||||||
|
173, 251, 182, 165, 10, 54, 33, 150, 133, 226, 106, 150, 99, 192, 179, 1, 230, 144,
|
||||||
|
151, 126, 18, 191, 54, 67, 249, 140, 230, 160, 56, 30, 170, 52,
|
||||||
|
]);
|
||||||
|
let res = res.unwrap();
|
||||||
|
assert_eq!(res, ref_hash);
|
||||||
|
|
||||||
|
// Sample just past the end
|
||||||
|
assert!(sample_file(&in_path, &[num_samples]).is_err());
|
||||||
|
remove_file(&in_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sample_file_invalid_offset() {
|
||||||
|
let in_path = tmp_file_path("test_sample_file_invalid_offset_input.txt");
|
||||||
|
{
|
||||||
|
let mut in_file = File::create(&in_path).unwrap();
|
||||||
|
for _ in 0..4096 {
|
||||||
|
in_file.write("123456foobar".as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let samples = [0, 200000];
|
||||||
|
let res = sample_file(&in_path, &samples);
|
||||||
|
assert!(res.is_err());
|
||||||
|
remove_file(in_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sample_file_missing_file() {
|
||||||
|
let in_path = tmp_file_path("test_sample_file_that_doesnt_exist.txt");
|
||||||
|
let samples = [0, 5];
|
||||||
|
let res = sample_file(&in_path, &samples);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
}
|
@@ -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 = "0.22.10"
|
version = "0.23.5"
|
||||||
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,10 +10,11 @@ homepage = "https://solana.com/"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
console = "0.9.1"
|
console = "0.9.1"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "0.22.10" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||||
solana-core = { path = "../core", version = "0.22.10" }
|
solana-core = { path = "../core", version = "0.23.5" }
|
||||||
solana-logger = { path = "../logger", version = "0.22.10" }
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.22.10" }
|
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.22.10" }
|
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.22.10" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||||
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
use clap::{crate_description, crate_name, App, Arg};
|
use clap::{crate_description, crate_name, App, Arg};
|
||||||
use console::style;
|
use console::style;
|
||||||
|
use solana_archiver_lib::archiver::Archiver;
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_validators::is_keypair,
|
input_validators::is_keypair,
|
||||||
keypair::{
|
keypair::{
|
||||||
@@ -8,7 +9,6 @@ use solana_clap_utils::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use solana_core::{
|
use solana_core::{
|
||||||
archiver::Archiver,
|
|
||||||
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
||||||
contact_info::ContactInfo,
|
contact_info::ContactInfo,
|
||||||
};
|
};
|
||||||
|
@@ -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 = "0.22.10"
|
version = "0.23.5"
|
||||||
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 = "0.22.10" }
|
solana-core = { path = "../core", version = "0.23.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "0.22.10" }
|
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||||
solana-logger = { path = "../logger", version = "0.22.10" }
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.22.10" }
|
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||||
solana-measure = { path = "../measure", version = "0.22.10" }
|
solana-measure = { path = "../measure", version = "0.23.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.22.10" }
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
crossbeam-channel = "0.3"
|
crossbeam-channel = "0.3"
|
||||||
|
@@ -162,8 +162,8 @@ fn main() {
|
|||||||
// If it is dropped before poh_service, then poh_service will error when
|
// If it is dropped before poh_service, then poh_service will error when
|
||||||
// calling send() on the channel.
|
// calling send() on the channel.
|
||||||
let signal_receiver = Arc::new(signal_receiver);
|
let signal_receiver = Arc::new(signal_receiver);
|
||||||
let mut total = 0;
|
let mut total_us = 0;
|
||||||
let mut tx_total = 0;
|
let mut tx_total_us = 0;
|
||||||
let mut txs_processed = 0;
|
let mut txs_processed = 0;
|
||||||
let mut root = 1;
|
let mut root = 1;
|
||||||
let collector = Pubkey::new_rand();
|
let collector = Pubkey::new_rand();
|
||||||
@@ -173,6 +173,7 @@ fn main() {
|
|||||||
chunk_len,
|
chunk_len,
|
||||||
num_threads,
|
num_threads,
|
||||||
};
|
};
|
||||||
|
let mut total_sent = 0;
|
||||||
for _ in 0..ITERS {
|
for _ in 0..ITERS {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let mut sent = 0;
|
let mut sent = 0;
|
||||||
@@ -223,7 +224,7 @@ fn main() {
|
|||||||
);
|
);
|
||||||
assert!(txs_processed < bank.transaction_count());
|
assert!(txs_processed < bank.transaction_count());
|
||||||
txs_processed = bank.transaction_count();
|
txs_processed = bank.transaction_count();
|
||||||
tx_total += duration_as_us(&now.elapsed());
|
tx_total_us += duration_as_us(&now.elapsed());
|
||||||
|
|
||||||
let mut poh_time = Measure::start("poh_time");
|
let mut poh_time = Measure::start("poh_time");
|
||||||
poh_recorder.lock().unwrap().reset(
|
poh_recorder.lock().unwrap().reset(
|
||||||
@@ -255,20 +256,21 @@ fn main() {
|
|||||||
poh_time.as_us(),
|
poh_time.as_us(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
tx_total += duration_as_us(&now.elapsed());
|
tx_total_us += duration_as_us(&now.elapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This signature clear may not actually clear the signatures
|
// This signature clear may not actually clear the signatures
|
||||||
// in this chunk, but since we rotate between CHUNKS then
|
// in this chunk, but since we rotate between CHUNKS then
|
||||||
// we should clear them by the time we come around again to re-use that chunk.
|
// we should clear them by the time we come around again to re-use that chunk.
|
||||||
bank.clear_signatures();
|
bank.clear_signatures();
|
||||||
total += duration_as_us(&now.elapsed());
|
total_us += duration_as_us(&now.elapsed());
|
||||||
debug!(
|
debug!(
|
||||||
"time: {} us checked: {} sent: {}",
|
"time: {} us checked: {} sent: {}",
|
||||||
duration_as_us(&now.elapsed()),
|
duration_as_us(&now.elapsed()),
|
||||||
txes / CHUNKS,
|
txes / CHUNKS,
|
||||||
sent,
|
sent,
|
||||||
);
|
);
|
||||||
|
total_sent += sent;
|
||||||
|
|
||||||
if bank.slot() > 0 && bank.slot() % 16 == 0 {
|
if bank.slot() > 0 && bank.slot() % 16 == 0 {
|
||||||
for tx in transactions.iter_mut() {
|
for tx in transactions.iter_mut() {
|
||||||
@@ -284,11 +286,11 @@ fn main() {
|
|||||||
}
|
}
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{{'name': 'banking_bench_total', 'median': '{}'}}",
|
"{{'name': 'banking_bench_total', 'median': '{}'}}",
|
||||||
total / ITERS as u64,
|
(1000.0 * 1000.0 * total_sent as f64) / (total_us as f64),
|
||||||
);
|
);
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{{'name': 'banking_bench_tx_total', 'median': '{}'}}",
|
"{{'name': 'banking_bench_tx_total', 'median': '{}'}}",
|
||||||
tx_total / ITERS as u64,
|
(1000.0 * 1000.0 * total_sent as f64) / (tx_total_us as f64),
|
||||||
);
|
);
|
||||||
|
|
||||||
drop(verified_sender);
|
drop(verified_sender);
|
||||||
|
@@ -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 = "0.22.10"
|
version = "0.23.5"
|
||||||
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/"
|
||||||
@@ -23,19 +23,19 @@ serde = "1.0.104"
|
|||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.44"
|
serde_json = "1.0.44"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "0.22.10" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||||
solana-core = { path = "../core", version = "0.22.10" }
|
solana-core = { path = "../core", version = "0.23.5" }
|
||||||
solana-genesis = { path = "../genesis", version = "0.22.10" }
|
solana-genesis = { path = "../genesis", version = "0.23.5" }
|
||||||
solana-client = { path = "../client", version = "0.22.10" }
|
solana-client = { path = "../client", version = "0.23.5" }
|
||||||
solana-faucet = { path = "../faucet", version = "0.22.10" }
|
solana-faucet = { path = "../faucet", version = "0.23.5" }
|
||||||
solana-exchange-program = { path = "../programs/exchange", version = "0.22.10" }
|
solana-exchange-program = { path = "../programs/exchange", version = "0.23.5" }
|
||||||
solana-logger = { path = "../logger", version = "0.22.10" }
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.22.10" }
|
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.22.10" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.22.10" }
|
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.22.10" }
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
untrusted = "0.7.0"
|
untrusted = "0.7.0"
|
||||||
ws = "0.9.1"
|
ws = "0.9.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-local-cluster = { path = "../local-cluster", version = "0.22.10" }
|
solana-local-cluster = { path = "../local-cluster", version = "0.23.5" }
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
use clap::{crate_description, crate_name, value_t, App, Arg, ArgMatches};
|
use clap::{crate_description, crate_name, value_t, App, Arg, ArgMatches};
|
||||||
use solana_core::gen_keys::GenKeys;
|
use solana_core::gen_keys::GenKeys;
|
||||||
use solana_faucet::faucet::FAUCET_PORT;
|
use solana_faucet::faucet::FAUCET_PORT;
|
||||||
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
|
use solana_sdk::signature::{read_keypair_file, Keypair};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@@ -16,6 +16,7 @@ use std::sync::mpsc::channel;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn test_exchange_local_cluster() {
|
fn test_exchange_local_cluster() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
|
@@ -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 = "0.22.10"
|
version = "0.23.5"
|
||||||
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 = "0.22.10" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||||
solana-core = { path = "../core", version = "0.22.10" }
|
solana-core = { path = "../core", version = "0.23.5" }
|
||||||
solana-logger = { path = "../logger", version = "0.22.10" }
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.22.10" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
use clap::{crate_description, crate_name, App, Arg};
|
use clap::{crate_description, crate_name, App, Arg};
|
||||||
use solana_core::packet::{Packet, Packets, PacketsRecycler, PACKET_DATA_SIZE};
|
use solana_core::packet::{Packet, Packets, PacketsRecycler, PACKET_DATA_SIZE};
|
||||||
use solana_core::result::Result;
|
|
||||||
use solana_core::streamer::{receiver, PacketReceiver};
|
use solana_core::streamer::{receiver, PacketReceiver};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||||
@@ -8,7 +7,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::thread::{spawn, JoinHandle};
|
use std::thread::{spawn, JoinHandle, Result};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
@@ -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 = "0.22.10"
|
version = "0.23.5"
|
||||||
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/"
|
||||||
@@ -16,24 +16,24 @@ serde = "1.0.104"
|
|||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.44"
|
serde_json = "1.0.44"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "0.22.10" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||||
solana-core = { path = "../core", version = "0.22.10" }
|
solana-core = { path = "../core", version = "0.23.5" }
|
||||||
solana-genesis = { path = "../genesis", version = "0.22.10" }
|
solana-genesis = { path = "../genesis", version = "0.23.5" }
|
||||||
solana-client = { path = "../client", version = "0.22.10" }
|
solana-client = { path = "../client", version = "0.23.5" }
|
||||||
solana-faucet = { path = "../faucet", version = "0.22.10" }
|
solana-faucet = { path = "../faucet", version = "0.23.5" }
|
||||||
solana-librapay = { path = "../programs/librapay", version = "0.22.10", optional = true }
|
solana-librapay = { path = "../programs/librapay", version = "0.23.5", optional = true }
|
||||||
solana-logger = { path = "../logger", version = "0.22.10" }
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.22.10" }
|
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||||
solana-measure = { path = "../measure", version = "0.22.10" }
|
solana-measure = { path = "../measure", version = "0.23.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.22.10" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.22.10" }
|
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.22.10" }
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
solana-move-loader-program = { path = "../programs/move_loader", version = "0.22.10", optional = true }
|
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.5", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "0.3.2"
|
serial_test = "0.3.2"
|
||||||
serial_test_derive = "0.3.1"
|
serial_test_derive = "0.3.1"
|
||||||
solana-local-cluster = { path = "../local-cluster", version = "0.22.10" }
|
solana-local-cluster = { path = "../local-cluster", version = "0.23.5" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
move = ["solana-librapay", "solana-move-loader-program"]
|
move = ["solana-librapay", "solana-move-loader-program"]
|
||||||
|
@@ -187,7 +187,9 @@ where
|
|||||||
sleep(Duration::from_millis(1));
|
sleep(Duration::from_millis(1));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while shared_tx_active_thread_count.load(Ordering::Relaxed) > 0 {
|
while !shared_txs.read().unwrap().is_empty()
|
||||||
|
|| shared_tx_active_thread_count.load(Ordering::Relaxed) > 0
|
||||||
|
{
|
||||||
sleep(Duration::from_millis(1));
|
sleep(Duration::from_millis(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
||||||
use solana_faucet::faucet::FAUCET_PORT;
|
use solana_faucet::faucet::FAUCET_PORT;
|
||||||
use solana_sdk::fee_calculator::FeeCalculator;
|
use solana_sdk::fee_calculator::FeeCalculator;
|
||||||
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
|
use solana_sdk::signature::{read_keypair_file, Keypair};
|
||||||
use std::{net::SocketAddr, process::exit, time::Duration};
|
use std::{net::SocketAddr, process::exit, time::Duration};
|
||||||
|
|
||||||
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::SOL_LAMPORTS;
|
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;
|
||||||
|
|
||||||
/// Holds the configuration for a single run of the benchmark
|
/// Holds the configuration for a single run of the benchmark
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@@ -12,7 +12,7 @@ use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::e
|
|||||||
pub const NUM_SIGNATURES_FOR_TXS: u64 = 100_000 * 60 * 60 * 24 * 7;
|
pub const NUM_SIGNATURES_FOR_TXS: u64 = 100_000 * 60 * 60 * 24 * 7;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
solana_logger::setup_with_filter("solana=info");
|
solana_logger::setup_with_default("solana=info");
|
||||||
solana_metrics::set_panic_hook("bench-tps");
|
solana_metrics::set_panic_hook("bench-tps");
|
||||||
|
|
||||||
let matches = cli::build_args(solana_clap_utils::version!()).get_matches();
|
let matches = cli::build_args(solana_clap_utils::version!()).get_matches();
|
||||||
|
@@ -24,7 +24,7 @@ msc {
|
|||||||
... ;
|
... ;
|
||||||
Validator abox Validator [label="\nmax\nlockout\n"];
|
Validator abox Validator [label="\nmax\nlockout\n"];
|
||||||
|||;
|
|||;
|
||||||
StakerX => Cluster [label="StakeState::RedeemCredits()"];
|
Cluster box Cluster [label="credits redeemed (at epoch)"];
|
||||||
StakerY => Cluster [label="StakeState::RedeemCredits()"] ;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
19
book/art/spv-bank-hash.bob
Normal file
19
book/art/spv-bank-hash.bob
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
+----------+
|
||||||
|
| Bank-Hash|
|
||||||
|
+----------+
|
||||||
|
^
|
||||||
|
|
|
||||||
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||||
|
: :
|
||||||
|
: +--------------+ +-------------+ :
|
||||||
|
: Hash( | Accounts-Hash| + | Block-Merkle| ) :
|
||||||
|
: +--------------+ +-------------+ :
|
||||||
|
: ^ :
|
||||||
|
+~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||||
|
|
|
||||||
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||||
|
: +---------------+ +---------------+ +---------------+ :
|
||||||
|
: Hash( | Hash(Account1)| + | Hash(Account2)| + ... + | Hash(AccountN)| ) :
|
||||||
|
: +---------------+ +---------------+ +---------------+ :
|
||||||
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|
||||||
|
|
@@ -1,18 +0,0 @@
|
|||||||
+------------+
|
|
||||||
| Bank-Merkle|
|
|
||||||
+------------+
|
|
||||||
^ ^
|
|
||||||
/ \
|
|
||||||
+-----------------+ +-------------+
|
|
||||||
| Bank-Diff-Merkle| | Block-Merkle|
|
|
||||||
+-----------------+ +-------------+
|
|
||||||
^ ^
|
|
||||||
/ \
|
|
||||||
+------+ +--------------------------+
|
|
||||||
| Hash | | Previous Bank-Diff-Merkle|
|
|
||||||
+------+ +--------------------------+
|
|
||||||
^ ^
|
|
||||||
/ \
|
|
||||||
+---------------+ +---------------+
|
|
||||||
| Hash(Account1)| | Hash(Account2)|
|
|
||||||
+---------------+ +---------------+
|
|
@@ -5,9 +5,9 @@ cd "$(dirname "$0")"
|
|||||||
|
|
||||||
usage=$(cargo -q run -p solana-cli -- -C ~/.foo --help | sed 's|'"$HOME"'|~|g')
|
usage=$(cargo -q run -p solana-cli -- -C ~/.foo --help | sed 's|'"$HOME"'|~|g')
|
||||||
|
|
||||||
out=${1:-src/api-reference/cli.md}
|
out=${1:-src/cli/usage.md}
|
||||||
|
|
||||||
cat src/api-reference/.cli.md > "$out"
|
cat src/cli/.usage.md.header > "$out"
|
||||||
|
|
||||||
section() {
|
section() {
|
||||||
declare mark=${2:-"###"}
|
declare mark=${2:-"###"}
|
||||||
|
@@ -3,4 +3,14 @@ set -e
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# md check
|
||||||
|
find src -name '*.md' -a \! -name SUMMARY.md |
|
||||||
|
while read -r file; do
|
||||||
|
if ! grep -q '('"${file#src/}"')' src/SUMMARY.md; then
|
||||||
|
echo "Error: $file missing from SUMMARY.md"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
make -j"$(nproc)" test
|
make -j"$(nproc)" test
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
BOB_SRCS=$(wildcard art/*.bob)
|
BOB_SRCS=$(wildcard art/*.bob)
|
||||||
MSC_SRCS=$(wildcard art/*.msc)
|
MSC_SRCS=$(wildcard art/*.msc)
|
||||||
MD_SRCS=$(wildcard src/*.md)
|
MD_SRCS=$(wildcard src/*.md src/*/*.md)
|
||||||
|
|
||||||
SVG_IMGS=$(BOB_SRCS:art/%.bob=src/.gitbook/assets/%.svg) $(MSC_SRCS:art/%.msc=src/.gitbook/assets/%.svg)
|
SVG_IMGS=$(BOB_SRCS:art/%.bob=src/.gitbook/assets/%.svg) $(MSC_SRCS:art/%.msc=src/.gitbook/assets/%.svg)
|
||||||
|
|
||||||
|
@@ -1,14 +1,31 @@
|
|||||||
# Table of contents
|
# Table of contents
|
||||||
|
|
||||||
* [Introduction](introduction.md)
|
* [Introduction](introduction.md)
|
||||||
* [Terminology](terminology.md)
|
* [Using Solana from the Command-line](cli/README.md)
|
||||||
* [Getting Started](getting-started/README.md)
|
* [Command-line Usage](cli/usage.md)
|
||||||
* [Testnet Participation](getting-started/testnet-participation.md)
|
* [Paper Wallet](paper-wallet/README.md)
|
||||||
* [Example Client: Web Wallet](getting-started/webwallet.md)
|
* [Installation](paper-wallet/installation.md)
|
||||||
* [Programming Model](programs/README.md)
|
* [Paper Wallet Usage](paper-wallet/usage.md)
|
||||||
* [Example: Tic-Tac-Toe](programs/tictactoe.md)
|
* [Offline Signing](offline-signing/README.md)
|
||||||
* [Drones](programs/drones.md)
|
* [Durable Transaction Nonces](offline-signing/durable-nonce.md)
|
||||||
* [A Solana Cluster](cluster/README.md)
|
* [Developing Applications](apps/README.md)
|
||||||
|
* [Example: Web Wallet](apps/webwallet.md)
|
||||||
|
* [Example: Tic-Tac-Toe](apps/tictactoe.md)
|
||||||
|
* [Drones](apps/drones.md)
|
||||||
|
* [Anatomy of a Transaction](transaction.md)
|
||||||
|
* [JSON RPC API](apps/jsonrpc-api.md)
|
||||||
|
* [JavaScript API](apps/javascript-api.md)
|
||||||
|
* [Running a Validator](running-validator/README.md)
|
||||||
|
* [Validator Requirements](running-validator/validator-reqs.md)
|
||||||
|
* [Choosing a Testnet](running-validator/validator-testnet.md)
|
||||||
|
* [Installing the Validator Software](running-validator/validator-software.md)
|
||||||
|
* [Starting a Validator](running-validator/validator-start.md)
|
||||||
|
* [Staking](running-validator/validator-stake.md)
|
||||||
|
* [Monitoring a Validator](running-validator/validator-monitor.md)
|
||||||
|
* [Publishing Validator Info](running-validator/validator-info.md)
|
||||||
|
* [Troubleshooting](running-validator/validator-troubleshoot.md)
|
||||||
|
* [Running an Archiver](running-archiver.md)
|
||||||
|
* [Understanding Solana's Architecture](cluster/README.md)
|
||||||
* [Synchronization](cluster/synchronization.md)
|
* [Synchronization](cluster/synchronization.md)
|
||||||
* [Leader Rotation](cluster/leader-rotation.md)
|
* [Leader Rotation](cluster/leader-rotation.md)
|
||||||
* [Fork Generation](cluster/fork-generation.md)
|
* [Fork Generation](cluster/fork-generation.md)
|
||||||
@@ -20,46 +37,13 @@
|
|||||||
* [Performance Metrics](cluster/performance-metrics.md)
|
* [Performance Metrics](cluster/performance-metrics.md)
|
||||||
* [Anatomy of a Validator](validator/README.md)
|
* [Anatomy of a Validator](validator/README.md)
|
||||||
* [TPU](validator/tpu.md)
|
* [TPU](validator/tpu.md)
|
||||||
* [TVU](validator/tvu/README.md)
|
* [TVU](validator/tvu.md)
|
||||||
* [Blockstore](validator/tvu/blockstore.md)
|
* [Blockstore](validator/blockstore.md)
|
||||||
* [Gossip Service](validator/gossip.md)
|
* [Gossip Service](validator/gossip.md)
|
||||||
* [The Runtime](validator/runtime.md)
|
* [The Runtime](validator/runtime.md)
|
||||||
* [Anatomy of a Transaction](transaction.md)
|
* [Building from Source](building-from-source.md)
|
||||||
* [Running a Validator](running-validator/README.md)
|
* [Terminology](terminology.md)
|
||||||
* [Validator Requirements](running-validator/validator-reqs.md)
|
|
||||||
* [Choosing a Testnet](running-validator/validator-testnet.md)
|
|
||||||
* [Installing the Validator Software](running-validator/validator-software.md)
|
|
||||||
* [Starting a Validator](running-validator/validator-start.md)
|
|
||||||
* [Staking](running-validator/validator-stake.md)
|
|
||||||
* [Monitoring a Validator](running-validator/validator-monitor.md)
|
|
||||||
* [Publishing Validator Info](running-validator/validator-info.md)
|
|
||||||
* [Troubleshooting](running-validator/validator-troubleshoot.md)
|
|
||||||
* [Running an Archiver](running-archiver.md)
|
|
||||||
* [Paper Wallet](paper-wallet/README.md)
|
|
||||||
* [Installation](paper-wallet/installation.md)
|
|
||||||
* [Paper Wallet Usage](paper-wallet/usage.md)
|
|
||||||
* [Offline Signing](offline-signing/README.md)
|
|
||||||
* [Durable Transaction Nonces](offline-signing/durable-nonce.md)
|
|
||||||
* [API Reference](api-reference/README.md)
|
|
||||||
* [Transaction](api-reference/transaction-api.md)
|
|
||||||
* [Instruction](api-reference/instruction-api.md)
|
|
||||||
* [Blockstreamer](api-reference/blockstreamer.md)
|
|
||||||
* [JSON RPC API](api-reference/jsonrpc-api.md)
|
|
||||||
* [JavaScript API](api-reference/javascript-api.md)
|
|
||||||
* [solana CLI](api-reference/cli.md)
|
|
||||||
* [Accepted Design Proposals](proposals/README.md)
|
|
||||||
* [Ledger Replication](proposals/ledger-replication-to-implement.md)
|
|
||||||
* [Secure Vote Signing](proposals/vote-signing-to-implement.md)
|
|
||||||
* [Cluster Test Framework](proposals/cluster-test-framework.md)
|
|
||||||
* [Validator](proposals/validator-proposal.md)
|
|
||||||
* [Simple Payment and State Verification](proposals/simple-payment-and-state-verification.md)
|
|
||||||
* [Cross-Program Invocation](proposals/cross-program-invocation.md)
|
|
||||||
* [Inter-chain Transaction Verification](proposals/interchain-transaction-verification.md)
|
|
||||||
* [Snapshot Verification](proposals/snapshot-verification.md)
|
|
||||||
* [Bankless Leader](proposals/bankless-leader.md)
|
|
||||||
* [Slashing](proposals/slashing.md)
|
|
||||||
* [Implemented Design Proposals](implemented-proposals/README.md)
|
* [Implemented Design Proposals](implemented-proposals/README.md)
|
||||||
* [Blockstore](implemented-proposals/blockstore.md)
|
|
||||||
* [Cluster Software Installation and Updates](implemented-proposals/installer.md)
|
* [Cluster Software Installation and Updates](implemented-proposals/installer.md)
|
||||||
* [Cluster Economics](implemented-proposals/ed_overview/README.md)
|
* [Cluster Economics](implemented-proposals/ed_overview/README.md)
|
||||||
* [Validation-client Economics](implemented-proposals/ed_overview/ed_validation_client_economics/README.md)
|
* [Validation-client Economics](implemented-proposals/ed_overview/ed_validation_client_economics/README.md)
|
||||||
@@ -70,6 +54,7 @@
|
|||||||
* [Replication-client Economics](implemented-proposals/ed_overview/ed_replication_client_economics/README.md)
|
* [Replication-client Economics](implemented-proposals/ed_overview/ed_replication_client_economics/README.md)
|
||||||
* [Storage-replication Rewards](implemented-proposals/ed_overview/ed_replication_client_economics/ed_rce_storage_replication_rewards.md)
|
* [Storage-replication Rewards](implemented-proposals/ed_overview/ed_replication_client_economics/ed_rce_storage_replication_rewards.md)
|
||||||
* [Replication-client Reward Auto-delegation](implemented-proposals/ed_overview/ed_replication_client_economics/ed_rce_replication_client_reward_auto_delegation.md)
|
* [Replication-client Reward Auto-delegation](implemented-proposals/ed_overview/ed_replication_client_economics/ed_rce_replication_client_reward_auto_delegation.md)
|
||||||
|
* [Storage Rent Economics](implemented-proposals/ed_overview/ed_storage_rent_economics.md)
|
||||||
* [Economic Sustainability](implemented-proposals/ed_overview/ed_economic_sustainability.md)
|
* [Economic Sustainability](implemented-proposals/ed_overview/ed_economic_sustainability.md)
|
||||||
* [Attack Vectors](implemented-proposals/ed_overview/ed_attack_vectors.md)
|
* [Attack Vectors](implemented-proposals/ed_overview/ed_attack_vectors.md)
|
||||||
* [Economic Design MVP](implemented-proposals/ed_overview/ed_mvp.md)
|
* [Economic Design MVP](implemented-proposals/ed_overview/ed_mvp.md)
|
||||||
@@ -88,3 +73,19 @@
|
|||||||
* [Rent](implemented-proposals/rent.md)
|
* [Rent](implemented-proposals/rent.md)
|
||||||
* [Durable Transaction Nonces](implemented-proposals/durable-tx-nonces.md)
|
* [Durable Transaction Nonces](implemented-proposals/durable-tx-nonces.md)
|
||||||
* [Validator Timestamp Oracle](implemented-proposals/validator-timestamp-oracle.md)
|
* [Validator Timestamp Oracle](implemented-proposals/validator-timestamp-oracle.md)
|
||||||
|
* [Commitment](implemented-proposals/commitment.md)
|
||||||
|
* [Snapshot Verification](implemented-proposals/snapshot-verification.md)
|
||||||
|
* [Accepted Design Proposals](proposals/README.md)
|
||||||
|
* [Ledger Replication](proposals/ledger-replication-to-implement.md)
|
||||||
|
* [Secure Vote Signing](proposals/vote-signing-to-implement.md)
|
||||||
|
* [Cluster Test Framework](proposals/cluster-test-framework.md)
|
||||||
|
* [Validator](proposals/validator-proposal.md)
|
||||||
|
* [Simple Payment and State Verification](proposals/simple-payment-and-state-verification.md)
|
||||||
|
* [Cross-Program Invocation](proposals/cross-program-invocation.md)
|
||||||
|
* [Inter-chain Transaction Verification](proposals/interchain-transaction-verification.md)
|
||||||
|
* [Snapshot Verification](proposals/snapshot-verification.md)
|
||||||
|
* [Bankless Leader](proposals/bankless-leader.md)
|
||||||
|
* [Slashing](proposals/slashing.md)
|
||||||
|
* [Tick Verification](proposals/tick-verification.md)
|
||||||
|
* [Block Confirmation](proposals/block-confirmation.md)
|
||||||
|
* [ABI Management](proposals/abi-management.md)
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
# API Reference
|
|
||||||
|
|
||||||
The following sections contain API references material you may find useful when developing applications utilizing a Solana cluster.
|
|
||||||
|
|
@@ -1,28 +0,0 @@
|
|||||||
# Blockstreamer
|
|
||||||
|
|
||||||
Solana supports a node type called an _blockstreamer_. This validator variation is intended for applications that need to observe the data plane without participating in transaction validation or ledger replication.
|
|
||||||
|
|
||||||
A blockstreamer runs without a vote signer, and can optionally stream ledger entries out to a Unix domain socket as they are processed. The JSON-RPC service still functions as on any other node.
|
|
||||||
|
|
||||||
To run a blockstreamer, include the argument `no-signer` and \(optional\) `blockstream` socket location:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ./multinode-demo/validator-x.sh --no-signer --blockstream <SOCKET>
|
|
||||||
```
|
|
||||||
|
|
||||||
The stream will output a series of JSON objects:
|
|
||||||
|
|
||||||
* An Entry event JSON object is sent when each ledger entry is processed, with the following fields:
|
|
||||||
* `dt`, the system datetime, as RFC3339-formatted string
|
|
||||||
* `t`, the event type, always "entry"
|
|
||||||
* `s`, the slot height, as unsigned 64-bit integer
|
|
||||||
* `h`, the tick height, as unsigned 64-bit integer
|
|
||||||
* `entry`, the entry, as JSON object
|
|
||||||
* A Block event JSON object is sent when a block is complete, with the following fields:
|
|
||||||
* `dt`, the system datetime, as RFC3339-formatted string
|
|
||||||
* `t`, the event type, always "block"
|
|
||||||
* `s`, the slot height, as unsigned 64-bit integer
|
|
||||||
* `h`, the tick height, as unsigned 64-bit integer
|
|
||||||
* `l`, the slot leader id, as base-58 encoded string
|
|
||||||
* `hash`, the [blockhash](terminology.md#blockhash), as base-58 encoded string
|
|
||||||
|
|
@@ -1,38 +0,0 @@
|
|||||||
# Instruction
|
|
||||||
|
|
||||||
For the purposes of building a [Transaction](../transaction.md), a more verbose instruction format is used:
|
|
||||||
|
|
||||||
* **Instruction:**
|
|
||||||
* **program\_id:** The pubkey of the on-chain program that executes the
|
|
||||||
|
|
||||||
instruction
|
|
||||||
|
|
||||||
* **accounts:** An ordered list of accounts that should be passed to
|
|
||||||
|
|
||||||
the program processing the instruction, including metadata detailing
|
|
||||||
|
|
||||||
if an account is a signer of the transaction and if it is a credit
|
|
||||||
|
|
||||||
only account.
|
|
||||||
|
|
||||||
* **data:** A byte array that is passed to the program executing the
|
|
||||||
|
|
||||||
instruction
|
|
||||||
|
|
||||||
A more compact form is actually included in a `Transaction`:
|
|
||||||
|
|
||||||
* **CompiledInstruction:**
|
|
||||||
* **program\_id\_index:** The index of the `program_id` in the
|
|
||||||
|
|
||||||
`account_keys` list
|
|
||||||
|
|
||||||
* **accounts:** An ordered list of indices into `account_keys`
|
|
||||||
|
|
||||||
specifying the accounds that should be passed to the program
|
|
||||||
|
|
||||||
processing the instruction.
|
|
||||||
|
|
||||||
* **data:** A byte array that is passed to the program executing the
|
|
||||||
|
|
||||||
instruction
|
|
||||||
|
|
@@ -1,62 +0,0 @@
|
|||||||
# Transaction
|
|
||||||
|
|
||||||
## Components of a `Transaction`
|
|
||||||
|
|
||||||
* **Transaction:**
|
|
||||||
* **message:** Defines the transaction
|
|
||||||
* **header:** Details the account types of and signatures required by
|
|
||||||
|
|
||||||
the transaction
|
|
||||||
|
|
||||||
* **num\_required\_signatures:** The total number of signatures
|
|
||||||
|
|
||||||
required to make the transaction valid.
|
|
||||||
|
|
||||||
* **num\_credit\_only\_signed\_accounts:** The last
|
|
||||||
|
|
||||||
`num_readonly_signed_accounts` signatures refer to signing
|
|
||||||
|
|
||||||
credit only accounts. Credit only accounts can be used concurrently
|
|
||||||
|
|
||||||
by multiple parallel transactions, but their balance may only be
|
|
||||||
|
|
||||||
increased, and their account data is read-only.
|
|
||||||
|
|
||||||
* **num\_credit\_only\_unsigned\_accounts:** The last
|
|
||||||
|
|
||||||
`num_readonly_unsigned_accounts` public keys in `account_keys` refer
|
|
||||||
|
|
||||||
to non-signing credit only accounts
|
|
||||||
|
|
||||||
* **account\_keys:** List of public keys used by the transaction, including
|
|
||||||
|
|
||||||
by the instructions and for signatures. The first
|
|
||||||
|
|
||||||
`num_required_signatures` public keys must sign the transaction.
|
|
||||||
|
|
||||||
* **recent\_blockhash:** The ID of a recent ledger entry. Validators will
|
|
||||||
|
|
||||||
reject transactions with a `recent_blockhash` that is too old.
|
|
||||||
|
|
||||||
* **instructions:** A list of [instructions](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/instruction.md) that are
|
|
||||||
|
|
||||||
run sequentially and committed in one atomic transaction if all
|
|
||||||
|
|
||||||
succeed.
|
|
||||||
* **signatures:** A list of signatures applied to the transaction. The
|
|
||||||
|
|
||||||
list is always of length `num_required_signatures`, and the signature
|
|
||||||
|
|
||||||
at index `i` corresponds to the public key at index `i` in `account_keys`.
|
|
||||||
|
|
||||||
The list is initialized with empty signatures \(i.e. zeros\), and
|
|
||||||
|
|
||||||
populated as signatures are added.
|
|
||||||
|
|
||||||
## Transaction Signing
|
|
||||||
|
|
||||||
A `Transaction` is signed by using an ed25519 keypair to sign the serialization of the `message`. The resulting signature is placed at the index of `signatures` matching the index of the keypair's public key in `account_keys`.
|
|
||||||
|
|
||||||
## Transaction Serialization
|
|
||||||
|
|
||||||
`Transaction`s \(and their `message`s\) are serialized and deserialized using the [bincode](https://crates.io/crates/bincode) crate with a non-standard vector serialization that uses only one byte for the length if it can be encoded in 7 bits, 2 bytes if it fits in 14 bits, or 3 bytes if it requires 15 or 16 bits. The vector serialization is defined by Solana's [short-vec](https://github.com/solana-labs/solana/blob/master/sdk/src/short_vec.rs).
|
|
@@ -1,6 +1,20 @@
|
|||||||
# Programming Model
|
# Programming Model
|
||||||
|
|
||||||
A client _app_ interacts with a Solana cluster by sending it _transactions_ with one or more _instructions_. The Solana _runtime_ passes those instructions to user-contributed _programs_. An instruction might, for example, tell a program to transfer _lamports_ from one _account_ to another or create an interactive contract that governs how lamports are transfered. Instructions are executed atomically. If any instruction is invalid, any changes made within the transaction are discarded.
|
An _app_ interacts with a Solana cluster by sending it _transactions_ with one or more _instructions_. The Solana _runtime_ passes those instructions to user-contributed _programs_. An instruction might, for example, tell a program to transfer _lamports_ from one _account_ to another or create an interactive contract that governs how lamports are transfered. Instructions are executed sequentially and atomically. If any instruction is invalid, any changes made within the transaction are discarded.
|
||||||
|
|
||||||
|
### Accounts and Signatures
|
||||||
|
|
||||||
|
Each transaction explicitly lists all account public keys referenced by the transaction's instructions. A subset of those public keys are each accompanied by a transaction signature. Those signatures signal on-chain programs that the account holder has authorized the transaction. Typically, the program uses the authorization to permit debiting the account or modifying its data.
|
||||||
|
|
||||||
|
The transaction also marks some accounts as _read-only accounts_. The runtime permits read-only accounts to be read concurrently. If a program attempts to modify a read-only account, the transaction is rejected by the runtime.
|
||||||
|
|
||||||
|
### Recent Blockhash
|
||||||
|
|
||||||
|
A Transaction includes a recent blockhash to prevent duplication and to give transactions lifetimes. Any transaction that is completely identical to a previous one is rejected, so adding a newer blockhash allows multiple transactions to repeat the exact same action. Transactions also have lifetimes that are defined by the blockhash, as any transaction whose blockhash is too old will be rejected.
|
||||||
|
|
||||||
|
### Instructions
|
||||||
|
|
||||||
|
Each instruction specifies a single program account \(which must be marked executable\), a subset of the transaction's accounts that should be passed to the program, and a data byte array instruction that is passed to the program. The program interprets the data array and operates on the accounts specified by the instructions. The program can return successfully, or with an error code. An error return causes the entire transaction to fail immediately.
|
||||||
|
|
||||||
## Deploying Programs to a Cluster
|
## Deploying Programs to a Cluster
|
||||||
|
|
@@ -16,6 +16,8 @@ Creator of on-chain game tic-tac-toe hosts a drone that responds to airdrop requ
|
|||||||
|
|
||||||
Creator of a new on-chain token \(ERC-20 interface\), may wish to do a worldwide airdrop to distribute its tokens to millions of users over just a few seconds. That drone cannot spend resources interacting with the Solana cluster. Instead, the drone should only verify the client is unique and human, and then return the signature. It may also want to listen to the Solana cluster for recent entry IDs to support client retries and to ensure the airdrop is targeting the desired cluster.
|
Creator of a new on-chain token \(ERC-20 interface\), may wish to do a worldwide airdrop to distribute its tokens to millions of users over just a few seconds. That drone cannot spend resources interacting with the Solana cluster. Instead, the drone should only verify the client is unique and human, and then return the signature. It may also want to listen to the Solana cluster for recent entry IDs to support client retries and to ensure the airdrop is targeting the desired cluster.
|
||||||
|
|
||||||
|
Note: the Solana cluster will not parallelize transactions funded by the same fee-paying account. This means that the max throughput of a single fee-paying account is limited to the number of _ticks_ processed per second by the current leader. Add additional fee-paying accounts to improve throughput.
|
||||||
|
|
||||||
## Attack vectors
|
## Attack vectors
|
||||||
|
|
||||||
### Invalid recent\_blockhash
|
### Invalid recent\_blockhash
|
@@ -25,15 +25,18 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
|
|||||||
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
|
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
|
||||||
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
|
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
|
||||||
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
|
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
|
||||||
|
* [getInflation](jsonrpc-api.md#getinflation)
|
||||||
* [getLeaderSchedule](jsonrpc-api.md#getleaderschedule)
|
* [getLeaderSchedule](jsonrpc-api.md#getleaderschedule)
|
||||||
* [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption)
|
* [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption)
|
||||||
* [getNumBlocksSinceSignatureConfirmation](jsonrpc-api.md#getnumblockssincesignatureconfirmation)
|
* [getNumBlocksSinceSignatureConfirmation](jsonrpc-api.md#getnumblockssincesignatureconfirmation)
|
||||||
* [getProgramAccounts](jsonrpc-api.md#getprogramaccounts)
|
* [getProgramAccounts](jsonrpc-api.md#getprogramaccounts)
|
||||||
* [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash)
|
* [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash)
|
||||||
|
* [getSignatureConfirmation](jsonrpc-api.md#getsignatureconfirmation)
|
||||||
* [getSignatureStatus](jsonrpc-api.md#getsignaturestatus)
|
* [getSignatureStatus](jsonrpc-api.md#getsignaturestatus)
|
||||||
* [getSlot](jsonrpc-api.md#getslot)
|
* [getSlot](jsonrpc-api.md#getslot)
|
||||||
* [getSlotLeader](jsonrpc-api.md#getslotleader)
|
* [getSlotLeader](jsonrpc-api.md#getslotleader)
|
||||||
* [getSlotsPerSegment](jsonrpc-api.md#getslotspersegment)
|
* [getSlotsPerSegment](jsonrpc-api.md#getslotspersegment)
|
||||||
|
* [getStoragePubkeysForSlot](jsonrpc-api.md#getstoragepubkeysforslot)
|
||||||
* [getStorageTurn](jsonrpc-api.md#getstorageturn)
|
* [getStorageTurn](jsonrpc-api.md#getstorageturn)
|
||||||
* [getStorageTurnRate](jsonrpc-api.md#getstorageturnrate)
|
* [getStorageTurnRate](jsonrpc-api.md#getstorageturnrate)
|
||||||
* [getTransactionCount](jsonrpc-api.md#gettransactioncount)
|
* [getTransactionCount](jsonrpc-api.md#gettransactioncount)
|
||||||
@@ -43,7 +46,8 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
|
|||||||
* [minimumLedgerSlot](jsonrpc-api.md#minimumledgerslot)
|
* [minimumLedgerSlot](jsonrpc-api.md#minimumledgerslot)
|
||||||
* [requestAirdrop](jsonrpc-api.md#requestairdrop)
|
* [requestAirdrop](jsonrpc-api.md#requestairdrop)
|
||||||
* [sendTransaction](jsonrpc-api.md#sendtransaction)
|
* [sendTransaction](jsonrpc-api.md#sendtransaction)
|
||||||
* [startSubscriptionChannel](jsonrpc-api.md#startsubscriptionchannel)
|
* [setLogFilter](jsonrpc-api.md#setlogfilter)
|
||||||
|
* [validatorExit](jsonrpc-api.md#validatorexit)
|
||||||
* [Subscription Websocket](jsonrpc-api.md#subscription-websocket)
|
* [Subscription Websocket](jsonrpc-api.md#subscription-websocket)
|
||||||
* [accountSubscribe](jsonrpc-api.md#accountsubscribe)
|
* [accountSubscribe](jsonrpc-api.md#accountsubscribe)
|
||||||
* [accountUnsubscribe](jsonrpc-api.md#accountunsubscribe)
|
* [accountUnsubscribe](jsonrpc-api.md#accountunsubscribe)
|
||||||
@@ -51,15 +55,17 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
|
|||||||
* [programUnsubscribe](jsonrpc-api.md#programunsubscribe)
|
* [programUnsubscribe](jsonrpc-api.md#programunsubscribe)
|
||||||
* [signatureSubscribe](jsonrpc-api.md#signaturesubscribe)
|
* [signatureSubscribe](jsonrpc-api.md#signaturesubscribe)
|
||||||
* [signatureUnsubscribe](jsonrpc-api.md#signatureunsubscribe)
|
* [signatureUnsubscribe](jsonrpc-api.md#signatureunsubscribe)
|
||||||
|
* [slotSubscribe](jsonrpc-api.md#slotsubscribe)
|
||||||
|
* [slotUnsubscribe](jsonrpc-api.md#slotunsubscribe)
|
||||||
|
|
||||||
## Request Formatting
|
## Request Formatting
|
||||||
|
|
||||||
To make a JSON-RPC request, send an HTTP POST request with a `Content-Type: application/json` header. The JSON request data should contain 4 fields:
|
To make a JSON-RPC request, send an HTTP POST request with a `Content-Type: application/json` header. The JSON request data should contain 4 fields:
|
||||||
|
|
||||||
* `jsonrpc`, set to `"2.0"`
|
* `jsonrpc: <string>`, set to `"2.0"`
|
||||||
* `id`, a unique client-generated identifying integer
|
* `id: <number>`, a unique client-generated identifying integer
|
||||||
* `method`, a string containing the method to be invoked
|
* `method: <string>`, a string containing the method to be invoked
|
||||||
* `params`, a JSON array of ordered parameter values
|
* `params: <array>`, a JSON array of ordered parameter values
|
||||||
|
|
||||||
Example using curl:
|
Example using curl:
|
||||||
|
|
||||||
@@ -69,9 +75,9 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "
|
|||||||
|
|
||||||
The response output will be a JSON object with the following fields:
|
The response output will be a JSON object with the following fields:
|
||||||
|
|
||||||
* `jsonrpc`, matching the request specification
|
* `jsonrpc: <string>`, matching the request specification
|
||||||
* `id`, matching the request identifier
|
* `id: <number>`, matching the request identifier
|
||||||
* `result`, requested data or success confirmation
|
* `result: <array|number|object|string>`, requested data or success confirmation
|
||||||
|
|
||||||
Requests can be sent in batches by sending an array of JSON-RPC request objects as the data for a single POST.
|
Requests can be sent in batches by sending an array of JSON-RPC request objects as the data for a single POST.
|
||||||
|
|
||||||
@@ -115,12 +121,12 @@ Returns a transaction receipt
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Signature of Transaction to confirm, as base-58 encoded string
|
* `<string>` - Signature of Transaction to confirm, as base-58 encoded string
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `RpcResponse<boolean>` - RpcResponse JSON object with `value` field set to Transaction status, boolean true if Transaction is confirmed
|
* `RpcResponse<bool>` - RpcResponse JSON object with `value` field set to Transaction status, boolean true if Transaction is confirmed
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -138,18 +144,19 @@ Returns all information associated with the account of provided Pubkey
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Pubkey of account to query, as base-58 encoded string
|
* `<string>` - Pubkey of account to query, as base-58 encoded string
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result value will be an RpcResponse JSON object containing an AccountInfo JSON object.
|
The result value will be an RpcResponse JSON object containing an AccountInfo JSON object.
|
||||||
|
|
||||||
* `RpcResponse<AccountInfo>`, RpcResponse JSON object with `value` field set to AccountInfo, a JSON object containing:
|
* `RpcResponse<AccountInfo>`, RpcResponse JSON object with `value` field set to AccountInfo, a JSON object containing:
|
||||||
* `lamports`, number of lamports assigned to this account, as a u64
|
* `lamports: <u64>`, number of lamports assigned to this account, as a u64
|
||||||
* `owner`, base-58 encoded pubkey of the program this account has been assigned to
|
* `owner: <string>`, base-58 encoded Pubkey of the program this account has been assigned to
|
||||||
* `data`, base-58 encoded data associated with the account
|
* `data: <string>`, base-58 encoded data associated with the account
|
||||||
* `executable`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
* `executable: <bool>`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
||||||
|
* `rentEpoch`: <u64>, the epoch at which this account will next owe rent, as u64
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -158,7 +165,7 @@ The result value will be an RpcResponse JSON object containing an AccountInfo JS
|
|||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getAccountInfo", "params":["2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"]}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getAccountInfo", "params":["2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"]}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"executable":false,"owner":"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM","lamports":1,"data":"Joig2k8Ax4JPMpWhXRyc2jMa7Wejz4X1xqVi3i7QRkmVj1ChUgNc4VNpGUQePJGBAui3c6886peU9GEbjsyeANN8JGStprwLbLwcw5wpPjuQQb9mwrjVmoDQBjj3MzZKgeHn6wmnQ5k8DBFuoCYKWWsJfH2gv9FvCzrN6K1CRcQZzF"}},"id":1}
|
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"executable":false,"owner":"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM","lamports":1,"data":"Joig2k8Ax4JPMpWhXRyc2jMa7Wejz4X1xqVi3i7QRkmVj1ChUgNc4VNpGUQePJGBAui3c6886peU9GEbjsyeANN8JGStprwLbLwcw5wpPjuQQb9mwrjVmoDQBjj3MzZKgeHn6wmnQ5k8DBFuoCYKWWsJfH2gv9FvCzrN6K1CRcQZzF","rentEpoch":2}},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getBalance
|
### getBalance
|
||||||
@@ -167,12 +174,12 @@ Returns the balance of the account of provided Pubkey
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Pubkey of account to query, as base-58 encoded string
|
* `<string>` - Pubkey of account to query, as base-58 encoded string
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `RpcResponse<u64>` - RpcResponse JSON object with `value` field set to quantity
|
* `RpcResponse<u64>` - RpcResponse JSON object with `value` field set to the balance
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -190,16 +197,15 @@ Returns commitment for particular block
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `u64` - block, identified by Slot
|
* `<u64>` - block, identified by Slot
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result field will be a JSON object containing:
|
The result field will be a JSON object containing:
|
||||||
|
|
||||||
* `commitment` - commitment, comprising either:
|
* `commitment` - commitment, comprising either:
|
||||||
* `null` - Unknown block
|
* `<null>` - Unknown block
|
||||||
* `object` - BlockCommitment
|
* `<array>` - commitment, array of u64 integers logging the amount of cluster stake in lamports that has voted on the block at each depth from 0 to `MAX_LOCKOUT_HISTORY`
|
||||||
* `array` - commitment, array of u64 integers logging the amount of cluster stake in lamports that has voted on the block at each depth from 0 to `MAX_LOCKOUT_HISTORY`
|
|
||||||
* `totalStake` - total active stake, in lamports, of the current epoch
|
* `totalStake` - total active stake, in lamports, of the current epoch
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
@@ -209,7 +215,7 @@ The result field will be a JSON object containing:
|
|||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getBlockCommitment","params":[5]}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getBlockCommitment","params":[5]}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":[{"commitment":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,32]},42],"id":1}
|
{"jsonrpc":"2.0","result":{"commitment":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,32],"totalStake": 42},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getBlockTime
|
### getBlockTime
|
||||||
@@ -228,12 +234,12 @@ query a node that is built from genesis and retains the entire ledger.
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `u64` - block, identified by Slot
|
* `<u64>` - block, identified by Slot
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `null` - block has not yet been produced
|
* `<null>` - block has not yet been produced
|
||||||
* `i64` - estimated production time, as Unix timestamp (seconds since the Unix epoch)
|
* `<i64>` - estimated production time, as Unix timestamp (seconds since the Unix epoch)
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -257,10 +263,10 @@ None
|
|||||||
|
|
||||||
The result field will be an array of JSON objects, each with the following sub fields:
|
The result field will be an array of JSON objects, each with the following sub fields:
|
||||||
|
|
||||||
* `pubkey` - Node public key, as base-58 encoded string
|
* `pubkey: <string>` - Node public key, as base-58 encoded string
|
||||||
* `gossip` - Gossip network address for the node
|
* `gossip: <string>` - Gossip network address for the node
|
||||||
* `tpu` - TPU network address for the node
|
* `tpu: <string>` - TPU network address for the node
|
||||||
* `rpc` - JSON RPC network address for the node, or `null` if the JSON RPC service is not enabled
|
* `rpc: <string>` - JSON RPC network address for the node, or `null` if the JSON RPC service is not enabled
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -278,25 +284,28 @@ Returns identity and transaction information about a confirmed block in the ledg
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `integer` - slot, as u64 integer
|
* `<u64>` - slot, as u64 integer
|
||||||
* `string` - (optional) encoding for each returned Transaction, either "json" or "binary". If not provided, the default encoding is JSON.
|
* `<string>` - (optional) encoding for each returned Transaction, either "json" or "binary". If not provided, the default encoding is JSON.
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result field will be an object with the following fields:
|
The result field will be an object with the following fields:
|
||||||
|
|
||||||
* `blockhash` - the blockhash of this block, as base-58 encoded string
|
* `blockhash: <string>` - the blockhash of this block, as base-58 encoded string
|
||||||
* `previousBlockhash` - the blockhash of this block's parent, as base-58 encoded string
|
* `previousBlockhash: <string>` - the blockhash of this block's parent, as base-58 encoded string
|
||||||
* `parentSlot` - the slot index of this block's parent
|
* `parentSlot: <u64>` - the slot index of this block's parent
|
||||||
* `transactions` - an array of JSON objects containing:
|
* `transactions: <array>` - an array of JSON objects containing:
|
||||||
* `transaction` - [Transaction](transaction-api.md) object, either in JSON format or base-58 encoded binary data, depending on encoding parameter
|
* `transaction: <object|string>` - [Transaction](transaction-api.md) object, either in JSON format or base-58 encoded binary data, depending on encoding parameter
|
||||||
* `meta` - transaction status metadata object, containing `null` or:
|
* `meta: <object>` - transaction status metadata object, containing `null` or:
|
||||||
* `status` - Transaction status:
|
* `status: <object>` - Transaction status:
|
||||||
* `"Ok": null` - Transaction was successful
|
* `"Ok": null` - Transaction was successful
|
||||||
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||||
* `fee` - fee this transaction was charged, as u64 integer
|
* `fee: <u64>` - fee this transaction was charged, as u64 integer
|
||||||
* `preBalances` - array of u64 account balances from before the transaction was processed
|
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed
|
||||||
* `postBalances` - array of u64 account balances after the transaction was processed
|
* `postBalances: <array>` - array of u64 account balances after the transaction was processed
|
||||||
|
* `rewards: <array>` - an array of JSON objects containing:
|
||||||
|
* `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward
|
||||||
|
* `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -305,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":[[{"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"]},{"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":18000,"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":[["81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ",{"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":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getConfirmedBlocks
|
### getConfirmedBlocks
|
||||||
@@ -320,8 +329,8 @@ Returns a list of confirmed blocks
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `integer` - start_slot, as u64 integer
|
* `<u64>` - start_slot, as u64 integer
|
||||||
* `integer` - (optional) end_slot, as u64 integer
|
* `<u64>` - (optional) end_slot, as u64 integer
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
@@ -345,15 +354,16 @@ Returns information about the current epoch
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result field will be an object with the following fields:
|
The result field will be an object with the following fields:
|
||||||
|
|
||||||
* `epoch`, the current epoch
|
* `absoluteSlot: <u64>`, the current slot
|
||||||
* `slotIndex`, the current slot relative to the start of the current epoch
|
* `epoch: <u64>`, the current epoch
|
||||||
* `slotsInEpoch`, the number of slots in this epoch
|
* `slotIndex: <u64>`, the current slot relative to the start of the current epoch
|
||||||
|
* `slotsInEpoch: <u64>`, the number of slots in this epoch
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -362,7 +372,7 @@ 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":"getEpochInfo"}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"epoch":3,"slotIndex":126,"slotsInEpoch":256},"id":1}
|
{"jsonrpc":"2.0","result":{"absoluteSlot":166598,"epoch":27,"slotIndex":2790,"slotsInEpoch":8192},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getEpochSchedule
|
### getEpochSchedule
|
||||||
@@ -377,11 +387,11 @@ None
|
|||||||
|
|
||||||
The result field will be an object with the following fields:
|
The result field will be an object with the following fields:
|
||||||
|
|
||||||
* `slotsPerEpoch`, the maximum number of slots in each epoch
|
* `slotsPerEpoch: <u64>`, the maximum number of slots in each epoch
|
||||||
* `leaderScheduleSlotOffset`, the number of slots before beginning of an epoch to calculate a leader schedule for that epoch
|
* `leaderScheduleSlotOffset: <u64>`, the number of slots before beginning of an epoch to calculate a leader schedule for that epoch
|
||||||
* `warmup`, whether epochs start short and grow
|
* `warmup: <bool>`, whether epochs start short and grow
|
||||||
* `firstNormalEpoch`, first normal-length epoch, log2(slotsPerEpoch) - log2(MINIMUM_SLOTS_PER_EPOCH)
|
* `firstNormalEpoch: <u64>`, first normal-length epoch, log2(slotsPerEpoch) - log2(MINIMUM_SLOTS_PER_EPOCH)
|
||||||
* `firstNormalSlot`, MINIMUM_SLOTS_PER_EPOCH * (2.pow(firstNormalEpoch) - 1)
|
* `firstNormalSlot: <u64>`, MINIMUM_SLOTS_PER_EPOCH * (2.pow(firstNormalEpoch) - 1)
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -403,7 +413,7 @@ None
|
|||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `string` - a Hash as base-58 encoded string
|
* `<string>` - a Hash as base-58 encoded string
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -415,14 +425,43 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
|||||||
{"jsonrpc":"2.0","result":"GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC","id":1}
|
{"jsonrpc":"2.0","result":"GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC","id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### getInflation
|
||||||
|
|
||||||
|
Returns the inflation configuration of the cluster
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
The result field will be an Inflation object with the following fields:
|
||||||
|
|
||||||
|
* `initial: <f64>`, the initial inflation percentage from time 0
|
||||||
|
* `terminal: <f64>`, terminal inflation percentage
|
||||||
|
* `taper: <f64>`, rate per year at which inflation is lowered
|
||||||
|
* `foundation: <f64>`, percentage of total inflation allocated to the foundation
|
||||||
|
* `foundationTerm: <f64>`, duration of foundation pool inflation in years
|
||||||
|
* `storage: <f64>`, percentage of total inflation allocated to storage rewards
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getInflation"}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":{"foundation":0.05,"foundationTerm":7.0,"initial":0.15,"storage":0.1,"taper":0.15,"terminal":0.015},"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
### getLeaderSchedule
|
### getLeaderSchedule
|
||||||
|
|
||||||
Returns the leader schedule for an epoch
|
Returns the leader schedule for an epoch
|
||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `slot` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. If unspecified, the leader schedule for the current epoch is fetched
|
* `<u64>` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. If unspecified, the leader schedule for the current epoch is fetched
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
@@ -446,12 +485,12 @@ Returns minimum balance required to make account rent exempt.
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `u64` - account data length
|
* `<usize>` - account data length
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `u64` - minimum lamports required in account
|
* `<u64>` - minimum lamports required in account
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -469,12 +508,12 @@ Returns the current number of blocks since signature has been confirmed.
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Signature of Transaction to confirm, as base-58 encoded string
|
* `<string>` - Signature of Transaction to confirm, as base-58 encoded string
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `u64` - count
|
* `<u64>` - count, or null if signature not found
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -492,18 +531,20 @@ Returns all accounts owned by the provided program Pubkey
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Pubkey of program, as base-58 encoded string
|
* `<string>` - Pubkey of program, as base-58 encoded string
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result field will be an array of arrays. Each sub array will contain:
|
The result field will be an array of JSON objects, which will contain:
|
||||||
|
|
||||||
* `string` - the account Pubkey as base-58 encoded string and a JSON object, with the following sub fields:
|
* `pubkey: <string>` - the account Pubkey as base-58 encoded string
|
||||||
* `lamports`, number of lamports assigned to this account, as a u64
|
* `account: <object>` - a JSON object, with the following sub fields:
|
||||||
* `owner`, base-58 encoded pubkey of the program this account has been assigned to
|
* `lamports: <u64>`, number of lamports assigned to this account, as a u64
|
||||||
* `data`, base-58 encoded data associated with the account
|
* `owner: <string>`, base-58 encoded Pubkey of the program this account has been assigned to
|
||||||
* `executable`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
* `data: <string>`, base-58 encoded data associated with the account
|
||||||
|
* `executable: <bool>`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
||||||
|
* `rentEpoch`: <u64>, the epoch at which this account will next owe rent, as u64
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -512,7 +553,7 @@ The result field will be an array of arrays. Each sub array will contain:
|
|||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getProgramAccounts", "params":["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T"]}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getProgramAccounts", "params":["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T"]}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":[["BqGKYtAKu69ZdWEBtZHh4xgJY1BYa2YBiBReQE3pe383", {"executable":false,"owner":"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T","lamports":1,"data":"", ["8nQwAgzN2yyUzrukXsCa3JELBYqDQrqJ3UyHiWazWxHR", {"executable":false,"owner":"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T","lamports":10,"data":[]]]},"id":1}
|
{"jsonrpc":"2.0","result":[{"account":{"data":"2R9jLfiAQ9bgdcw6h8s44439","executable":false,"lamports":15298080,"owner":"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T","rentEpoch":28},"pubkey":"CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY"}],"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getRecentBlockhash
|
### getRecentBlockhash
|
||||||
@@ -521,15 +562,15 @@ Returns a recent block hash from the ledger, and a fee schedule that can be used
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
An RpcResponse containing a JSON object consisting of a string blockhash and FeeCalculator JSON object.
|
An RpcResponse containing a JSON object consisting of a string blockhash and FeeCalculator JSON object.
|
||||||
|
|
||||||
* `RpcResponse<array>` - RpcResponse JSON object with `value` field set to a JSON object including:
|
* `RpcResponse<object>` - RpcResponse JSON object with `value` field set to a JSON object including:
|
||||||
* `blockhash` - a Hash as base-58 encoded string
|
* `blockhash: <string>` - a Hash as base-58 encoded string
|
||||||
* `feeCalculator` - FeeCalculator object, the fee schedule for this block hash
|
* `feeCalculator: <object>` - FeeCalculator object, the fee schedule for this block hash
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -538,7 +579,34 @@ 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": "GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC","feeCalculator":{"lamportsPerSignature": 0}}},"id":1}
|
{"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}
|
||||||
|
```
|
||||||
|
|
||||||
|
### getSignatureConfirmation
|
||||||
|
|
||||||
|
Returns the status and number of confirmations of a given signature.
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
* `<string>` - Signature of Transaction to confirm, as base-58 encoded string
|
||||||
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
* `<null>` - Unknown transaction
|
||||||
|
* `<object>` - Transaction confirmations and status:
|
||||||
|
* `confirmations: <u64>` - count of confirmations since transaction was processed
|
||||||
|
* `status: <object>` -
|
||||||
|
* `"Ok": <null>` - Transaction was successful
|
||||||
|
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureConfirmation", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":{"confirmations":12,"status":{"Ok": null}},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getSignatureStatus
|
### getSignatureStatus
|
||||||
@@ -547,14 +615,14 @@ Returns the status of a given signature. This method is similar to [confirmTrans
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Signature of Transaction to confirm, as base-58 encoded string
|
* `<string>` - Signature of Transaction to confirm, as base-58 encoded string
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `null` - Unknown transaction
|
* `<null>` - Unknown transaction
|
||||||
* `object` - Transaction status:
|
* `<object>` - Transaction status:
|
||||||
* `"Ok": null` - Transaction was successful
|
* `"Ok": <null>` - Transaction was successful
|
||||||
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
@@ -564,7 +632,7 @@ Returns the status of a given signature. This method is similar to [confirmTrans
|
|||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":"SignatureNotFound","id":1}
|
{"jsonrpc":"2.0","result":{"Ok": null},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getSlot
|
### getSlot
|
||||||
@@ -573,11 +641,11 @@ Returns the current slot the node is processing
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `u64` - Current slot
|
* `<u64>` - Current slot
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -595,11 +663,11 @@ Returns the current slot leader
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `string` - Node Id as base-58 encoded string
|
* `<string>` - Node identity Pubkey as base-58 encoded string
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -617,11 +685,11 @@ Returns the current storage segment size in terms of slots
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `u64` - Number of slots in a storage segment
|
* `<u64>` - Number of slots in a storage segment
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -632,6 +700,27 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
|||||||
{"jsonrpc":"2.0","result":1024,"id":1}
|
{"jsonrpc":"2.0","result":1024,"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### getStoragePubkeysForSlot
|
||||||
|
|
||||||
|
Returns the storage Pubkeys for a particular slot
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
An array of Pubkeys, as base-58 encoded strings
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getStoragePubkeysForSlot","params":[1]}' http://localhost:8899
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":["GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC"],"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
### getStorageTurn
|
### getStorageTurn
|
||||||
|
|
||||||
Returns the current storage turn's blockhash and slot
|
Returns the current storage turn's blockhash and slot
|
||||||
@@ -644,8 +733,8 @@ None
|
|||||||
|
|
||||||
A JSON object consisting of
|
A JSON object consisting of
|
||||||
|
|
||||||
* `blockhash` - a Hash as base-58 encoded string indicating the blockhash of the turn slot
|
* `blockhash: <string>` - a Hash as base-58 encoded string indicating the blockhash of the turn slot
|
||||||
* `slot` - the current storage turn slot
|
* `slot: <u64>` - the current storage turn slot
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -653,7 +742,7 @@ A JSON object consisting of
|
|||||||
// Request
|
// Request
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getStorageTurn"}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getStorageTurn"}' http://localhost:8899
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"blockhash": "GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC", "slot": "2048"},"id":1}
|
{"jsonrpc":"2.0","result":{"blockhash": "GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC", "slot": 2048},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getStorageTurnRate
|
### getStorageTurnRate
|
||||||
@@ -666,7 +755,7 @@ None
|
|||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `u64` - Number of slots in storage turn
|
* `<u64>` - Number of slots in storage turn
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -683,11 +772,11 @@ Returns the current Transaction count from the ledger
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `u64` - count
|
* `<u64>` - count
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -701,15 +790,15 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
|||||||
|
|
||||||
### getTotalSupply
|
### getTotalSupply
|
||||||
|
|
||||||
Returns the current total supply in Lamports
|
Returns the current total supply in lamports
|
||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `u64` - Total supply
|
* `<u64>` - Total supply
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -731,7 +820,7 @@ None
|
|||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result field will be a JSON object with the following sub fields:
|
The result field will be a JSON object with the following fields:
|
||||||
|
|
||||||
* `solana-core`, software version of solana-core
|
* `solana-core`, software version of solana-core
|
||||||
|
|
||||||
@@ -741,7 +830,7 @@ The result field will be a JSON object with the following sub 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": "0.17.2"},"id":1}
|
{"jsonrpc":"2.0","result":{"solana-core": "0.23.5"},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getVoteAccounts
|
### getVoteAccounts
|
||||||
@@ -750,18 +839,19 @@ Returns the account info and associated stake for all the voting accounts in the
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result field will be a JSON object of `current` and `delinquent` accounts, each containing an array of JSON objects with the following sub fields:
|
The result field will be a JSON object of `current` and `delinquent` accounts, each containing an array of JSON objects with the following sub fields:
|
||||||
|
|
||||||
* `votePubkey` - Vote account public key, as base-58 encoded string
|
* `votePubkey: <string>` - Vote account public key, as base-58 encoded string
|
||||||
* `nodePubkey` - Node public key, as base-58 encoded string
|
* `nodePubkey: <string>` - Node public key, as base-58 encoded string
|
||||||
* `activatedStake` - the stake, in lamports, delegated to this vote account and active in this epoch
|
* `activatedStake: <u64>` - the stake, in lamports, delegated to this vote account and active in this epoch
|
||||||
* `epochVoteAccount` - bool, whether the vote account is staked for this epoch
|
* `epochVoteAccount: <bool>` - bool, whether the vote account is staked for this epoch
|
||||||
* `commission`, percentage (0-100) of rewards payout owed to the vote account
|
* `commission: <number>`, percentage (0-100) of rewards payout owed to the vote account
|
||||||
* `lastVote` - Most recent slot voted on by this vote account
|
* `lastVote: <u64>` - Most recent slot voted on by this vote account
|
||||||
|
* `epochCredits: <array>` - History of how many credits earned by the end of each epoch, as an array of arrays containing: [epoch, credits, previousCredits]
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -770,7 +860,7 @@ The result field will be a JSON object of `current` and `delinquent` accounts, e
|
|||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"current":[{"commission":0,"epochVoteAccount":true,"nodePubkey":"B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD","lastVote":147,"activatedStake":42,"votePubkey":"3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"}],"delinquent":[{"commission":127,"epochVoteAccount":false,"nodePubkey":"6ZPxeQaDo4bkZLRsdNrCzchNQr5LN9QMc9sipXv9Kw8f","lastVote":0,"activatedStake":0,"votePubkey":"CmgCk4aMS7KW1SHX3s9K5tBJ6Yng2LBaC8MFov4wx9sm"}]},"id":1}
|
{"jsonrpc":"2.0","result":{"current":[{"commission":0,"epochVoteAccount":true,"epochCredits":[[1,64,0],[2,192,64]],"nodePubkey":"B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD","lastVote":147,"activatedStake":42,"votePubkey":"3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"}],"delinquent":[{"commission":127,"epochVoteAccount":false,"epochCredits":[],"nodePubkey":"6ZPxeQaDo4bkZLRsdNrCzchNQr5LN9QMc9sipXv9Kw8f","lastVote":0,"activatedStake":0,"votePubkey":"CmgCk4aMS7KW1SHX3s9K5tBJ6Yng2LBaC8MFov4wx9sm"}]},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### minimumLedgerSlot
|
### minimumLedgerSlot
|
||||||
@@ -802,13 +892,13 @@ Requests an airdrop of lamports to a Pubkey
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Pubkey of account to receive lamports, as base-58 encoded string
|
* `<string>` - Pubkey of account to receive lamports, as base-58 encoded string
|
||||||
* `integer` - lamports, as a u64
|
* `<integer>` - lamports, as a u64
|
||||||
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) (used for retrieving blockhash and verifying airdrop success)
|
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) (used for retrieving blockhash and verifying airdrop success)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `string` - Transaction Signature of airdrop, as base-58 encoded string
|
* `<string>` - Transaction Signature of airdrop, as base-58 encoded string
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -826,22 +916,66 @@ Creates new transaction
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `array` - array of octets containing a fully-signed Transaction
|
* `<array>` - array of octets containing a fully-signed Transaction
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `string` - Transaction Signature, as base-58 encoded string
|
* `<string>` - Transaction Signature, as base-58 encoded string
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
// Request
|
// Request
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"sendTransaction", "params":[[61, 98, 55, 49, 15, 187, 41, 215, 176, 49, 234, 229, 228, 77, 129, 221, 239, 88, 145, 227, 81, 158, 223, 123, 14, 229, 235, 247, 191, 115, 199, 71, 121, 17, 32, 67, 63, 209, 239, 160, 161, 2, 94, 105, 48, 159, 235, 235, 93, 98, 172, 97, 63, 197, 160, 164, 192, 20, 92, 111, 57, 145, 251, 6, 40, 240, 124, 194, 149, 155, 16, 138, 31, 113, 119, 101, 212, 128, 103, 78, 191, 80, 182, 234, 216, 21, 121, 243, 35, 100, 122, 68, 47, 57, 13, 39, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 40, 240, 124, 194, 149, 155, 16, 138, 31, 113, 119, 101, 212, 128, 103, 78, 191, 80, 182, 234, 216, 21, 121, 243, 35, 100, 122, 68, 47, 57, 11, 12, 106, 49, 74, 226, 201, 16, 161, 192, 28, 84, 124, 97, 190, 201, 171, 186, 6, 18, 70, 142, 89, 185, 176, 154, 115, 61, 26, 163, 77, 1, 88, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"sendTransaction", "params":["3gKEMTuxvm3DKEJc4UyiyoNz1sxwdVRW2pyDDXqaCvUjGApnsazGh2y4W92zuaSSdJhBbWLYAkZokBt4N5oW27R7zCVaLLpLxvATL2GgheEh9DmmDR1P9r1ZqirVXM2fF3z5cafmc4EtwWc1UErFdCWj1qYvy4bDGMLXRYLURxaKytEEqrxz6JXj8rUHhDpjTZeFxmC6iAW3hZr6cmaAzewQCQfiEv2HfydriwHDtN95u3Y1EF6SuXxcRqox2aTjGye2Ln9zFj4XbnAtjCmkZhR"]}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":"2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b","id":1}
|
{"jsonrpc":"2.0","result":"2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b","id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### setLogFilter
|
||||||
|
|
||||||
|
Sets the log filter on the validator
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
* `<string>` - the new log filter to use
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
* `<null>`
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"setLogFilter", "params":["solana_core=debug"]}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":null,"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
### validatorExit
|
||||||
|
|
||||||
|
If a validator boots with RPC exit enabled (`--enable-rpc-exit` parameter), this request causes the validator to exit.
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
* `<bool>` - Whether the validator exit operation was successful
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"validatorExit"}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":true,"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
### Subscription Websocket
|
### Subscription Websocket
|
||||||
|
|
||||||
After connect to the RPC PubSub websocket at `ws://<ADDRESS>/`:
|
After connect to the RPC PubSub websocket at `ws://<ADDRESS>/`:
|
||||||
@@ -870,14 +1004,14 @@ Subscribe to an account to receive notifications when the lamports or data for a
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - account Pubkey, as base-58 encoded string
|
* `<string>` - account Pubkey, as base-58 encoded string
|
||||||
* `integer` - optional, number of confirmed blocks to wait before notification.
|
* `<u64>` - optional, number of confirmed blocks to wait before notification.
|
||||||
|
|
||||||
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `integer` - Subscription id \(needed to unsubscribe\)
|
* `<number>` - Subscription id \(needed to unsubscribe\)
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -894,7 +1028,7 @@ Subscribe to an account to receive notifications when the lamports or data for a
|
|||||||
#### Notification Format:
|
#### Notification Format:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
{"jsonrpc": "2.0","method": "accountNotification", "params": {"result": {"executable":false,"owner":"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM","lamports":1,"data":"Joig2k8Ax4JPMpWhXRyc2jMa7Wejz4X1xqVi3i7QRkmVj1ChUgNc4VNpGUQePJGBAui3c6886peU9GEbjsyeANN8JGStprwLbLwcw5wpPjuQQb9mwrjVmoDQBjj3MzZKgeHn6wmnQ5k8DBFuoCYKWWsJfH2gv9FvCzrN6K1CRcQZzF"},"subscription":0}}
|
{"jsonrpc": "2.0","method": "accountNotification", "params": {"result": {"executable":false,"owner":"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM","lamports":1,"data":"Joig2k8Ax4JPMpWhXRyc2jMa7Wejz4X1xqVi3i7QRkmVj1ChUgNc4VNpGUQePJGBAui3c6886peU9GEbjsyeANN8JGStprwLbLwcw5wpPjuQQb9mwrjVmoDQBjj3MzZKgeHn6wmnQ5k8DBFuoCYKWWsJfH2gv9FvCzrN6K1CRcQZzF","rentEpoch":28},"subscription":0}}
|
||||||
```
|
```
|
||||||
|
|
||||||
### accountUnsubscribe
|
### accountUnsubscribe
|
||||||
@@ -903,11 +1037,11 @@ Unsubscribe from account change notifications
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `integer` - id of account Subscription to cancel
|
* `<number>` - id of account Subscription to cancel
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `bool` - unsubscribe success message
|
* `<bool>` - unsubscribe success message
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -925,14 +1059,14 @@ Subscribe to a program to receive notifications when the lamports or data for a
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - program\_id Pubkey, as base-58 encoded string
|
* `<string>` - program\_id Pubkey, as base-58 encoded string
|
||||||
* `integer` - optional, number of confirmed blocks to wait before notification.
|
* `<u64>` - optional, number of confirmed blocks to wait before notification.
|
||||||
|
|
||||||
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `integer` - Subscription id \(needed to unsubscribe\)
|
* `<integer>` - Subscription id \(needed to unsubscribe\)
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -948,11 +1082,11 @@ Subscribe to a program to receive notifications when the lamports or data for a
|
|||||||
|
|
||||||
#### Notification Format:
|
#### Notification Format:
|
||||||
|
|
||||||
* `string` - account Pubkey, as base-58 encoded string
|
* `<string>` - account Pubkey, as base-58 encoded string
|
||||||
* `object` - account info JSON object \(see [getAccountInfo](jsonrpc-api.md#getaccountinfo) for field details\)
|
* `<object>` - account info JSON object \(see [getAccountInfo](jsonrpc-api.md#getaccountinfo) for field details\)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
{"jsonrpc":"2.0","method":"programNotification","params":{{"result":{"pubkey": "8Rshv2oMkPu5E4opXTRyuyBeZBqQ4S477VG26wUTFxUM","account":{"executable":false,"lamports":1,"owner":"9gZbPtbtHrs6hEWgd6MbVY9VPFtS5Z8xKtnYwA2NynHV","data":"4SZWhnbSt3njU4QHVgPrWeekz1BudU4ttmdr9ezmrL4X6XeLeL83xVAo6ZdxwU3oXgHNeF2q6tWZbnVnBXmvNyeLVEGt8ZQ4ZmgjHfVNCEwBtzh2aDrHgQSjBFLYAdmM3uwBhcm1EyHJLeUiFqpsoAUhn6Vphwrpf44dWRAGsAJZbzvVrUW9bfucpR7xudHHg2MxQ2CdqsfS3TfWUJY3vaf2A4AUNzfAmNPHBGi99nU2hYubGSVSPcpVPpdRWQkydgqasBmTosd"}},"subscription":0}}
|
{"jsonrpc":"2.0","method":"programNotification","params":{{"result":["8Rshv2oMkPu5E4opXTRyuyBeZBqQ4S477VG26wUTFxUM",{"executable":false,"lamports":1,"owner":"9gZbPtbtHrs6hEWgd6MbVY9VPFtS5Z8xKtnYwA2NynHV","data":"4SZWhnbSt3njU4QHVgPrWeekz1BudU4ttmdr9ezmrL4X6XeLeL83xVAo6ZdxwU3oXgHNeF2q6tWZbnVnBXmvNyeLVEGt8ZQ4ZmgjHfVNCEwBtzh2aDrHgQSjBFLYAdmM3uwBhcm1EyHJLeUiFqpsoAUhn6Vphwrpf44dWRAGsAJZbzvVrUW9bfucpR7xudHHg2MxQ2CdqsfS3TfWUJY3vaf2A4AUNzfAmNPHBGi99nU2hYubGSVSPcpVPpdRWQkydgqasBmTosd","rentEpoch":28}],"subscription":0}}
|
||||||
```
|
```
|
||||||
|
|
||||||
### programUnsubscribe
|
### programUnsubscribe
|
||||||
@@ -961,11 +1095,11 @@ Unsubscribe from program-owned account change notifications
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `integer` - id of account Subscription to cancel
|
* `<integer>` - id of account Subscription to cancel
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `bool` - unsubscribe success message
|
* `<bool>` - unsubscribe success message
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -983,8 +1117,8 @@ Subscribe to a transaction signature to receive notification when the transactio
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `string` - Transaction Signature, as base-58 encoded string
|
* `<string>` - Transaction Signature, as base-58 encoded string
|
||||||
* `integer` - optional, number of confirmed blocks to wait before notification.
|
* `<integer>` - optional, number of confirmed blocks to wait before notification.
|
||||||
|
|
||||||
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
|
||||||
|
|
||||||
@@ -1016,11 +1150,11 @@ Unsubscribe from signature confirmation notification
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `integer` - subscription id to cancel
|
* `<integer>` - subscription id to cancel
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
* `bool` - unsubscribe success message
|
* `<bool>` - unsubscribe success message
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
@@ -1031,3 +1165,53 @@ Unsubscribe from signature confirmation notification
|
|||||||
// Result
|
// Result
|
||||||
{"jsonrpc": "2.0","result": true,"id": 1}
|
{"jsonrpc": "2.0","result": true,"id": 1}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### slotSubscribe
|
||||||
|
|
||||||
|
Subscribe to receive notification anytime a slot is processed by the validator
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
* `integer` - subscription id \(needed to unsubscribe\)
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
{"jsonrpc":"2.0", "id":1, "method":"slotSubscribe"}
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc": "2.0","result": 0,"id": 1}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Notification Format:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
{"jsonrpc": "2.0","method": "slotNotification", "params": {"result":{"parent":75,"root":44,"slot":76},"subscription":0}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### slotUnsubscribe
|
||||||
|
|
||||||
|
Unsubscribe from signature confirmation notification
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
* `<integer>` - subscription id to cancel
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
* `<bool>` - unsubscribe success message
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
{"jsonrpc":"2.0", "id":1, "method":"slotUnsubscribe", "params":[0]}
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc": "2.0","result": true,"id": 1}
|
||||||
|
```
|
@@ -1,4 +1,4 @@
|
|||||||
# Getting Started
|
# Building from Source
|
||||||
|
|
||||||
The Solana git repository contains all the scripts you might need to spin up your own local testnet. Depending on what you're looking to achieve, you may want to run a different variation, as the full-fledged, performance-enhanced multinode testnet is considerably more complex to set up than a Rust-only, singlenode testnode. If you are looking to develop high-level features, such as experimenting with smart contracts, save yourself some setup headaches and stick to the Rust-only singlenode demo. If you're doing performance optimization of the transaction pipeline, consider the enhanced singlenode demo. If you're doing consensus work, you'll need at least a Rust-only multinode demo. If you want to reproduce our TPS metrics, run the enhanced multinode demo.
|
The Solana git repository contains all the scripts you might need to spin up your own local testnet. Depending on what you're looking to achieve, you may want to run a different variation, as the full-fledged, performance-enhanced multinode testnet is considerably more complex to set up than a Rust-only, singlenode testnode. If you are looking to develop high-level features, such as experimenting with smart contracts, save yourself some setup headaches and stick to the Rust-only singlenode demo. If you're doing performance optimization of the transaction pipeline, consider the enhanced singlenode demo. If you're doing consensus work, you'll need at least a Rust-only multinode demo. If you want to reproduce our TPS metrics, run the enhanced multinode demo.
|
||||||
|
|
||||||
@@ -52,12 +52,12 @@ $ NDEBUG=1 ./multinode-demo/faucet.sh
|
|||||||
|
|
||||||
### Singlenode Testnet
|
### Singlenode Testnet
|
||||||
|
|
||||||
Before you start a validator, make sure you know the IP address of the machine you want to be the bootstrap leader for the demo, and make sure that udp ports 8000-10000 are open on all the machines you want to test with.
|
Before you start a validator, make sure you know the IP address of the machine you want to be the bootstrap validator for the demo, and make sure that udp ports 8000-10000 are open on all the machines you want to test with.
|
||||||
|
|
||||||
Now start the bootstrap leader in a separate shell:
|
Now start the bootstrap validator in a separate shell:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ NDEBUG=1 ./multinode-demo/bootstrap-leader.sh
|
$ NDEBUG=1 ./multinode-demo/bootstrap-validator.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Wait a few seconds for the server to initialize. It will print "leader ready..." when it's ready to receive transactions. The leader will request some tokens from the faucet if it doesn't have any. The faucet does not need to be running for subsequent leader starts.
|
Wait a few seconds for the server to initialize. It will print "leader ready..." when it's ready to receive transactions. The leader will request some tokens from the faucet if it doesn't have any. The faucet does not need to be running for subsequent leader starts.
|
||||||
@@ -74,7 +74,7 @@ To run a performance-enhanced validator on Linux, [CUDA 10.0](https://developer.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ ./fetch-perf-libs.sh
|
$ ./fetch-perf-libs.sh
|
||||||
$ NDEBUG=1 SOLANA_CUDA=1 ./multinode-demo/bootstrap-leader.sh
|
$ NDEBUG=1 SOLANA_CUDA=1 ./multinode-demo/bootstrap-validator.sh
|
||||||
$ NDEBUG=1 SOLANA_CUDA=1 ./multinode-demo/validator.sh
|
$ NDEBUG=1 SOLANA_CUDA=1 ./multinode-demo/validator.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -121,6 +121,34 @@ thread apply all bt
|
|||||||
|
|
||||||
This will dump all the threads stack traces into gdb.txt
|
This will dump all the threads stack traces into gdb.txt
|
||||||
|
|
||||||
|
### Blockstreamer
|
||||||
|
|
||||||
|
Solana supports a node type called an _blockstreamer_. This validator variation is intended for applications that need to observe the data plane without participating in transaction validation or ledger replication.
|
||||||
|
|
||||||
|
A blockstreamer runs without a vote signer, and can optionally stream ledger entries out to a Unix domain socket as they are processed. The JSON-RPC service still functions as on any other node.
|
||||||
|
|
||||||
|
To run a blockstreamer, include the argument `no-signer` and \(optional\) `blockstream` socket location:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ NDEBUG=1 ./multinode-demo/validator-x.sh --no-signer --blockstream <SOCKET>
|
||||||
|
```
|
||||||
|
|
||||||
|
The stream will output a series of JSON objects:
|
||||||
|
|
||||||
|
* An Entry event JSON object is sent when each ledger entry is processed, with the following fields:
|
||||||
|
* `dt`, the system datetime, as RFC3339-formatted string
|
||||||
|
* `t`, the event type, always "entry"
|
||||||
|
* `s`, the slot height, as unsigned 64-bit integer
|
||||||
|
* `h`, the tick height, as unsigned 64-bit integer
|
||||||
|
* `entry`, the entry, as JSON object
|
||||||
|
* A Block event JSON object is sent when a block is complete, with the following fields:
|
||||||
|
* `dt`, the system datetime, as RFC3339-formatted string
|
||||||
|
* `t`, the event type, always "block"
|
||||||
|
* `s`, the slot height, as unsigned 64-bit integer
|
||||||
|
* `h`, the tick height, as unsigned 64-bit integer
|
||||||
|
* `l`, the slot leader id, as base-58 encoded string
|
||||||
|
* `hash`, the [blockhash](terminology.md#blockhash), as base-58 encoded string
|
||||||
|
|
||||||
## Public Testnet
|
## Public Testnet
|
||||||
|
|
||||||
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`.
|
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`.
|
5
book/src/cli/README.md
Normal file
5
book/src/cli/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Using Solana from the Command-line
|
||||||
|
|
||||||
|
This chapter describes the command-line tools for interacting with Solana. One
|
||||||
|
could use these tools to send payments, stake validators, and check account
|
||||||
|
balances.
|
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ A Solana cluster is a set of validators working together to serve client transac
|
|||||||
|
|
||||||
## Creating a Cluster
|
## Creating a Cluster
|
||||||
|
|
||||||
Before starting any validators, one first needs to create a _genesis config_. The config references two public keys, a _mint_ and a _bootstrap leader_. The validator holding the bootstrap leader's private key is responsible for appending the first entries to the ledger. It initializes its internal state with the mint's account. That account will hold the number of native tokens defined by the genesis config. The second validator then contacts the bootstrap leader to register as a _validator_ or _archiver_. Additional validators then register with any registered member of the cluster.
|
Before starting any validators, one first needs to create a _genesis config_. The config references two public keys, a _mint_ and a _bootstrap validator_. The validator holding the bootstrap validator's private key is responsible for appending the first entries to the ledger. It initializes its internal state with the mint's account. That account will hold the number of native tokens defined by the genesis config. The second validator then contacts the bootstrap validator to register as a _validator_ or _archiver_. Additional validators then register with any registered member of the cluster.
|
||||||
|
|
||||||
A validator receives all entries from the leader and submits votes confirming those entries are valid. After voting, the validator is expected to store those entries until archiver nodes submit proofs that they have stored copies of it. Once the validator observes a sufficient number of copies exist, it deletes its copy.
|
A validator receives all entries from the leader and submits votes confirming those entries are valid. After voting, the validator is expected to store those entries until archiver nodes submit proofs that they have stored copies of it. Once the validator observes a sufficient number of copies exist, it deletes its copy.
|
||||||
|
|
||||||
@@ -37,4 +37,4 @@ Solana rotates leaders at fixed intervals, called _slots_. Each leader may only
|
|||||||
|
|
||||||
Next, transactions are broken into batches so that a node can send transactions to multiple parties without making multiple copies. If, for example, the leader needed to send 60 transactions to 6 nodes, it would break that collection of 60 into batches of 10 transactions and send one to each node. This allows the leader to put 60 transactions on the wire, not 60 transactions for each node. Each node then shares its batch with its peers. Once the node has collected all 6 batches, it reconstructs the original set of 60 transactions.
|
Next, transactions are broken into batches so that a node can send transactions to multiple parties without making multiple copies. If, for example, the leader needed to send 60 transactions to 6 nodes, it would break that collection of 60 into batches of 10 transactions and send one to each node. This allows the leader to put 60 transactions on the wire, not 60 transactions for each node. Each node then shares its batch with its peers. Once the node has collected all 6 batches, it reconstructs the original set of 60 transactions.
|
||||||
|
|
||||||
A batch of transactions can only be split so many times before it is so small that header information becomes the primary consumer of network bandwidth. At the time of this writing, the approach is scaling well up to about 150 validators. To scale up to hundreds of thousands of validators, each node can apply the same technique as the leader node to another set of nodes of equal size. We call the technique _data plane fanout_; learn more in the [data plan fanout](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/data-plane-fanout.md) section.
|
A batch of transactions can only be split so many times before it is so small that header information becomes the primary consumer of network bandwidth. At the time of this writing, the approach is scaling well up to about 150 validators. To scale up to hundreds of thousands of validators, each node can apply the same technique as the leader node to another set of nodes of equal size. We call the technique [_Turbine Block Propogation_](turbine-block-propagation.md).
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Stake Delegation and Rewards
|
# Stake Delegation and Rewards
|
||||||
|
|
||||||
Stakers are rewarded for helping to validate the ledger. They do this by delegating their stake to validator nodes. Those validators do the legwork of replaying the ledger and send votes to a per-node vote account to which stakers can delegate their stakes. The rest of the cluster uses those stake-weighted votes to select a block when forks arise. Both the validator and staker need some economic incentive to play their part. The validator needs to be compensated for its hardware and the staker needs to be compensated for the risk of getting its stake slashed. The economics are covered in [staking rewards](../proposals/staking-rewards.md). This chapter, on the other hand, describes the underlying mechanics of its implementation.
|
Stakers are rewarded for helping to validate the ledger. They do this by delegating their stake to validator nodes. Those validators do the legwork of replaying the ledger and send votes to a per-node vote account to which stakers can delegate their stakes. The rest of the cluster uses those stake-weighted votes to select a block when forks arise. Both the validator and staker need some economic incentive to play their part. The validator needs to be compensated for its hardware and the staker needs to be compensated for the risk of getting its stake slashed. The economics are covered in [staking rewards](../implemented-proposals/staking-rewards.md). This chapter, on the other hand, describes the underlying mechanics of its implementation.
|
||||||
|
|
||||||
## Basic Design
|
## Basic Design
|
||||||
|
|
||||||
@@ -94,42 +94,22 @@ The Stakes and the RewardsPool are accounts that are owned by the same `Stake` p
|
|||||||
|
|
||||||
### StakeInstruction::DelegateStake
|
### StakeInstruction::DelegateStake
|
||||||
|
|
||||||
The Stake account is moved from Ininitialized to StakeState::Stake form. This is how stakers choose their initial delegate validator node and activate their stake account lamports. The transaction must be signed by the stake's `authorized_staker`. If the stake account is already StakeState::Stake \(i.e. already activated\), the stake is re-delegated. Stakes may be re-delegated at any time, and updated stakes are reflected immediately, but only one re-delegation is permitted per epoch.
|
The Stake account is moved from Initialized to StakeState::Stake form, or from a deactivated (i.e. fully cooled-down) StakeState::Stake to activated StakeState::Stake. This is how stakers choose the vote account and validator node to which their stake account lamports are delegated. The transaction must be signed by the stake's `authorized_staker`.
|
||||||
|
|
||||||
* `account[0]` - RW - The StakeState::Stake instance. `StakeState::Stake::credits_observed` is initialized to `VoteState::credits`, `StakeState::Stake::voter_pubkey` is initialized to `account[1]`. If this is the initial delegation of stake, `StakeState::Stake::stake` is initialized to the account's balance in lamports, `StakeState::Stake::activated` is initialized to the current Bank epoch, and `StakeState::Stake::deactivated` is initialized to std::u64::MAX
|
* `account[0]` - RW - The StakeState::Stake instance. `StakeState::Stake::credits_observed` is initialized to `VoteState::credits`, `StakeState::Stake::voter_pubkey` is initialized to `account[1]`. If this is the initial delegation of stake, `StakeState::Stake::stake` is initialized to the account's balance in lamports, `StakeState::Stake::activated` is initialized to the current Bank epoch, and `StakeState::Stake::deactivated` is initialized to std::u64::MAX
|
||||||
* `account[1]` - R - The VoteState instance.
|
* `account[1]` - R - The VoteState instance.
|
||||||
* `account[2]` - R - sysvar::clock account, carries information about current Bank epoch
|
* `account[2]` - R - sysvar::clock account, carries information about current Bank epoch
|
||||||
* `account[3]` - R - stake::Config accoount, carries warmup, cooldown, and slashing configuration
|
* `account[3]` - R - sysvar::stakehistory account, carries information about stake history
|
||||||
|
* `account[4]` - R - stake::Config accoount, carries warmup, cooldown, and slashing configuration
|
||||||
|
|
||||||
### StakeInstruction::Authorize\(Pubkey, StakeAuthorize\)
|
### StakeInstruction::Authorize\(Pubkey, StakeAuthorize\)
|
||||||
|
|
||||||
Updates the account with a new authorized staker or withdrawer, according to the StakeAuthorize parameter \(`Staker` or `Withdrawer`\). The transaction must be by signed by the Stakee account's current `authorized_staker` or `authorized_withdrawer`.
|
Updates the account with a new authorized staker or withdrawer, according to the StakeAuthorize parameter \(`Staker` or `Withdrawer`\). The transaction must be by signed by the Stakee account's current `authorized_staker` or `authorized_withdrawer`. Any stake lock-up must have expired, or the lock-up custodian must also sign the transaction.
|
||||||
|
|
||||||
* `account[0]` - RW - The StakeState
|
* `account[0]` - RW - The StakeState
|
||||||
|
|
||||||
`StakeState::authorized_staker` or `authorized_withdrawer` is set to to `Pubkey`.
|
`StakeState::authorized_staker` or `authorized_withdrawer` is set to to `Pubkey`.
|
||||||
|
|
||||||
### StakeInstruction::RedeemVoteCredits
|
|
||||||
|
|
||||||
The staker or the owner of the Stake account sends a transaction with this instruction to claim rewards.
|
|
||||||
|
|
||||||
The Vote account and the Stake account pair maintain a lifetime counter of total rewards generated and claimed. Rewards are paid according to a point value supplied by the Bank from inflation. A `point` is one credit \* one staked lamport, rewards paid are proportional to the number of lamports staked.
|
|
||||||
|
|
||||||
* `account[0]` - RW - The StakeState::Stake instance that is redeeming rewards.
|
|
||||||
* `account[1]` - R - The VoteState instance, must be the same as `StakeState::voter_pubkey`
|
|
||||||
* `account[2]` - RW - The StakeState::RewardsPool instance that will fulfill the request \(picked at random\).
|
|
||||||
* `account[3]` - R - sysvar::rewards account from the Bank that carries point value.
|
|
||||||
* `account[4]` - R - sysvar::stake\_history account from the Bank that carries stake warmup/cooldown history
|
|
||||||
|
|
||||||
Reward is paid out for the difference between `VoteState::credits` to `StakeState::Stake::credits_observed`, multiplied by `sysvar::rewards::Rewards::validator_point_value`. `StakeState::Stake::credits_observed` is updated to`VoteState::credits`. The commission is deposited into the Vote account token balance, and the reward is deposited to the Stake account token balance and the stake account's `stake` is increased by the same amount \(re-invested\).
|
|
||||||
|
|
||||||
```text
|
|
||||||
let credits_to_claim = vote_state.credits - stake_state.credits_observed;
|
|
||||||
stake_state.credits_observed = vote_state.credits;
|
|
||||||
```
|
|
||||||
|
|
||||||
`credits_to_claim` is used to compute the reward and commission, and `StakeState::Stake::credits_observed` is updated to the latest `VoteState::credits` value.
|
|
||||||
|
|
||||||
### StakeInstruction::Deactivate
|
### StakeInstruction::Deactivate
|
||||||
|
|
||||||
A staker may wish to withdraw from the network. To do so he must first deactivate his stake, and wait for cool down.
|
A staker may wish to withdraw from the network. To do so he must first deactivate his stake, and wait for cool down.
|
||||||
@@ -162,11 +142,11 @@ Lamports build up over time in a Stake account and any excess over activated sta
|
|||||||
|
|
||||||
## Staking Rewards
|
## Staking Rewards
|
||||||
|
|
||||||
The specific mechanics and rules of the validator rewards regime is outlined here. Rewards are earned by delegating stake to a validator that is voting correctly. Voting incorrectly exposes that validator's stakes to [slashing](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/staking-and-rewards.md).
|
The specific mechanics and rules of the validator rewards regime is outlined here. Rewards are earned by delegating stake to a validator that is voting correctly. Voting incorrectly exposes that validator's stakes to [slashing](../proposals/slashing.md).
|
||||||
|
|
||||||
### Basics
|
### Basics
|
||||||
|
|
||||||
The network pays rewards from a portion of network [inflation](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/inflation.md). The number of lamports available to pay rewards for an epoch is fixed and must be evenly divided among all staked nodes according to their relative stake weight and participation. The weighting unit is called a [point](../terminology.md#point).
|
The network pays rewards from a portion of network [inflation](../terminology.md#inflation). The number of lamports available to pay rewards for an epoch is fixed and must be evenly divided among all staked nodes according to their relative stake weight and participation. The weighting unit is called a [point](../terminology.md#point).
|
||||||
|
|
||||||
Rewards for an epoch are not available until the end of that epoch.
|
Rewards for an epoch are not available until the end of that epoch.
|
||||||
|
|
||||||
@@ -228,4 +208,4 @@ Only lamports in excess of effective+activating stake may be withdrawn at any ti
|
|||||||
|
|
||||||
### Lock-up
|
### Lock-up
|
||||||
|
|
||||||
Stake accounts support the notion of lock-up, wherein the stake account balance is unavailable for withdrawal until a specified time. Lock-up is specified as an epoch height, i.e. the minimum epoch height that must be reached by the network before the stake account balance is available for withdrawal, unless the transaction is also signed by a specified custodian. This information is gathered when the stake account is created, and stored in the Lockup field of the stake account's state.
|
Stake accounts support the notion of lock-up, wherein the stake account balance is unavailable for withdrawal until a specified time. Lock-up is specified as an epoch height, i.e. the minimum epoch height that must be reached by the network before the stake account balance is available for withdrawal, unless the transaction is also signed by a specified custodian. This information is gathered when the stake account is created, and stored in the Lockup field of the stake account's state. Changing the authorized staker or withdrawer is also subject to lock-up, as such an operation is effectively a transfer.
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
# Testnet Participation
|
|
||||||
|
|
||||||
Participate in our testnet:
|
|
||||||
|
|
||||||
* [Running a Validator](../running-validator/)
|
|
||||||
* [Running an Archiver](../running-archiver.md)
|
|
||||||
|
|
@@ -1,90 +0,0 @@
|
|||||||
# Blockstore
|
|
||||||
|
|
||||||
After a block reaches finality, all blocks from that one on down to the genesis block form a linear chain with the familiar name blockchain. Until that point, however, the validator must maintain all potentially valid chains, called _forks_. The process by which forks naturally form as a result of leader rotation is described in [fork generation](../cluster/fork-generation.md). The _blockstore_ data structure described here is how a validator copes with those forks until blocks are finalized.
|
|
||||||
|
|
||||||
The blockstore allows a validator to record every shred it observes on the network, in any order, as long as the shred is signed by the expected leader for a given slot.
|
|
||||||
|
|
||||||
Shreds are moved to a fork-able key space the tuple of `leader slot` + `shred index` \(within the slot\). This permits the skip-list structure of the Solana protocol to be stored in its entirety, without a-priori choosing which fork to follow, which Entries to persist or when to persist them.
|
|
||||||
|
|
||||||
Repair requests for recent shreds are served out of RAM or recent files and out of deeper storage for less recent shreds, as implemented by the store backing Blockstore.
|
|
||||||
|
|
||||||
## Functionalities of Blockstore
|
|
||||||
|
|
||||||
1. Persistence: the Blockstore lives in the front of the nodes verification
|
|
||||||
|
|
||||||
pipeline, right behind network receive and signature verification. If the
|
|
||||||
|
|
||||||
shred received is consistent with the leader schedule \(i.e. was signed by the
|
|
||||||
|
|
||||||
leader for the indicated slot\), it is immediately stored.
|
|
||||||
|
|
||||||
2. Repair: repair is the same as window repair above, but able to serve any
|
|
||||||
|
|
||||||
shred that's been received. Blockstore stores shreds with signatures,
|
|
||||||
|
|
||||||
preserving the chain of origination.
|
|
||||||
|
|
||||||
3. Forks: Blockstore supports random access of shreds, so can support a
|
|
||||||
|
|
||||||
validator's need to rollback and replay from a Bank checkpoint.
|
|
||||||
|
|
||||||
4. Restart: with proper pruning/culling, the Blockstore can be replayed by
|
|
||||||
|
|
||||||
ordered enumeration of entries from slot 0. The logic of the replay stage
|
|
||||||
|
|
||||||
\(i.e. dealing with forks\) will have to be used for the most recent entries in
|
|
||||||
|
|
||||||
the Blockstore.
|
|
||||||
|
|
||||||
## Blockstore Design
|
|
||||||
|
|
||||||
1. Entries in the Blockstore are stored as key-value pairs, where the key is the concatenated slot index and shred index for an entry, and the value is the entry data. Note shred indexes are zero-based for each slot \(i.e. they're slot-relative\).
|
|
||||||
2. The Blockstore maintains metadata for each slot, in the `SlotMeta` struct containing:
|
|
||||||
* `slot_index` - The index of this slot
|
|
||||||
* `num_blocks` - The number of blocks in the slot \(used for chaining to a previous slot\)
|
|
||||||
* `consumed` - The highest shred index `n`, such that for all `m < n`, there exists a shred in this slot with shred index equal to `n` \(i.e. the highest consecutive shred index\).
|
|
||||||
* `received` - The highest received shred index for the slot
|
|
||||||
* `next_slots` - A list of future slots this slot could chain to. Used when rebuilding
|
|
||||||
|
|
||||||
the ledger to find possible fork points.
|
|
||||||
|
|
||||||
* `last_index` - The index of the shred that is flagged as the last shred for this slot. This flag on a shred will be set by the leader for a slot when they are transmitting the last shred for a slot.
|
|
||||||
* `is_rooted` - True iff every block from 0...slot forms a full sequence without any holes. We can derive is\_rooted for each slot with the following rules. Let slot\(n\) be the slot with index `n`, and slot\(n\).is\_full\(\) is true if the slot with index `n` has all the ticks expected for that slot. Let is\_rooted\(n\) be the statement that "the slot\(n\).is\_rooted is true". Then:
|
|
||||||
|
|
||||||
is\_rooted\(0\) is\_rooted\(n+1\) iff \(is\_rooted\(n\) and slot\(n\).is\_full\(\)
|
|
||||||
3. Chaining - When a shred for a new slot `x` arrives, we check the number of blocks \(`num_blocks`\) for that new slot \(this information is encoded in the shred\). We then know that this new slot chains to slot `x - num_blocks`.
|
|
||||||
4. Subscriptions - The Blockstore records a set of slots that have been "subscribed" to. This means entries that chain to these slots will be sent on the Blockstore channel for consumption by the ReplayStage. See the `Blockstore APIs` for details.
|
|
||||||
5. Update notifications - The Blockstore notifies listeners when slot\(n\).is\_rooted is flipped from false to true for any `n`.
|
|
||||||
|
|
||||||
## Blockstore APIs
|
|
||||||
|
|
||||||
The Blockstore offers a subscription based API that ReplayStage uses to ask for entries it's interested in. The entries will be sent on a channel exposed by the Blockstore. These subscription API's are as follows: 1. `fn get_slots_since(slot_indexes: &[u64]) -> Vec<SlotMeta>`: Returns new slots connecting to any element of the list `slot_indexes`.
|
|
||||||
|
|
||||||
1. `fn get_slot_entries(slot_index: u64, entry_start_index: usize, max_entries: Option<u64>) -> Vec<Entry>`: Returns the entry vector for the slot starting with `entry_start_index`, capping the result at `max` if `max_entries == Some(max)`, otherwise, no upper limit on the length of the return vector is imposed.
|
|
||||||
|
|
||||||
Note: Cumulatively, this means that the replay stage will now have to know when a slot is finished, and subscribe to the next slot it's interested in to get the next set of entries. Previously, the burden of chaining slots fell on the Blockstore.
|
|
||||||
|
|
||||||
## Interfacing with Bank
|
|
||||||
|
|
||||||
The bank exposes to replay stage:
|
|
||||||
|
|
||||||
1. `prev_hash`: which PoH chain it's working on as indicated by the hash of the last
|
|
||||||
|
|
||||||
entry it processed
|
|
||||||
|
|
||||||
2. `tick_height`: the ticks in the PoH chain currently being verified by this
|
|
||||||
|
|
||||||
bank
|
|
||||||
|
|
||||||
3. `votes`: a stack of records that contain: 1. `prev_hashes`: what anything after this vote must chain to in PoH 2. `tick_height`: the tick height at which this vote was cast 3. `lockout period`: how long a chain must be observed to be in the ledger to
|
|
||||||
|
|
||||||
be able to be chained below this vote
|
|
||||||
|
|
||||||
Replay stage uses Blockstore APIs to find the longest chain of entries it can hang off a previous vote. If that chain of entries does not hang off the latest vote, the replay stage rolls back the bank to that vote and replays the chain from there.
|
|
||||||
|
|
||||||
## Pruning Blockstore
|
|
||||||
|
|
||||||
Once Blockstore entries are old enough, representing all the possible forks becomes less useful, perhaps even problematic for replay upon restart. Once a validator's votes have reached max lockout, however, any Blockstore contents that are not on the PoH chain for that vote for can be pruned, expunged.
|
|
||||||
|
|
||||||
Archiver nodes will be responsible for storing really old ledger contents, and validators need only persist their bank periodically.
|
|
||||||
|
|
@@ -28,7 +28,7 @@ lockout on a bank `b`.
|
|||||||
|
|
||||||
This computation is performed on a votable candidate bank `b` as follows.
|
This computation is performed on a votable candidate bank `b` as follows.
|
||||||
|
|
||||||
```
|
```text
|
||||||
let output: HashMap<b, StakeLockout> = HashMap::new();
|
let output: HashMap<b, StakeLockout> = HashMap::new();
|
||||||
for vote_account in b.vote_accounts {
|
for vote_account in b.vote_accounts {
|
||||||
for v in vote_account.vote_stack {
|
for v in vote_account.vote_stack {
|
||||||
@@ -62,7 +62,7 @@ votes > v as the number of confirmations will be lower).
|
|||||||
|
|
||||||
Now more specifically, we augment the above computation to:
|
Now more specifically, we augment the above computation to:
|
||||||
|
|
||||||
```
|
```text
|
||||||
let output: HashMap<b, StakeLockout> = HashMap::new();
|
let output: HashMap<b, StakeLockout> = HashMap::new();
|
||||||
let fork_commitment_cache = ForkCommitmentCache::default();
|
let fork_commitment_cache = ForkCommitmentCache::default();
|
||||||
for vote_account in b.vote_accounts {
|
for vote_account in b.vote_accounts {
|
||||||
@@ -76,7 +76,7 @@ Now more specifically, we augment the above computation to:
|
|||||||
```
|
```
|
||||||
|
|
||||||
where `f'` is defined as:
|
where `f'` is defined as:
|
||||||
```
|
```text
|
||||||
fn f`(
|
fn f`(
|
||||||
stake_lockout: &mut StakeLockout,
|
stake_lockout: &mut StakeLockout,
|
||||||
some_ancestor: &mut BlockCommitment,
|
some_ancestor: &mut BlockCommitment,
|
||||||
|
@@ -10,7 +10,6 @@ These protocol-based rewards, to be distributed to participating validation and
|
|||||||
|
|
||||||
Transaction fees are market-based participant-to-participant transfers, attached to network interactions as a necessary motivation and compensation for the inclusion and execution of a proposed transaction \(be it a state execution or proof-of-replication verification\). A mechanism for long-term economic stability and forking protection through partial burning of each transaction fee is also discussed below.
|
Transaction fees are market-based participant-to-participant transfers, attached to network interactions as a necessary motivation and compensation for the inclusion and execution of a proposed transaction \(be it a state execution or proof-of-replication verification\). A mechanism for long-term economic stability and forking protection through partial burning of each transaction fee is also discussed below.
|
||||||
|
|
||||||
A high-level schematic of Solana’s crypto-economic design is shown below in **Figure 1**. The specifics of validation-client economics are described in sections: [Validation-client Economics](ed_validation_client_economics/), [State-validation Protocol-based Rewards](ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md), [State-validation Transaction Fees](ed_validation_client_economics/ed_vce_state_validation_transaction_fees.md) and [Replication-validation Transaction Fees](ed_validation_client_economics/ed_vce_replication_validation_transaction_fees.md). Also, the chapter titled [Validation Stake Delegation](ed_validation_client_economics/ed_vce_validation_stake_delegation.md) closes with a discussion of validator delegation opportunties and marketplace. Additionally, in [Storage Rent Economics](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/ed_storage_rent_economics.md), we describe an implementation of storage rent to account for the externality costs of maintaining the active state of the ledger. The [Replication-client Economics](ed_replication_client_economics/) chapter will review the Solana network design for global ledger storage/redundancy and archiver-client economics \([Storage-replication rewards](ed_replication_client_economics/ed_rce_storage_replication_rewards.md)\) along with an archiver-to-validator delegation mechanism designed to aide participant on-boarding into the Solana economy discussed in [Replication-client Reward Auto-delegation](ed_replication_client_economics/ed_rce_replication_client_reward_auto_delegation.md). An outline of features for an MVP economic design is discussed in the [Economic Design MVP](ed_mvp.md) section. Finally, in chapter [Attack Vectors](ed_attack_vectors.md), various attack vectors will be described and potential vulnerabilities explored and parameterized.
|
A high-level schematic of Solana’s crypto-economic design is shown below in **Figure 1**. The specifics of validation-client economics are described in sections: [Validation-client Economics](ed_validation_client_economics/), [State-validation Protocol-based Rewards](ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md), [State-validation Transaction Fees](ed_validation_client_economics/ed_vce_state_validation_transaction_fees.md) and [Replication-validation Transaction Fees](ed_validation_client_economics/ed_vce_replication_validation_transaction_fees.md). Also, the chapter titled [Validation Stake Delegation](ed_validation_client_economics/ed_vce_validation_stake_delegation.md) closes with a discussion of validator delegation opportunties and marketplace. Additionally, in [Storage Rent Economics](ed_storage_rent_economics.md), we describe an implementation of storage rent to account for the externality costs of maintaining the active state of the ledger. The [Replication-client Economics](ed_replication_client_economics/) chapter will review the Solana network design for global ledger storage/redundancy and archiver-client economics \([Storage-replication rewards](ed_replication_client_economics/ed_rce_storage_replication_rewards.md)\) along with an archiver-to-validator delegation mechanism designed to aide participant on-boarding into the Solana economy discussed in [Replication-client Reward Auto-delegation](ed_replication_client_economics/ed_rce_replication_client_reward_auto_delegation.md). An outline of features for an MVP economic design is discussed in the [Economic Design MVP](ed_mvp.md) section. Finally, in chapter [Attack Vectors](ed_attack_vectors.md), various attack vectors will be described and potential vulnerabilities explored and parameterized.
|
||||||
|
|
||||||
**Figure 1**: Schematic overview of Solana economic incentive design.
|
**Figure 1**: Schematic overview of Solana economic incentive design.
|
||||||
|
|
||||||
|
@@ -8,5 +8,4 @@ While replication-clients are incentivized and rewarded through protocol-based r
|
|||||||
|
|
||||||
The validation of PoReps by validation-clients is computationally more expensive than state-validation \(detail in the [Economic Sustainability](../ed_economic_sustainability.md) chapter\), thus the transaction fees are expected to be proportionally higher.
|
The validation of PoReps by validation-clients is computationally more expensive than state-validation \(detail in the [Economic Sustainability](../ed_economic_sustainability.md) chapter\), thus the transaction fees are expected to be proportionally higher.
|
||||||
|
|
||||||
There are various attack vectors available for colluding validation and replication clients, also described in detail below in [Economic Sustainability](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/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\).
|
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\).
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ Validator-client rewards for these services are to be distributed at the end of
|
|||||||
|
|
||||||
The effective protocol-based annual interest rate \(%\) per epoch received by validation-clients is to be a function of:
|
The effective protocol-based annual interest rate \(%\) per epoch received by validation-clients is to be a function of:
|
||||||
|
|
||||||
* the current global inflation rate, derived from the pre-determined dis-inflationary issuance schedule \(see [Validation-client Economics](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/ed_validartion_client_economics.md)\)
|
* the current global inflation rate, derived from the pre-determined dis-inflationary issuance schedule \(see [Validation-client Economics](.)\)
|
||||||
* the fraction of staked SOLs out of the current total circulating supply,
|
* the fraction of staked SOLs out of the current total circulating supply,
|
||||||
* the up-time/participation \[% of available slots that validator had opportunity to vote on\] of a given validator over the previous epoch.
|
* the up-time/participation \[% of available slots that validator had opportunity to vote on\] of a given validator over the previous epoch.
|
||||||
|
|
||||||
|
@@ -13,7 +13,6 @@ Many current blockchain economies \(e.g. Bitcoin, Ethereum\), rely on protocol-b
|
|||||||
|
|
||||||
Transaction fees are set by the network cluster based on recent historical throughput, see [Congestion Driven Fees](../../transaction-fees.md#congestion-driven-fees). This minimum portion of each transaction fee can be dynamically adjusted depending on historical gas usage. In this way, the protocol can use the minimum fee to target a desired hardware utilisation. By monitoring a protocol specified gas usage with respect to a desired, target usage amount, the minimum fee can be raised/lowered which should, in turn, lower/raise the actual gas usage per block until it reaches the target amount. This adjustment process can be thought of as similar to the difficulty adjustment algorithm in the Bitcoin protocol, however in this case it is adjusting the minimum transaction fee to guide the transaction processing hardware usage to a desired level.
|
Transaction fees are set by the network cluster based on recent historical throughput, see [Congestion Driven Fees](../../transaction-fees.md#congestion-driven-fees). This minimum portion of each transaction fee can be dynamically adjusted depending on historical gas usage. In this way, the protocol can use the minimum fee to target a desired hardware utilisation. By monitoring a protocol specified gas usage with respect to a desired, target usage amount, the minimum fee can be raised/lowered which should, in turn, lower/raise the actual gas usage per block until it reaches the target amount. This adjustment process can be thought of as similar to the difficulty adjustment algorithm in the Bitcoin protocol, however in this case it is adjusting the minimum transaction fee to guide the transaction processing hardware usage to a desired level.
|
||||||
|
|
||||||
As mentioned, a fixed-proportion of each transaction fee is to be destroyed. The intent of this design is to retain leader incentive to include as many transactions as possible within the leader-slot time, while providing an inflation limiting mechansim that protects against "tax evasion" attacks \(i.e. side-channel fee payments\)[1](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/ed_referenced.md).
|
As mentioned, a fixed-proportion of each transaction fee is to be destroyed. The intent of this design is to retain leader incentive to include as many transactions as possible within the leader-slot time, while providing an inflation limiting mechansim that protects against "tax evasion" attacks \(i.e. side-channel fee payments\)[1](../ed_references.md).
|
||||||
|
|
||||||
Additionally, the burnt fees can be a consideration in fork selection. In the case of a PoH fork with a malicious, censoring leader, we would expect the total fees destroyed to be less than a comparable honest fork, due to the fees lost from censoring. If the censoring leader is to compensate for these lost protocol fees, they would have to replace the burnt fees on their fork themselves, thus potentially reducing the incentive to censor in the first place.
|
Additionally, the burnt fees can be a consideration in fork selection. In the case of a PoH fork with a malicious, censoring leader, we would expect the total fees destroyed to be less than a comparable honest fork, due to the fees lost from censoring. If the censoring leader is to compensate for these lost protocol fees, they would have to replace the burnt fees on their fork themselves, thus potentially reducing the incentive to censor in the first place.
|
||||||
|
|
||||||
|
@@ -18,9 +18,9 @@ Accounts whose balance is insufficient to satisfy the rent that would be due sim
|
|||||||
|
|
||||||
A percentage of the rent collected is destroyed. The rest is distributed to validator accounts by stake weight, a la transaction fees, at the end of every slot.
|
A percentage of the rent collected is destroyed. The rest is distributed to validator accounts by stake weight, a la transaction fees, at the end of every slot.
|
||||||
|
|
||||||
## Credit only
|
## Read-only accounts
|
||||||
|
|
||||||
Credit only accounts are treated as a special case. They are loaded as if rent were due, but updates to their state may be delayed until the end of the slot, when credits are paid.
|
Read-only accounts are not being charged rent in current implementation.
|
||||||
|
|
||||||
## Design considerations, others considered
|
## Design considerations, others considered
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ For brevity this design assumes that a single voter with a stake is deployed as
|
|||||||
|
|
||||||
## Time
|
## Time
|
||||||
|
|
||||||
The Solana cluster generates a source of time via a Verifiable Delay Function we are calling [Proof of History](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/book/src/synchronization.md).
|
The Solana cluster generates a source of time via a Verifiable Delay Function we are calling [Proof of History](../cluster/synchronization.md).
|
||||||
|
|
||||||
Proof of History is used to create a deterministic round robin schedule for all the active leaders. At any given time only 1 leader, which can be computed from the ledger itself, can propose a fork. For more details, see [fork generation](../cluster/fork-generation.md) and [leader rotation](../cluster/leader-rotation.md).
|
Proof of History is used to create a deterministic round robin schedule for all the active leaders. At any given time only 1 leader, which can be computed from the ledger itself, can propose a fork. For more details, see [fork generation](../cluster/fork-generation.md) and [leader rotation](../cluster/leader-rotation.md).
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ When evaluating multiple forks, each validator should use the following rules:
|
|||||||
3. Pick the fork that has the greatest amount of cluster transaction fees.
|
3. Pick the fork that has the greatest amount of cluster transaction fees.
|
||||||
4. Pick the latest fork in terms of PoH.
|
4. Pick the latest fork in terms of PoH.
|
||||||
|
|
||||||
Cluster transaction fees are fees that are deposited to the mining pool as described in the [Staking Rewards](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/book/src/staking-rewards.md) section.
|
Cluster transaction fees are fees that are deposited to the mining pool as described in the [Staking Rewards](staking-rewards.md) section.
|
||||||
|
|
||||||
## PoH ASIC Resistance
|
## PoH ASIC Resistance
|
||||||
|
|
||||||
@@ -134,4 +134,3 @@ An attacker generates a concurrent fork from an older block to try to rollback t
|
|||||||
* 3 votes have a lockout of 8 slots. Concurrent fork must be at least 8 slots ahead and produced in 3 slots. Therefore requires an ASIC 2.6x faster.
|
* 3 votes have a lockout of 8 slots. Concurrent fork must be at least 8 slots ahead and produced in 3 slots. Therefore requires an ASIC 2.6x faster.
|
||||||
* 10 votes have a lockout of 1024 slots. 1024/10, or 102.4x faster ASIC.
|
* 10 votes have a lockout of 1024 slots. 1024/10, or 102.4x faster ASIC.
|
||||||
* 20 votes have a lockout of 2^20 slots. 2^20/20, or 52,428.8x faster ASIC.
|
* 20 votes have a lockout of 2^20 slots. 2^20/20, or 52,428.8x faster ASIC.
|
||||||
|
|
||||||
|
@@ -64,7 +64,7 @@ presently stored nonce value with
|
|||||||
- Command
|
- Command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana get-nonce nonce-keypair.json
|
solana nonce nonce-keypair.json
|
||||||
```
|
```
|
||||||
|
|
||||||
- Output
|
- Output
|
||||||
@@ -105,7 +105,7 @@ Inspect a nonce account in a more human friendly format with
|
|||||||
- Command
|
- Command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana show-nonce-account nonce-keypair.json
|
solana nonce-account nonce-keypair.json
|
||||||
```
|
```
|
||||||
|
|
||||||
- Output
|
- Output
|
||||||
@@ -117,7 +117,7 @@ nonce: DZar6t2EaCFQTbUP4DHKwZ1wT8gCPW2aRfkVWhydkBvS
|
|||||||
```
|
```
|
||||||
|
|
||||||
{% hint style="info" %}
|
{% hint style="info" %}
|
||||||
[Full usage documentation](../api-reference/cli.md#solana-show-nonce-account)
|
[Full usage documentation](../api-reference/cli.md#solana-nonce-account)
|
||||||
{% endhint %}
|
{% endhint %}
|
||||||
|
|
||||||
### Withdraw Funds from a Nonce Account
|
### Withdraw Funds from a Nonce Account
|
||||||
@@ -236,7 +236,7 @@ Remember, `alice.json` is the [nonce authority](#nonce-authority) in this exampl
|
|||||||
{% endhint %}
|
{% endhint %}
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ solana show-nonce-account nonce.json
|
$ solana nonce-account nonce.json
|
||||||
balance: 1 SOL
|
balance: 1 SOL
|
||||||
minimum balance required: 0.00136416 SOL
|
minimum balance required: 0.00136416 SOL
|
||||||
nonce: F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7
|
nonce: F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7
|
||||||
@@ -256,7 +256,7 @@ $ solana balance -k bob.json
|
|||||||
1 SOL
|
1 SOL
|
||||||
```
|
```
|
||||||
```bash
|
```bash
|
||||||
$ solana show-nonce-account nonce.json
|
$ solana nonce-account nonce.json
|
||||||
balance: 1 SOL
|
balance: 1 SOL
|
||||||
minimum balance required: 0.00136416 SOL
|
minimum balance required: 0.00136416 SOL
|
||||||
nonce: 6bjroqDcZgTv6Vavhqf81oBHTv3aMnX19UTB51YhAZnN
|
nonce: 6bjroqDcZgTv6Vavhqf81oBHTv3aMnX19UTB51YhAZnN
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
Follow this guide to setup Solana's key generation tool called `solana-keygen`
|
Follow this guide to setup Solana's key generation tool called `solana-keygen`
|
||||||
|
|
||||||
{% hint style="warn" %}
|
{% hint style="warn" %}
|
||||||
After installation, ensure your version is `0.21.1` or higher by running `solana-keygen -V`
|
After installation, ensure your version is `0.23.1` or higher by running `solana-keygen -V`
|
||||||
{% endhint %}
|
{% endhint %}
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
@@ -102,7 +102,7 @@ networked machine.
|
|||||||
Next, configure the `solana` CLI tool to connect to a particular cluster:
|
Next, configure the `solana` CLI tool to connect to a particular cluster:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana set --url <CLUSTER URL> # (i.e. http://testnet.solana.com:8899)
|
solana config set --url <CLUSTER URL> # (i.e. http://testnet.solana.com:8899)
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, to check the balance, run the following command:
|
Finally, to check the balance, run the following command:
|
||||||
|
90
book/src/proposals/abi-management.md
Normal file
90
book/src/proposals/abi-management.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Solana ABI management process
|
||||||
|
|
||||||
|
This document proposes the Solana ABI management process. The ABI management
|
||||||
|
process is an engineering practice and a supporting technical framework to avoid
|
||||||
|
introducing unintended incompatible ABI changes.
|
||||||
|
|
||||||
|
# Problem
|
||||||
|
|
||||||
|
The Solana ABI (binary interface to the cluster) is currently only defined
|
||||||
|
implicitly by the implementation and requires a very careful eye to notice
|
||||||
|
breaking changes. This makes it extremely difficult to upgrade the software
|
||||||
|
on an existing cluster without rebooting the ledger.
|
||||||
|
|
||||||
|
# Requirements and objectives
|
||||||
|
|
||||||
|
- Unintended ABI changes can be detected as CI failures mechanically.
|
||||||
|
- Newer implementation must be able to process the oldest data (since genesis)
|
||||||
|
once we go mainnet.
|
||||||
|
- The objective of this proposal is to protect the ABI while sustaining rather
|
||||||
|
rapid development by opting for a mechanical process rather than a very long
|
||||||
|
human-driven auditing process.
|
||||||
|
- Once signed cryptographically, data blob must be identical, so no
|
||||||
|
in-place data format update is possible regardless of inbound and outbound of
|
||||||
|
the online system. Also, considering the sheer volume of transactions we're
|
||||||
|
aiming to handle, retrospective in-place update is undesirable at best.
|
||||||
|
|
||||||
|
# Solution
|
||||||
|
|
||||||
|
Instead of natural human's eye due-diligence, which should be assumed to fail
|
||||||
|
regularly, we need a systematic assurance of not breaking the cluster when
|
||||||
|
changing the source code.
|
||||||
|
|
||||||
|
For that purpose, we introduce a mechanism of marking every ABI-related things
|
||||||
|
in source code (`struct`s, `enum`s) with the new `#[frozen_abi]` attribute. This
|
||||||
|
takes hard-coded digest value derived from types of its fields via
|
||||||
|
`ser::Serialize`. And the attribute automatically generates a unit test to try
|
||||||
|
to detect any unsanctioned changes to the marked ABI-related things.
|
||||||
|
|
||||||
|
However, the detection cannot be complete; no matter how hard we statically
|
||||||
|
analyze the source code, it's still possible to break ABI. For example, this
|
||||||
|
includes not-`derive`d hand-written `ser::Serialize`, underlying library's
|
||||||
|
implementation changes (for example `bincode`), CPU architecture differences.
|
||||||
|
The detection of these possible ABI incompatibilities is out-of-scope for this
|
||||||
|
ABI management.
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
|
||||||
|
ABI item/type: various types to be used for serialization, which collectively
|
||||||
|
comprises the whole ABI for any system components. For example, those types
|
||||||
|
include `struct`s and `enum`s.
|
||||||
|
|
||||||
|
ABI item digest: Some fixed hash derived from type information of ABI item's
|
||||||
|
fields.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```patch
|
||||||
|
+#[frozen_abi(digest="1c6a53e9")]
|
||||||
|
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct Vote {
|
||||||
|
/// A stack of votes starting with the oldest vote
|
||||||
|
pub slots: Vec<Slot>,
|
||||||
|
/// signature of the bank's state at the last slot
|
||||||
|
pub hash: Hash,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Developer's workflow
|
||||||
|
|
||||||
|
To know the digest for new ABI items, developers can add `frozen_abi` with a
|
||||||
|
random digest value and run the unit tests and replace it with the correct
|
||||||
|
digest from the assertion test error message.
|
||||||
|
|
||||||
|
In general, once we add `frozen_abi` and its change is published in the stable
|
||||||
|
release channel, its digest should never change. If such a change is needed, we
|
||||||
|
should opt for defining a new struct like `FooV1`. And special release flow like
|
||||||
|
hard forks should be approached.
|
||||||
|
|
||||||
|
# Implementation remarks
|
||||||
|
|
||||||
|
We use some degree of macro machinery to automatically generate unit tests
|
||||||
|
and calculate a digest from ABI items. This is doable by clever use of
|
||||||
|
`serde::Serialize` ([1]) and `any::typename` ([2]). For a precedent for similar
|
||||||
|
implementation, `ink` from the Parity Technologies [3] could be informational.
|
||||||
|
|
||||||
|
# References
|
||||||
|
|
||||||
|
1. [(De)Serialization with type info · Issue #1095 · serde-rs/serde](https://github.com/serde-rs/serde/issues/1095#issuecomment-345483479)
|
||||||
|
2. [`std::any::type_name` - Rust](https://doc.rust-lang.org/std/any/fn.type_name.html)
|
||||||
|
3. [Parity's ink to write smart contracts](https://github.com/paritytech/ink)
|
@@ -10,7 +10,7 @@ When replay stage starts processing the same transactions, it can assume that Po
|
|||||||
|
|
||||||
## Fee Account
|
## Fee Account
|
||||||
|
|
||||||
The [fee account](https://github.com/solana-labs/solana/tree/b5f7a4bff9953415b1f3d385bd59bc65c1ec11a4/book/src/proposals/terminology.md#fee_account) pays for the transaction to be included in the block. The leader only needs to validate that the fee account has the balance to pay for the fee.
|
The [fee account](../terminology.md#fee_account) pays for the transaction to be included in the block. The leader only needs to validate that the fee account has the balance to pay for the fee.
|
||||||
|
|
||||||
## Balance Cache
|
## Balance Cache
|
||||||
|
|
||||||
@@ -53,4 +53,3 @@ The same fee account can be reused many times in the same block until it is used
|
|||||||
Clients that transmit a large number of transactions per second should use a dedicated fee account that is not used as Credit-Debit in any instruction.
|
Clients that transmit a large number of transactions per second should use a dedicated fee account that is not used as Credit-Debit in any instruction.
|
||||||
|
|
||||||
Once an account fee is used as Credit-Debit, it will fail the balance check until the balance cache is reset.
|
Once an account fee is used as Credit-Debit, it will fail the balance check until the balance cache is reset.
|
||||||
|
|
||||||
|
@@ -1,74 +1,108 @@
|
|||||||
# Simple Payment and State Verification
|
# Simple Payment and State Verification
|
||||||
|
|
||||||
It is often useful to allow low resourced clients to participate in a Solana cluster. Be this participation economic or contract execution, verification that a client's activity has been accepted by the network is typically expensive. This proposal lays out a mechanism for such clients to confirm that their actions have been committed to the ledger state with minimal resource expenditure and third-party trust.
|
It is often useful to allow low resourced clients to participate in a Solana
|
||||||
|
cluster. Be this participation economic or contract execution, verification
|
||||||
|
that a client's activity has been accepted by the network is typically
|
||||||
|
expensive. This proposal lays out a mechanism for such clients to confirm that
|
||||||
|
their actions have been committed to the ledger state with minimal resource
|
||||||
|
expenditure and third-party trust.
|
||||||
|
|
||||||
## A Naive Approach
|
## A Naive Approach
|
||||||
|
|
||||||
Validators store the signatures of recently confirmed transactions for a short period of time to ensure that they are not processed more than once. Validators provide a JSON RPC endpoint, which clients can use to query the cluster if a transaction has been recently processed. Validators also provide a PubSub notification, whereby a client registers to be notified when a given signature is observed by the validator. While these two mechanisms allow a client to verify a payment, they are not a proof and rely on completely trusting a validator.
|
Validators store the signatures of recently confirmed transactions for a short
|
||||||
|
period of time to ensure that they are not processed more than once. Validators
|
||||||
|
provide a JSON RPC endpoint, which clients can use to query the cluster if a
|
||||||
|
transaction has been recently processed. Validators also provide a PubSub
|
||||||
|
notification, whereby a client registers to be notified when a given signature
|
||||||
|
is observed by the validator. While these two mechanisms allow a client to
|
||||||
|
verify a payment, they are not a proof and rely on completely trusting a
|
||||||
|
validator.
|
||||||
|
|
||||||
We will describe a way to minimize this trust using Merkle Proofs to anchor the validator's response in the ledger, allowing the client to confirm on their own that a sufficient number of their preferred validators have confirmed a transaction. Requiring multiple validator attestations further reduces trust in the validator, as it increases both the technical and economic difficulty of compromising several other network participants.
|
We will describe a way to minimize this trust using Merkle Proofs to anchor the
|
||||||
|
validator's response in the ledger, allowing the client to confirm on their own
|
||||||
|
that a sufficient number of their preferred validators have confirmed a
|
||||||
|
transaction. Requiring multiple validator attestations further reduces trust in
|
||||||
|
the validator, as it increases both the technical and economic difficulty of
|
||||||
|
compromising several other network participants.
|
||||||
|
|
||||||
## Light Clients
|
## Light Clients
|
||||||
|
|
||||||
A 'light client' is a cluster participant that does not itself run a validator. This light client would provide a level of security greater than trusting a remote validator, without requiring the light client to spend a lot of resources verifying the ledger.
|
A 'light client' is a cluster participant that does not itself run a validator.
|
||||||
|
This light client would provide a level of security greater than trusting a
|
||||||
|
remote validator, without requiring the light client to spend a lot of resources
|
||||||
|
verifying the ledger.
|
||||||
|
|
||||||
Rather than providing transaction signatures directly to a light client, the validator instead generates a Merkle Proof from the transaction of interest to the root of a Merkle Tree of all transactions in the including block. This Merkle Root is stored in a ledger entry which is voted on by validators, providing it consensus legitimacy. The additional level of security for a light client depends on an initial canonical set of validators the light client considers to be the stakeholders of the cluster. As that set is changed, the client can update its internal set of known validators with [receipts](simple-payment-and-state-verification.md#receipts). This may become challenging with a large number of delegated stakes.
|
Rather than providing transaction signatures directly to a light client, the
|
||||||
|
validator instead generates a Merkle Proof from the transaction of interest to
|
||||||
|
the root of a Merkle Tree of all transactions in the including block. This
|
||||||
|
Merkle Root is stored in a ledger entry which is voted on by validators,
|
||||||
|
providing it consensus legitimacy. The additional level of security for a light
|
||||||
|
client depends on an initial canonical set of validators the light client
|
||||||
|
considers to be the stakeholders of the cluster. As that set is changed, the
|
||||||
|
client can update its internal set of known validators with
|
||||||
|
[receipts](simple-payment-and-state-verification.md#receipts). This may become
|
||||||
|
challenging with a large number of delegated stakes.
|
||||||
|
|
||||||
Validators themselves may want to use light client APIs for performance reasons. For example, during the initial launch of a validator, the validator may use a cluster provided checkpoint of the state and verify it with a receipt.
|
Validators themselves may want to use light client APIs for performance reasons.
|
||||||
|
For example, during the initial launch of a validator, the validator may use a
|
||||||
|
cluster provided checkpoint of the state and verify it with a receipt.
|
||||||
|
|
||||||
## Receipts
|
## Receipts
|
||||||
|
|
||||||
A receipt is a minimal proof that; a transaction has been included in a block, that the block has been voted on by the client's preferred set of validators and that the votes have reached the desired confirmation depth.
|
A receipt is a minimal proof that; a transaction has been included in a block,
|
||||||
|
that the block has been voted on by the client's preferred set of validators
|
||||||
|
and that the votes have reached the desired confirmation depth.
|
||||||
|
|
||||||
The receipts for both state and payments start with a Merkle Path from the value into a Bank-Merkle that has been voted on and included in the ledger. A chain of PoH Entries containing subsequent validator votes, deriving from the Bank-Merkle, is the confirmation proof.
|
### Transaction Inclusion Proof
|
||||||
|
|
||||||
Clients can examine this ledger data and compute the finality using Solana's fork selection rules.
|
A transaction inclusion proof is a data structure that contains a Merkle Path
|
||||||
|
from a transaction, through an Entry-Merkle to a Block-Merkle, which is included
|
||||||
|
in a Bank-Hash with the required set of validator votes. A chain of PoH Entries
|
||||||
|
containing subsequent validator votes, deriving from the Bank-Hash, is the proof
|
||||||
|
of confirmation. Clients can examine this ledger data and compute finality using
|
||||||
|
Solana's fork selection rules.
|
||||||
|
|
||||||
### Payment Merkle Path
|
An Entry-Merkle is a Merkle Root including all transactions in a given entry,
|
||||||
|
sorted by signature.
|
||||||
|
|
||||||
A payment receipt is a data structure that contains a Merkle Path from a transaction to the required set of validator votes.
|
A Block-Merkle is the Merkle Root of all the Entry-Merkles sequenced in the block.
|
||||||
|
|
||||||
An Entry-Merkle is a Merkle Root including all transactions in the entry, sorted by signature.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
A Block-Merkle is a Merkle root of all the Entry-Merkles sequenced in the block. Transaction status is necessary for the receipt because the state receipt is constructed for the block. Two transactions over the same state can appear in the block, and therefore, there is no way to infer from just the state whether a transaction that is committed to the ledger has succeeded or failed in modifying the intended state. It may not be necessary to encode the full status code, but a single status bit to indicate the transaction's success.
|
A Bank-Hash is the hash of the concatenation of the Block-Merkle and Accounts-Hash
|
||||||
|
|
||||||
### State Merkle Path
|
<img alt="Bank Hash Diagram" src="img/spv-bank-hash.svg" class="center"/>
|
||||||
|
|
||||||
A state receipt provides a confirmation that a specific state is committed at the end of the block. Inter-block state transitions do not generate a receipt.
|
An Accounts-Hash is the hash of the concatentation of the state hashes of each
|
||||||
|
account modified during the current slot.
|
||||||
|
|
||||||
For example:
|
Transaction status is necessary for the receipt because the state receipt is
|
||||||
|
constructed for the block. Two transactions over the same state can appear in
|
||||||
|
the block, and therefore, there is no way to infer from just the state whether
|
||||||
|
a transaction that is committed to the ledger has succeeded or failed in
|
||||||
|
modifying the intended state. It may not be necessary to encode the full status
|
||||||
|
code, but a single status bit to indicate the transaction's success.
|
||||||
|
|
||||||
* A sends 5 Lamports to B
|
### Account State Verification
|
||||||
* B spends 5 Lamports
|
|
||||||
* C sends 5 Lamports to A
|
|
||||||
|
|
||||||
At the end of the block, A and B are in the exact same starting state, and any state receipt would point to the same value for A or B.
|
An account's state (balance or other data) can be verified by submitting a
|
||||||
|
transaction with a ___TBD___ Instruction to the cluster. The client can then
|
||||||
The Bank-Merkle is computed from the Merkle Tree of the new state changes, along with the Previous Bank-Merkle, and the Block-Merkle.
|
use a [Transaction Inclusion Proof](#transaction-inclusion-proof) to verify
|
||||||
|
whether the cluster agrees that the acount has reached the expected state.
|
||||||

|
|
||||||
|
|
||||||
A state receipt contains only the state changes occurring in the block. A direct Merkle Path to the current Bank-Merkle guarantees the state value at that bank hash, but it cannot be used to generate a “current” receipt to the latest state if the state modification occurred in some previous block. There is no guarantee that the path provided by the validator is the latest one available out of all the previous Bank-Merkles.
|
|
||||||
|
|
||||||
Clients that want to query the chain for a receipt of the "latest" state would need to create a transaction that would update the Merkle Path for that account, such as a credit of 0 Lamports.
|
|
||||||
|
|
||||||
### Validator Votes
|
### Validator Votes
|
||||||
|
|
||||||
Leaders should coalesce the validator votes by stake weight into a single entry. This will reduce the number of entries necessary to create a receipt.
|
Leaders should coalesce the validator votes by stake weight into a single entry.
|
||||||
|
This will reduce the number of entries necessary to create a receipt.
|
||||||
|
|
||||||
### Chain of Entries
|
### Chain of Entries
|
||||||
|
|
||||||
A receipt has a PoH link from the payment or state Merkle Path root to a list of consecutive validation votes.
|
A receipt has a PoH link from the payment or state Merkle Path root to a list
|
||||||
|
of consecutive validation votes.
|
||||||
|
|
||||||
It contains the following:
|
It contains the following:
|
||||||
|
|
||||||
* State -> Bank-Merkle
|
* Transaction -> Entry-Merkle -> Block-Merkle -> Bank-Hash
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
* Transaction -> Entry-Merkle -> Block-Merkle -> Bank-Merkle
|
|
||||||
|
|
||||||
And a vector of PoH entries:
|
And a vector of PoH entries:
|
||||||
|
|
||||||
@@ -89,21 +123,33 @@ LightEntry {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The light entries are reconstructed from Entries and simply show the entry Merkle Root that was mixed in to the PoH hash, instead of the full transaction set.
|
The light entries are reconstructed from Entries and simply show the entry
|
||||||
|
Merkle Root that was mixed in to the PoH hash, instead of the full transaction
|
||||||
|
set.
|
||||||
|
|
||||||
Clients do not need the starting vote state. The [fork selection](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/book/src/fork-selection.md) algorithm is defined such that only votes that appear after the transaction provide finality for the transaction, and finality is independent of the starting state.
|
Clients do not need the starting vote state. The
|
||||||
|
[fork selection](../implemented-proposals/tower-bft.md) algorithm is defined
|
||||||
|
such that only votes that appear after the transaction provide finality for the
|
||||||
|
transaction, and finality is independent of the starting state.
|
||||||
|
|
||||||
### Verification
|
### Verification
|
||||||
|
|
||||||
A light client that is aware of the supermajority set validators can verify a receipt by following the Merkle Path to the PoH chain. The Bank-Merkle is the Merkle Root and will appear in votes included in an Entry. The light client can simulate [fork selection](https://github.com/solana-labs/solana/tree/aacead62c0eb052068172eba6b53fc85874d6d54/book/src/book/src/fork-selection.md) for the consecutive votes and verify that the receipt is confirmed at the desired lockout threshold.
|
A light client that is aware of the supermajority set validators can verify a
|
||||||
|
receipt by following the Merkle Path to the PoH chain. The Block-Merkle is the
|
||||||
|
Merkle Root and will appear in votes included in an Entry. The light client can
|
||||||
|
simulate [fork selection](../implemented-proposals/tower-bft.md) for the
|
||||||
|
consecutive votes and verify that the receipt is confirmed at the desired
|
||||||
|
lockout threshold.
|
||||||
|
|
||||||
### Synthetic State
|
### Synthetic State
|
||||||
|
|
||||||
Synthetic state should be computed into the Bank-Merkle along with the bank generated state.
|
Synthetic state should be computed into the Bank-Hash along with the bank
|
||||||
|
generated state.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
* Epoch validator accounts and their stakes and weights.
|
* Epoch validator accounts and their stakes and weights.
|
||||||
* Computed fee rates
|
* Computed fee rates
|
||||||
|
|
||||||
These values should have an entry in the Bank-Merkle. They should live under known accounts, and therefore have an exact address in the Merkle Path.
|
These values should have an entry in the Bank-Hash. They should live under known
|
||||||
|
accounts, and therefore have an index into the hash concatenation.
|
||||||
|
@@ -2,24 +2,44 @@
|
|||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
When we first started Solana, the goal was to de-risk our TPS claims. We knew that between optimistic concurrency control and sufficiently long leader slots, that PoS consensus was not the biggest risk to TPS. It was GPU-based signature verification, software pipelining and concurrent banking. Thus, the TPU was born. After topping 100k TPS, we split the team into one group working toward 710k TPS and another to flesh out the validator pipeline. Hence, the TVU was born. The current architecture is a consequence of incremental development with that ordering and project priorities. It is not a reflection of what we ever believed was the most technically elegant cross-section of those technologies. In the context of leader rotation, the strong distinction between leading and validating is blurred.
|
When we first started Solana, the goal was to de-risk our TPS claims. We knew
|
||||||
|
that between optimistic concurrency control and sufficiently long leader slots,
|
||||||
|
that PoS consensus was not the biggest risk to TPS. It was GPU-based signature
|
||||||
|
verification, software pipelining and concurrent banking. Thus, the TPU was
|
||||||
|
born. After topping 100k TPS, we split the team into one group working toward
|
||||||
|
710k TPS and another to flesh out the validator pipeline. Hence, the TVU was
|
||||||
|
born. The current architecture is a consequence of incremental development with
|
||||||
|
that ordering and project priorities. It is not a reflection of what we ever
|
||||||
|
believed was the most technically elegant cross-section of those technologies.
|
||||||
|
In the context of leader rotation, the strong distinction between leading and
|
||||||
|
validating is blurred.
|
||||||
|
|
||||||
## Difference between validating and leading
|
## Difference between validating and leading
|
||||||
|
|
||||||
The fundamental difference between the pipelines is when the PoH is present. In a leader, we process transactions, removing bad ones, and then tag the result with a PoH hash. In the validator, we verify that hash, peel it off, and process the transactions in exactly the same way. The only difference is that if a validator sees a bad transaction, it can't simply remove it like the leader does, because that would cause the PoH hash to change. Instead, it rejects the whole block. The other difference between the pipelines is what happens _after_ banking. The leader broadcasts entries to downstream validators whereas the validator will have already done that in RetransmitStage, which is a confirmation time optimization. The validation pipeline, on the other hand, has one last step. Any time it finishes processing a block, it needs to weigh any forks it's observing, possibly cast a vote, and if so, reset its PoH hash to the block hash it just voted on.
|
The fundamental difference between the pipelines is when the PoH is present. In
|
||||||
|
a leader, we process transactions, removing bad ones, and then tag the result
|
||||||
|
with a PoH hash. In the validator, we verify that hash, peel it off, and
|
||||||
|
process the transactions in exactly the same way. The only difference is that
|
||||||
|
if a validator sees a bad transaction, it can't simply remove it like the
|
||||||
|
leader does, because that would cause the PoH hash to change. Instead, it
|
||||||
|
rejects the whole block. The other difference between the pipelines is what
|
||||||
|
happens _after_ banking. The leader broadcasts entries to downstream validators
|
||||||
|
whereas the validator will have already done that in RetransmitStage, which is
|
||||||
|
a confirmation time optimization. The validation pipeline, on the other hand,
|
||||||
|
has one last step. Any time it finishes processing a block, it needs to weigh
|
||||||
|
any forks it's observing, possibly cast a vote, and if so, reset its PoH hash
|
||||||
|
to the block hash it just voted on.
|
||||||
|
|
||||||
## Proposed Design
|
## Proposed Design
|
||||||
|
|
||||||
We unwrap the many abstraction layers and build a single pipeline that can toggle leader mode on whenever the validator's ID shows up in the leader schedule.
|
We unwrap the many abstraction layers and build a single pipeline that can
|
||||||
|
toggle leader mode on whenever the validator's ID shows up in the leader
|
||||||
|
schedule.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Notable changes
|
## Notable changes
|
||||||
|
|
||||||
* No threads are shut down to switch out of leader mode. Instead, FetchStage
|
|
||||||
|
|
||||||
should forward transactions to the next leader.
|
|
||||||
|
|
||||||
* Hoist FetchStage and BroadcastStage out of TPU
|
* Hoist FetchStage and BroadcastStage out of TPU
|
||||||
* BankForks renamed to Banktree
|
* BankForks renamed to Banktree
|
||||||
* TPU moves to new socket-free crate called solana-tpu.
|
* TPU moves to new socket-free crate called solana-tpu.
|
||||||
@@ -27,8 +47,6 @@ We unwrap the many abstraction layers and build a single pipeline that can toggl
|
|||||||
* TVU goes away
|
* TVU goes away
|
||||||
* New RepairStage absorbs Shred Fetch Stage and repair requests
|
* New RepairStage absorbs Shred Fetch Stage and repair requests
|
||||||
* JSON RPC Service is optional - used for debugging. It should instead be part
|
* JSON RPC Service is optional - used for debugging. It should instead be part
|
||||||
|
|
||||||
of a separate `solana-blockstreamer` executable.
|
of a separate `solana-blockstreamer` executable.
|
||||||
|
|
||||||
* New MulticastStage absorbs retransmit part of RetransmitStage
|
* New MulticastStage absorbs retransmit part of RetransmitStage
|
||||||
* MulticastStage downstream of Blockstore
|
* MulticastStage downstream of Blockstore
|
||||||
|
@@ -149,8 +149,8 @@ From another console, confirm the IP address and **identity pubkey** of your arc
|
|||||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
solana-gossip spy --entrypoint testnet.solana.com:8001
|
||||||
```
|
```
|
||||||
|
|
||||||
Provide the **storage account pubkey** to the `solana show-storage-account` command to view the recent mining activity from your archiver:
|
Provide the **storage account pubkey** to the `solana storage-account` command to view the recent mining activity from your archiver:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana --keypair storage-keypair.json show-storage-account $STORAGE_IDENTITY
|
solana --keypair storage-keypair.json storage-account $STORAGE_IDENTITY
|
||||||
```
|
```
|
||||||
|
@@ -21,11 +21,11 @@ solana balance --lamports
|
|||||||
|
|
||||||
## Check Vote Activity
|
## Check Vote Activity
|
||||||
|
|
||||||
The `solana show-vote-account` command displays the recent voting activity from
|
The `solana vote-account` command displays the recent voting activity from
|
||||||
your validator:
|
your validator:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana show-vote-account ~/validator-vote-keypair.json
|
solana vote-account ~/validator-vote-keypair.json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Get Cluster Info
|
## Get Cluster Info
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
# Installing the Validator Software
|
# Installing the Validator Software
|
||||||
|
|
||||||
Install the Solana release
|
Install the Solana release
|
||||||
[v0.21.0](https://github.com/solana-labs/solana/releases/tag/v0.21.0) on your
|
[v0.23.1](https://github.com/solana-labs/solana/releases/tag/v0.23.1) on your
|
||||||
machine by running:
|
machine by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.21.0/install/solana-install-init.sh | sh -s - 0.21.0
|
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.23.1/install/solana-install-init.sh | sh -s - 0.23.1
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are connecting to a different testnet, you can replace `0.21.0` with the
|
If you are connecting to a different testnet, you can replace `0.23.1` with the
|
||||||
release tag matching the software version of your desired testnet, or replace it
|
release tag matching the software version of your desired testnet, or replace it
|
||||||
with the named channel `stable`, `beta`, or `edge`.
|
with the named channel `stable`, `beta`, or `edge`.
|
||||||
|
|
||||||
@@ -16,11 +16,11 @@ The following output indicates a successful update:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
looking for latest release
|
looking for latest release
|
||||||
downloading v0.21.0 installer
|
downloading v0.23.1 installer
|
||||||
Configuration: /home/solana/.config/solana/install/config.yml
|
Configuration: /home/solana/.config/solana/install/config.yml
|
||||||
Active release directory: /home/solana/.local/share/solana/install/active_release
|
Active release directory: /home/solana/.local/share/solana/install/active_release
|
||||||
* Release version: 0.21.0
|
* Release version: 0.23.1
|
||||||
* Release URL: https://github.com/solana-labs/solana/releases/download/v0.21.0/solana-release-x86_64-unknown-linux-gnu.tar.bz2
|
* Release URL: https://github.com/solana-labs/solana/releases/download/v0.23.1/solana-release-x86_64-unknown-linux-gnu.tar.bz2
|
||||||
Update successful
|
Update successful
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -54,11 +54,7 @@ solana delegate-stake ~/validator-stake-keypair.json ~/some-other-validator-vote
|
|||||||
```
|
```
|
||||||
|
|
||||||
Assuming the node is voting, now you're up and running and generating validator
|
Assuming the node is voting, now you're up and running and generating validator
|
||||||
rewards. You'll want to periodically redeem/claim your rewards:
|
rewards. Rewards are paid automatically on epoch boundaries.
|
||||||
|
|
||||||
```bash
|
|
||||||
solana redeem-vote-credits ~/validator-stake-keypair.json ~/validator-vote-keypair.json
|
|
||||||
```
|
|
||||||
|
|
||||||
The rewards lamports earned are split between your stake account and the vote
|
The rewards lamports earned are split between your stake account and the vote
|
||||||
account according to the commission rate set in the vote account. Rewards can
|
account according to the commission rate set in the vote account. Rewards can
|
||||||
@@ -85,11 +81,10 @@ so it can take an hour or more for stake to come fully online.
|
|||||||
|
|
||||||
To monitor your validator during its warmup period:
|
To monitor your validator during its warmup period:
|
||||||
|
|
||||||
* View your vote account:`solana show-vote-account ~/validator-vote-keypair.json` This displays the current state of all the votes the validator has submitted to the network.
|
* View your vote account:`solana vote-account ~/validator-vote-keypair.json` This displays the current state of all the votes the validator has submitted to the network.
|
||||||
* View your stake account, the delegation preference and details of your stake:`solana show-stake-account ~/validator-stake-keypair.json`
|
* View your stake account, the delegation preference and details of your stake:`solana stake-account ~/validator-stake-keypair.json`
|
||||||
* `solana uptime ~/validator-vote-keypair.json` will display the voting history \(aka, uptime\) of your validator over recent Epochs
|
* `solana validators` displays the current active stake of all validators, including yours
|
||||||
* `solana show-validators` displays the current active stake of all validators, including yours
|
* `solana stake-history ` shows the history of stake warming up and cooling down over recent epochs
|
||||||
* `solana show-stake-history ` shows the history of stake warming up and cooling down over recent epochs
|
|
||||||
* Look for log messages on your validator indicating your next leader slot: `[2019-09-27T20:16:00.319721164Z INFO solana_core::replay_stage] <VALIDATOR_IDENTITY_PUBKEY> voted and reset PoH at tick height ####. My next leader slot is ####`
|
* Look for log messages on your validator indicating your next leader slot: `[2019-09-27T20:16:00.319721164Z INFO solana_core::replay_stage] <VALIDATOR_IDENTITY_PUBKEY> voted and reset PoH at tick height ####. My next leader slot is ####`
|
||||||
* Once your stake is warmed up, you will see a stake balance listed for your validator on the [Solana Network Explorer](http://explorer.solana.com/validators)
|
* Once your stake is warmed up, you will see a stake balance listed for your validator on the [Solana Network Explorer](http://explorer.solana.com/validators)
|
||||||
|
|
||||||
@@ -132,6 +127,3 @@ depending on active stake and the size of your stake.
|
|||||||
|
|
||||||
Note that a stake account may only be used once, so after deactivation, use the
|
Note that a stake account may only be used once, so after deactivation, use the
|
||||||
cli's `withdraw-stake` command to recover the previously staked lamports.
|
cli's `withdraw-stake` command to recover the previously staked lamports.
|
||||||
|
|
||||||
Be sure and redeem your credits before withdrawing all your lamports. Once the
|
|
||||||
account is fully withdrawn, the account is destroyed.
|
|
||||||
|
@@ -6,7 +6,7 @@ The solana cli includes `get` and `set` configuration commands to automatically
|
|||||||
set the `--url` argument for cli commands. For example:
|
set the `--url` argument for cli commands. For example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana set --url http://testnet.solana.com:8899
|
solana config set --url http://testnet.solana.com:8899
|
||||||
```
|
```
|
||||||
|
|
||||||
\(You can always override the set configuration by explicitly passing the
|
\(You can always override the set configuration by explicitly passing the
|
||||||
@@ -18,7 +18,7 @@ Before attaching a validator node, sanity check that the cluster is accessible
|
|||||||
to your machine by fetching the transaction count:
|
to your machine by fetching the transaction count:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana get-transaction-count
|
solana transaction-count
|
||||||
```
|
```
|
||||||
|
|
||||||
Inspect the network explorer at
|
Inspect the network explorer at
|
||||||
@@ -100,7 +100,7 @@ Now that you have a keypair, set the solana configuration to use your validator
|
|||||||
keypair for all following commands:
|
keypair for all following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana set --keypair ~/validator-keypair.json
|
solana config set --keypair ~/validator-keypair.json
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see the following output:
|
You should see the following output:
|
||||||
|
@@ -26,7 +26,7 @@ A preimage resistant [hash](terminology.md#hash) of the [ledger](terminology.md#
|
|||||||
|
|
||||||
The number of [blocks](terminology.md#block) beneath the current block. The first block after the [genesis block](terminology.md#genesis-block) has height one.
|
The number of [blocks](terminology.md#block) beneath the current block. The first block after the [genesis block](terminology.md#genesis-block) has height one.
|
||||||
|
|
||||||
## bootstrap leader
|
## bootstrap validator
|
||||||
|
|
||||||
The first [validator](terminology.md#validator) to produce a [block](terminology.md#block).
|
The first [validator](terminology.md#validator) to produce a [block](terminology.md#block).
|
||||||
|
|
||||||
@@ -112,6 +112,10 @@ The configuration file that prepares the [ledger](terminology.md#ledger) for the
|
|||||||
|
|
||||||
A digital fingerprint of a sequence of bytes.
|
A digital fingerprint of a sequence of bytes.
|
||||||
|
|
||||||
|
## inflation
|
||||||
|
|
||||||
|
An increase in token supply over time used to fund rewards for validation and replication and to fund continued development of Solana.
|
||||||
|
|
||||||
## instruction
|
## instruction
|
||||||
|
|
||||||
The smallest unit of a [program](terminology.md#program) that a [client](terminology.md#client) can include in a [transaction](terminology.md#instruction).
|
The smallest unit of a [program](terminology.md#program) that a [client](terminology.md#client) can include in a [transaction](terminology.md#instruction).
|
||||||
|
@@ -1,17 +1,79 @@
|
|||||||
# Anatomy of a Transaction
|
# Anatomy of a Transaction
|
||||||
|
|
||||||
Transactions encode lists of instructions that are executed sequentially, and only committed if all the instructions complete successfully. All account updates are reverted upon the failure of a transaction. Each transaction details the accounts used, including which must sign and which are read only, a recent blockhash, the instructions, and any signatures.
|
This chapter documents the binary format of a transaction.
|
||||||
|
|
||||||
## Accounts and Signatures
|
## Transaction Format
|
||||||
|
|
||||||
Each transaction explicitly lists all account public keys referenced by the transaction's instructions. A subset of those public keys are each accompanied by a transaction signature. Those signatures signal on-chain programs that the account holder has authorized the transaction. Typically, the program uses the authorization to permit debiting the account or modifying its data.
|
A transaction contains a [compact-array](#compact-array-format) of signatures,
|
||||||
|
followed by a [message](#message-format). Each item in the signatures array is
|
||||||
|
a [digital signature](#signature-format) of the given message. The Solana
|
||||||
|
runtime verifies that the number of signatures matches the number in the first
|
||||||
|
8 bits of the [message header](#message-header-format). It also verifies that
|
||||||
|
each signature was signed by the private key corresponding to the public key at
|
||||||
|
the same index in the message's account addresses array.
|
||||||
|
|
||||||
The transaction also marks some accounts as _read-only accounts_. The runtime permits read-only accounts to be read concurrently. If a program attempts to modify a read-only account, the transaction is rejected by the runtime.
|
### Signature Format
|
||||||
|
|
||||||
## Recent Blockhash
|
Each digital signature is in the ed25519 binary format and consumes 64 bytes.
|
||||||
|
|
||||||
A Transaction includes a recent blockhash to prevent duplication and to give transactions lifetimes. Any transaction that is completely identical to a previous one is rejected, so adding a newer blockhash allows multiple transactions to repeat the exact same action. Transactions also have lifetimes that are defined by the blockhash, as any transaction whose blockhash is too old will be rejected.
|
|
||||||
|
|
||||||
## Instructions
|
## Message Format
|
||||||
|
|
||||||
Each instruction specifies a single program account \(which must be marked executable\), a subset of the transaction's accounts that should be passed to the program, and a data byte array instruction that is passed to the program. The program interprets the data array and operates on the accounts specified by the instructions. The program can return successfully, or with an error code. An error return causes the entire transaction to fail immediately.
|
A message contains a [header](#message-header-format), followed by a
|
||||||
|
compact-array of [account addresses](#account-addresses-format), followed by a
|
||||||
|
recent [blockhash](#blockhash-format), followed by a compact-array of
|
||||||
|
[instructions](#instruction-format).
|
||||||
|
|
||||||
|
### Message Header Format
|
||||||
|
|
||||||
|
The message header contains three unsigned 8-bit values. The first value is the
|
||||||
|
number of required signatures in the containing transaction. The second value
|
||||||
|
is the number of those corresponding account addresses that are read-only. The
|
||||||
|
third value in the message header is the number of read-only account addresses
|
||||||
|
not requiring signatures.
|
||||||
|
|
||||||
|
### Account Addresses Format
|
||||||
|
|
||||||
|
The addresses that require signatures appear at the beginning of the account
|
||||||
|
address array, with addresses requesting write access first and read-only
|
||||||
|
accounts following. The addresses that do not require signatures follow the
|
||||||
|
addresses that do, again with read-write accounts first and read-only accounts
|
||||||
|
following.
|
||||||
|
|
||||||
|
|
||||||
|
### Blockhash Format
|
||||||
|
|
||||||
|
A blockhash contains a 32-byte SHA-256 hash. It is used to indicate when a
|
||||||
|
client last observed the ledger. Validators will reject transactions when the
|
||||||
|
blockhash is too old.
|
||||||
|
|
||||||
|
|
||||||
|
## Instruction Format
|
||||||
|
|
||||||
|
An instruction contains a program ID index, followed by a compact-array of
|
||||||
|
account address indexes, followed by a compact-array of opaque 8-bit data. The
|
||||||
|
program ID index is used to identify an on-chain program that can interpret the
|
||||||
|
opaque data. The program ID index is an unsigned 8-bit index to an account
|
||||||
|
address in the message's array of account addresses. The account address
|
||||||
|
indexes are each an unsigned 8-bit index into that same array.
|
||||||
|
|
||||||
|
|
||||||
|
## Compact-Array Format
|
||||||
|
|
||||||
|
A compact-array is serialized as the array length, followed by each array item.
|
||||||
|
The array length is a special multi-byte encoding called compact-u16.
|
||||||
|
|
||||||
|
### Compact-u16 Format
|
||||||
|
|
||||||
|
A compact-u16 is a multi-byte encoding of 16 bits. The first byte contains the
|
||||||
|
lower 7 bits of the value in its lower 7 bits. If the value is above 0x7f, the
|
||||||
|
high bit is set and the next 7 bits of the value are placed into the lower 7
|
||||||
|
bits of a second byte. If the value is above 0x3fff, the high bit is set and
|
||||||
|
the remaining 2 bits of the value are placed into the lower 2 bits of a third
|
||||||
|
byte.
|
||||||
|
|
||||||
|
## Account Address Format
|
||||||
|
|
||||||
|
An account address is 32-bytes of arbitrary data. When the address requires a
|
||||||
|
digital signature, the runtime interprets it as the public key of an ed25519
|
||||||
|
keypair.
|
||||||
|
24
chacha-cuda/Cargo.toml
Normal file
24
chacha-cuda/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "solana-chacha-cuda"
|
||||||
|
version = "0.23.5"
|
||||||
|
description = "Solana Chacha Cuda APIs"
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
homepage = "https://solana.com/"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4.8"
|
||||||
|
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.5" }
|
||||||
|
solana-chacha = { path = "../chacha", version = "0.23.5" }
|
||||||
|
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||||
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
|
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||||
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex-literal = "0.2.1"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "solana_chacha_cuda"
|
@@ -1,6 +1,6 @@
|
|||||||
// Module used by validators to approve storage mining proofs in parallel using the GPU
|
// Module used by validators to approve storage mining proofs in parallel using the GPU
|
||||||
|
|
||||||
use crate::chacha::{CHACHA_BLOCK_SIZE, CHACHA_KEY_SIZE};
|
use solana_chacha::chacha::{CHACHA_BLOCK_SIZE, CHACHA_KEY_SIZE};
|
||||||
use solana_ledger::blockstore::Blockstore;
|
use solana_ledger::blockstore::Blockstore;
|
||||||
use solana_perf::perf_libs;
|
use solana_perf::perf_libs;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
@@ -113,12 +113,12 @@ pub fn chacha_cbc_encrypt_file_many_keys(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::archiver::sample_file;
|
use solana_archiver_utils::sample_file;
|
||||||
use crate::chacha::chacha_cbc_encrypt_ledger;
|
use solana_chacha::chacha::chacha_cbc_encrypt_ledger;
|
||||||
use solana_ledger::entry::create_ticks;
|
use solana_ledger::entry::create_ticks;
|
||||||
use solana_ledger::get_tmp_ledger_path;
|
use solana_ledger::get_tmp_ledger_path;
|
||||||
use solana_sdk::clock::DEFAULT_SLOTS_PER_SEGMENT;
|
use solana_sdk::clock::DEFAULT_SLOTS_PER_SEGMENT;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::Keypair;
|
||||||
use std::fs::{remove_dir_all, remove_file};
|
use std::fs::{remove_dir_all, remove_file};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
8
chacha-cuda/src/lib.rs
Normal file
8
chacha-cuda/src/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate hex_literal;
|
||||||
|
|
||||||
|
pub mod chacha_cuda;
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-chacha-sys"
|
name = "solana-chacha-sys"
|
||||||
version = "0.22.10"
|
version = "0.23.5"
|
||||||
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"
|
||||||
@@ -9,4 +9,4 @@ license = "Apache-2.0"
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0.48"
|
cc = "1.0.49"
|
||||||
|
1
chacha/.gitignore
vendored
Normal file
1
chacha/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/farf/
|
25
chacha/Cargo.toml
Normal file
25
chacha/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "solana-chacha"
|
||||||
|
version = "0.23.5"
|
||||||
|
description = "Solana Chacha APIs"
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
homepage = "https://solana.com/"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4.8"
|
||||||
|
rand = "0.6.5"
|
||||||
|
rand_chacha = "0.1.1"
|
||||||
|
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.5" }
|
||||||
|
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||||
|
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||||
|
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||||
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex-literal = "0.2.1"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "solana_chacha"
|
@@ -74,13 +74,14 @@ pub fn chacha_cbc_encrypt_ledger(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::chacha::chacha_cbc_encrypt_ledger;
|
use crate::chacha::chacha_cbc_encrypt_ledger;
|
||||||
use crate::gen_keys::GenKeys;
|
use rand::SeedableRng;
|
||||||
|
use rand_chacha::ChaChaRng;
|
||||||
use solana_ledger::blockstore::Blockstore;
|
use solana_ledger::blockstore::Blockstore;
|
||||||
use solana_ledger::entry::Entry;
|
use solana_ledger::entry::Entry;
|
||||||
use solana_ledger::get_tmp_ledger_path;
|
use solana_ledger::get_tmp_ledger_path;
|
||||||
use solana_sdk::hash::{hash, Hash, Hasher};
|
use solana_sdk::hash::{hash, Hash, Hasher};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::KeypairUtil;
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -92,8 +93,9 @@ mod tests {
|
|||||||
let one = hash(&zero.as_ref());
|
let one = hash(&zero.as_ref());
|
||||||
|
|
||||||
let seed = [2u8; 32];
|
let seed = [2u8; 32];
|
||||||
let mut rnd = GenKeys::new(seed);
|
|
||||||
let keypair = rnd.gen_keypair();
|
let mut generator = ChaChaRng::from_seed(seed);
|
||||||
|
let keypair = Keypair::generate(&mut generator);
|
||||||
|
|
||||||
let mut id = one;
|
let mut id = one;
|
||||||
let mut num_hashes = 0;
|
let mut num_hashes = 0;
|
||||||
@@ -135,8 +137,9 @@ mod tests {
|
|||||||
let out_path = tmp_file_path("test_encrypt_ledger");
|
let out_path = tmp_file_path("test_encrypt_ledger");
|
||||||
|
|
||||||
let seed = [2u8; 32];
|
let seed = [2u8; 32];
|
||||||
let mut rnd = GenKeys::new(seed);
|
|
||||||
let keypair = rnd.gen_keypair();
|
let mut generator = ChaChaRng::from_seed(seed);
|
||||||
|
let keypair = Keypair::generate(&mut generator);
|
||||||
|
|
||||||
let entries = make_tiny_deterministic_test_entries(slots_per_segment);
|
let entries = make_tiny_deterministic_test_entries(slots_per_segment);
|
||||||
blockstore
|
blockstore
|
8
chacha/src/lib.rs
Normal file
8
chacha/src/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate hex_literal;
|
||||||
|
|
||||||
|
pub mod chacha;
|
@@ -78,7 +78,9 @@ ARGS+=(
|
|||||||
# Also propagate environment variables needed for codecov
|
# Also propagate environment variables needed for codecov
|
||||||
# https://docs.codecov.io/docs/testing-with-docker#section-codecov-inside-docker
|
# https://docs.codecov.io/docs/testing-with-docker#section-codecov-inside-docker
|
||||||
# We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite...
|
# We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite...
|
||||||
CODECOV_ENVS=$(CI=true bash <(curl -s https://codecov.io/env))
|
# Unfortunately, codecov.io fails sometimes:
|
||||||
|
# curl: (7) Failed to connect to codecov.io port 443: Connection timed out
|
||||||
|
CODECOV_ENVS=$(CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused https://codecov.io/env; do sleep 10; done))
|
||||||
|
|
||||||
if $INTERACTIVE; then
|
if $INTERACTIVE; then
|
||||||
if [[ -n $1 ]]; then
|
if [[ -n $1 ]]; then
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM solanalabs/rust:1.40.0
|
FROM solanalabs/rust:1.41.0
|
||||||
ARG date
|
ARG date
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
|
@@ -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.40.0
|
FROM rust:1.41.0
|
||||||
|
|
||||||
# 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
|
||||||
@@ -17,6 +17,7 @@ RUN set -x \
|
|||||||
clang-7 \
|
clang-7 \
|
||||||
cmake \
|
cmake \
|
||||||
lcov \
|
lcov \
|
||||||
|
libudev-dev \
|
||||||
libclang-common-7-dev \
|
libclang-common-7-dev \
|
||||||
mscgen \
|
mscgen \
|
||||||
net-tools \
|
net-tools \
|
||||||
|
@@ -29,7 +29,7 @@ Start a local cluster and run sanity on it
|
|||||||
-x - Add an extra validator (may be supplied multiple times)
|
-x - Add an extra validator (may be supplied multiple times)
|
||||||
-r - Select the RPC endpoint hosted by a node that starts as
|
-r - Select the RPC endpoint hosted by a node that starts as
|
||||||
a validator node. If unspecified the RPC endpoint hosted by
|
a validator node. If unspecified the RPC endpoint hosted by
|
||||||
the bootstrap leader will be used.
|
the bootstrap validator will be used.
|
||||||
-c - Reuse existing node/ledger configuration from a previous sanity
|
-c - Reuse existing node/ledger configuration from a previous sanity
|
||||||
run
|
run
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ source multinode-demo/common.sh
|
|||||||
|
|
||||||
nodes=(
|
nodes=(
|
||||||
"multinode-demo/faucet.sh"
|
"multinode-demo/faucet.sh"
|
||||||
"multinode-demo/bootstrap-leader.sh \
|
"multinode-demo/bootstrap-validator.sh \
|
||||||
--no-restart \
|
--no-restart \
|
||||||
--init-complete-file init-complete-node1.log \
|
--init-complete-file init-complete-node1.log \
|
||||||
--dynamic-port-range 8000-8050"
|
--dynamic-port-range 8000-8050"
|
||||||
@@ -170,7 +170,7 @@ startNodes() {
|
|||||||
logs+=("$(getNodeLogFile "$i" "$cmd")")
|
logs+=("$(getNodeLogFile "$i" "$cmd")")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 1 == bootstrap leader, wait until it boots before starting
|
# 1 == bootstrap validator, wait until it boots before starting
|
||||||
# other validators
|
# other validators
|
||||||
if [[ "$i" -eq 1 ]]; then
|
if [[ "$i" -eq 1 ]]; then
|
||||||
SECONDS=
|
SECONDS=
|
||||||
@@ -178,8 +178,8 @@ startNodes() {
|
|||||||
|
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
$solana_cli --keypair config/bootstrap-leader/identity-keypair.json \
|
$solana_cli --keypair config/bootstrap-validator/identity-keypair.json \
|
||||||
--url http://127.0.0.1:8899 get-genesis-hash
|
--url http://127.0.0.1:8899 genesis-hash
|
||||||
) | tee genesis-hash.log
|
) | tee genesis-hash.log
|
||||||
maybeExpectedGenesisHash="--expected-genesis-hash $(tail -n1 genesis-hash.log)"
|
maybeExpectedGenesisHash="--expected-genesis-hash $(tail -n1 genesis-hash.log)"
|
||||||
fi
|
fi
|
||||||
@@ -277,7 +277,7 @@ rollingNodeRestart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
verifyLedger() {
|
verifyLedger() {
|
||||||
for ledger in bootstrap-leader validator; do
|
for ledger in bootstrap-validator validator; do
|
||||||
echo "--- $ledger ledger verification"
|
echo "--- $ledger ledger verification"
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
@@ -331,7 +331,7 @@ while [[ $iteration -le $iterations ]]; do
|
|||||||
rm -rf $client_keypair
|
rm -rf $client_keypair
|
||||||
) || flag_error
|
) || flag_error
|
||||||
|
|
||||||
echo "--- RPC API: bootstrap-leader getTransactionCount ($iteration)"
|
echo "--- RPC API: bootstrap-validator getTransactionCount ($iteration)"
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
curl --retry 5 --retry-delay 2 --retry-connrefused \
|
curl --retry 5 --retry-delay 2 --retry-connrefused \
|
||||||
@@ -351,7 +351,7 @@ while [[ $iteration -le $iterations ]]; do
|
|||||||
http://localhost:18899
|
http://localhost:18899
|
||||||
) || flag_error
|
) || flag_error
|
||||||
|
|
||||||
# Verify transaction count as reported by the bootstrap-leader node is advancing
|
# Verify transaction count as reported by the bootstrap-validator node is advancing
|
||||||
transactionCount=$(sed -e 's/{"jsonrpc":"2.0","result":\([0-9]*\),"id":1}/\1/' log-transactionCount.txt)
|
transactionCount=$(sed -e 's/{"jsonrpc":"2.0","result":\([0-9]*\),"id":1}/\1/' log-transactionCount.txt)
|
||||||
if [[ -n $lastTransactionCount ]]; then
|
if [[ -n $lastTransactionCount ]]; then
|
||||||
echo "--- Transaction count check: $lastTransactionCount < $transactionCount"
|
echo "--- Transaction count check: $lastTransactionCount < $transactionCount"
|
||||||
|
@@ -20,6 +20,7 @@ declare prints=(
|
|||||||
declare print_free_tree=(
|
declare print_free_tree=(
|
||||||
'core/src'
|
'core/src'
|
||||||
'faucet/src'
|
'faucet/src'
|
||||||
|
'ledger/src'
|
||||||
'metrics/src'
|
'metrics/src'
|
||||||
'net-utils/src'
|
'net-utils/src'
|
||||||
'runtime/src'
|
'runtime/src'
|
||||||
|
@@ -8,7 +8,6 @@ me=$(basename "$0")
|
|||||||
echo --- update gitbook-cage
|
echo --- update gitbook-cage
|
||||||
if [[ -n $CI_BRANCH ]]; then
|
if [[ -n $CI_BRANCH ]]; then
|
||||||
(
|
(
|
||||||
|
|
||||||
set -x
|
set -x
|
||||||
(
|
(
|
||||||
. ci/rust-version.sh stable
|
. ci/rust-version.sh stable
|
||||||
@@ -25,74 +24,8 @@ if [[ -n $CI_BRANCH ]]; then
|
|||||||
git reset --hard HEAD~
|
git reset --hard HEAD~
|
||||||
fi
|
fi
|
||||||
)
|
)
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
source ci/rust-version.sh stable
|
|
||||||
eval "$(ci/channel-info.sh)"
|
|
||||||
|
|
||||||
if [[ -n $PUBLISH_BOOK_TAG ]]; then
|
|
||||||
CURRENT_TAG="$(git describe --tags)"
|
|
||||||
COMMIT_TO_PUBLISH="$(git rev-list -n 1 "${PUBLISH_BOOK_TAG}")"
|
|
||||||
|
|
||||||
# book is manually published at a specified release tag
|
|
||||||
if [[ $PUBLISH_BOOK_TAG != "$CURRENT_TAG" ]]; then
|
|
||||||
(
|
|
||||||
cat <<EOF
|
|
||||||
steps:
|
|
||||||
- trigger: "$BUILDKITE_PIPELINE_SLUG"
|
|
||||||
async: true
|
|
||||||
build:
|
|
||||||
message: "$BUILDKITE_MESSAGE"
|
|
||||||
commit: "$COMMIT_TO_PUBLISH"
|
|
||||||
env:
|
|
||||||
PUBLISH_BOOK_TAG: "$PUBLISH_BOOK_TAG"
|
|
||||||
EOF
|
|
||||||
) | buildkite-agent pipeline upload
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
repo=git@github.com:solana-labs/book.git
|
|
||||||
BOOK="book"
|
|
||||||
else
|
else
|
||||||
# book-edge and book-beta are published automatically on the tip of the branch
|
echo CI_BRANCH not set
|
||||||
case $CHANNEL in
|
|
||||||
edge)
|
|
||||||
repo=git@github.com:solana-labs/book-edge.git
|
|
||||||
;;
|
|
||||||
beta)
|
|
||||||
repo=git@github.com:solana-labs/book-beta.git
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "--- publish skipped"
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
BOOK=$CHANNEL
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ci/docker-run.sh "$rust_stable_docker_image" bash -exc "book/build.sh"
|
|
||||||
|
|
||||||
echo --- create book repo
|
|
||||||
(
|
|
||||||
set -x
|
|
||||||
cd book/html/
|
|
||||||
git init .
|
|
||||||
git add ./* ./.nojekyll
|
|
||||||
git config user.email maintainers@solana.com
|
|
||||||
git config user.name "$me"
|
|
||||||
git commit -m "${CI_COMMIT:-local}"
|
|
||||||
)
|
|
||||||
|
|
||||||
echo "--- publish $BOOK"
|
|
||||||
(
|
|
||||||
cd book/html/
|
|
||||||
git remote add origin $repo
|
|
||||||
git fetch origin master
|
|
||||||
if ! git diff HEAD origin/master --quiet; then
|
|
||||||
git push -f origin HEAD:master
|
|
||||||
else
|
|
||||||
echo "Content unchanged, publish skipped"
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
@@ -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.40.0
|
stable_version=1.41.0
|
||||||
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=2019-12-19
|
nightly_version=2020-02-06
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@ _ cargo +"$rust_stable" clippy --all --exclude solana-sdk-c -- --deny=warnings
|
|||||||
_ cargo +"$rust_stable" clippy --manifest-path sdk-c/Cargo.toml -- --deny=warnings
|
_ cargo +"$rust_stable" clippy --manifest-path sdk-c/Cargo.toml -- --deny=warnings
|
||||||
|
|
||||||
_ cargo +"$rust_stable" audit --version
|
_ cargo +"$rust_stable" audit --version
|
||||||
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2019-0013 --ignore RUSTSEC-2018-0015 --ignore RUSTSEC-2019-0031 --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
|
_ book/build.sh
|
||||||
@@ -30,7 +30,7 @@ _ ci/check-ssh-keys.sh
|
|||||||
|
|
||||||
{
|
{
|
||||||
cd programs/bpf
|
cd programs/bpf
|
||||||
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2019-0031
|
_ cargo +"$rust_stable" audit
|
||||||
for project in rust/*/ ; do
|
for project in rust/*/ ; do
|
||||||
echo "+++ do_bpf_checks $project"
|
echo "+++ do_bpf_checks $project"
|
||||||
(
|
(
|
||||||
|
@@ -42,7 +42,9 @@ if [[ -z "$CODECOV_TOKEN" ]]; then
|
|||||||
echo CODECOV_TOKEN undefined, codecov.io upload skipped
|
echo CODECOV_TOKEN undefined, codecov.io upload skipped
|
||||||
else
|
else
|
||||||
# We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite...
|
# We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite...
|
||||||
CI=true bash <(curl -s https://codecov.io/bash) -X gcov -f target/cov/lcov.info
|
# Unfortunately, codecov.io fails sometimes:
|
||||||
|
# curl: (7) Failed to connect to codecov.io port 443: Connection timed out
|
||||||
|
CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused https://codecov.io/bash; do sleep 10; done) -Z -X gcov -f target/cov/lcov.info
|
||||||
|
|
||||||
annotate --style success --context codecov.io \
|
annotate --style success --context codecov.io \
|
||||||
"CodeCov report: https://codecov.io/github/solana-labs/solana/commit/${CI_COMMIT:0:9}"
|
"CodeCov report: https://codecov.io/github/solana-labs/solana/commit/${CI_COMMIT:0:9}"
|
||||||
|
@@ -86,7 +86,7 @@ test-stable-perf)
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
_ cargo +"$rust_stable" build --bins ${V:+--verbose}
|
_ cargo +"$rust_stable" build --bins ${V:+--verbose}
|
||||||
_ cargo +"$rust_stable" test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture
|
_ cargo +"$rust_stable" test --package solana-chacha-cuda --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture
|
||||||
;;
|
;;
|
||||||
test-move)
|
test-move)
|
||||||
ci/affects-files.sh \
|
ci/affects-files.sh \
|
||||||
|
@@ -206,6 +206,7 @@ steps:
|
|||||||
TESTNET_DB_HOST: "$TESTNET_DB_HOST"
|
TESTNET_DB_HOST: "$TESTNET_DB_HOST"
|
||||||
GCE_NODE_COUNT: "$GCE_NODE_COUNT"
|
GCE_NODE_COUNT: "$GCE_NODE_COUNT"
|
||||||
GCE_LOW_QUOTA_NODE_COUNT: "$GCE_LOW_QUOTA_NODE_COUNT"
|
GCE_LOW_QUOTA_NODE_COUNT: "$GCE_LOW_QUOTA_NODE_COUNT"
|
||||||
|
RUST_LOG: "$RUST_LOG"
|
||||||
EOF
|
EOF
|
||||||
) | buildkite-agent pipeline upload
|
) | buildkite-agent pipeline upload
|
||||||
exit 0
|
exit 0
|
||||||
@@ -377,7 +378,7 @@ deploy() {
|
|||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
ci/testnet-deploy.sh -p testnet-solana-com -C gce -z us-west1-b \
|
ci/testnet-deploy.sh -p testnet-solana-com -C gce -z us-west1-b \
|
||||||
-t "$CHANNEL_OR_TAG" -n 1 -c 0 -u -P \
|
-t "$CHANNEL_OR_TAG" -n 0 -c 0 -u -P \
|
||||||
-a testnet-solana-com --letsencrypt testnet.solana.com \
|
-a testnet-solana-com --letsencrypt testnet.solana.com \
|
||||||
--limit-ledger-size \
|
--limit-ledger-size \
|
||||||
${skipCreate:+-e} \
|
${skipCreate:+-e} \
|
||||||
@@ -388,7 +389,7 @@ deploy() {
|
|||||||
(
|
(
|
||||||
echo "--- net.sh update"
|
echo "--- net.sh update"
|
||||||
set -x
|
set -x
|
||||||
time net/net.sh update -t "$CHANNEL_OR_TAG" --platform linux --platform osx --platform windows
|
time net/net.sh update -t "$CHANNEL_OR_TAG" --platform linux --platform osx #--platform windows
|
||||||
)
|
)
|
||||||
;;
|
;;
|
||||||
testnet-perf)
|
testnet-perf)
|
||||||
@@ -454,6 +455,10 @@ deploy() {
|
|||||||
TDS_CLIENT_COUNT="1"
|
TDS_CLIENT_COUNT="1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -n $TDS_SLOTS_PER_EPOCH ]]; then
|
||||||
|
maybeSlotsPerEpoch=(--slots-per-epoch "$TDS_SLOTS_PER_EPOCH")
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -z $ENABLE_GPU ]]; then
|
if [[ -z $ENABLE_GPU ]]; then
|
||||||
maybeGpu=(-G "--machine-type n1-standard-16 --accelerator count=2,type=nvidia-tesla-v100")
|
maybeGpu=(-G "--machine-type n1-standard-16 --accelerator count=2,type=nvidia-tesla-v100")
|
||||||
elif [[ $ENABLE_GPU == skip ]]; then
|
elif [[ $ENABLE_GPU == skip ]]; then
|
||||||
@@ -539,7 +544,7 @@ deploy() {
|
|||||||
${maybeInternalNodesLamports} \
|
${maybeInternalNodesLamports} \
|
||||||
${maybeExternalAccountsFile} \
|
${maybeExternalAccountsFile} \
|
||||||
--target-lamports-per-signature 0 \
|
--target-lamports-per-signature 0 \
|
||||||
--slots-per-epoch 4096 \
|
"${maybeSlotsPerEpoch[@]}" \
|
||||||
${maybeAdditionalDisk}
|
${maybeAdditionalDisk}
|
||||||
)
|
)
|
||||||
;;
|
;;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-clap-utils"
|
name = "solana-clap-utils"
|
||||||
version = "0.22.10"
|
version = "0.23.5"
|
||||||
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,9 +11,9 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
rpassword = "4.0"
|
rpassword = "4.0"
|
||||||
semver = "0.9.0"
|
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.22.10" }
|
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||||
tiny-bip39 = "0.6.2"
|
tiny-bip39 = "0.7.0"
|
||||||
url = "2.1.0"
|
url = "2.1.0"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
use crate::keypair::{keypair_from_seed_phrase, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG};
|
use crate::keypair::{keypair_from_seed_phrase, 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_sdk::{
|
use solana_sdk::{
|
||||||
clock::UnixTimestamp,
|
clock::UnixTimestamp,
|
||||||
native_token::sol_to_lamports,
|
native_token::sol_to_lamports,
|
||||||
@@ -64,6 +65,20 @@ pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {
|
|||||||
value_of(matches, name).or_else(|| keypair_of(matches, name).map(|keypair| keypair.pubkey()))
|
value_of(matches, name).or_else(|| keypair_of(matches, name).map(|keypair| keypair.pubkey()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pubkeys_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Pubkey>> {
|
||||||
|
matches.values_of(name).map(|values| {
|
||||||
|
values
|
||||||
|
.map(|value| {
|
||||||
|
value.parse::<Pubkey>().unwrap_or_else(|_| {
|
||||||
|
read_keypair_file(value)
|
||||||
|
.expect("read_keypair_file failed")
|
||||||
|
.pubkey()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Return pubkey/signature pairs for a string of the form pubkey=signature
|
// Return pubkey/signature pairs for a string of the form pubkey=signature
|
||||||
pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubkey, Signature)>> {
|
pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubkey, Signature)>> {
|
||||||
matches.values_of(name).map(|values| {
|
matches.values_of(name).map(|values| {
|
||||||
@@ -86,6 +101,16 @@ pub fn amount_of(matches: &ArgMatches<'_>, name: &str, unit: &str) -> Option<u64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn derivation_of(matches: &ArgMatches<'_>, name: &str) -> Option<DerivationPath> {
|
||||||
|
matches.value_of(name).map(|derivation_str| {
|
||||||
|
let derivation_str = derivation_str.replace("'", "");
|
||||||
|
let mut parts = derivation_str.split('/');
|
||||||
|
let account = parts.next().unwrap().parse::<u16>().unwrap();
|
||||||
|
let change = parts.next().map(|change| change.parse::<u16>().unwrap());
|
||||||
|
DerivationPath { account, change }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -154,7 +179,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_keypair_of() {
|
fn test_keypair_of() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
|
let outfile = tmp_file_path("test_keypair_of.json", &keypair.pubkey());
|
||||||
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
||||||
|
|
||||||
let matches = app()
|
let matches = app()
|
||||||
@@ -178,7 +203,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_pubkey_of() {
|
fn test_pubkey_of() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
|
let outfile = tmp_file_path("test_pubkey_of.json", &keypair.pubkey());
|
||||||
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
||||||
|
|
||||||
let matches = app()
|
let matches = app()
|
||||||
@@ -202,6 +227,26 @@ mod tests {
|
|||||||
fs::remove_file(&outfile).unwrap();
|
fs::remove_file(&outfile).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pubkeys_of() {
|
||||||
|
let keypair = Keypair::new();
|
||||||
|
let outfile = tmp_file_path("test_pubkeys_of.json", &keypair.pubkey());
|
||||||
|
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
||||||
|
|
||||||
|
let matches = app().clone().get_matches_from(vec![
|
||||||
|
"test",
|
||||||
|
"--multiple",
|
||||||
|
&keypair.pubkey().to_string(),
|
||||||
|
"--multiple",
|
||||||
|
&outfile,
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
pubkeys_of(&matches, "multiple"),
|
||||||
|
Some(vec![keypair.pubkey(), keypair.pubkey()])
|
||||||
|
);
|
||||||
|
fs::remove_file(&outfile).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pubkeys_sigs_of() {
|
fn test_pubkeys_sigs_of() {
|
||||||
let key1 = Pubkey::new_rand();
|
let key1 = Pubkey::new_rand();
|
||||||
@@ -243,4 +288,40 @@ mod tests {
|
|||||||
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "lamports"]);
|
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "lamports"]);
|
||||||
assert_eq!(amount_of(&matches, "single", "unit"), None);
|
assert_eq!(amount_of(&matches, "single", "unit"), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_derivation_of() {
|
||||||
|
let matches = app()
|
||||||
|
.clone()
|
||||||
|
.get_matches_from(vec!["test", "--single", "2/3"]);
|
||||||
|
assert_eq!(
|
||||||
|
derivation_of(&matches, "single"),
|
||||||
|
Some(DerivationPath {
|
||||||
|
account: 2,
|
||||||
|
change: Some(3)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(derivation_of(&matches, "another"), None);
|
||||||
|
let matches = app()
|
||||||
|
.clone()
|
||||||
|
.get_matches_from(vec!["test", "--single", "2"]);
|
||||||
|
assert_eq!(
|
||||||
|
derivation_of(&matches, "single"),
|
||||||
|
Some(DerivationPath {
|
||||||
|
account: 2,
|
||||||
|
change: None
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(derivation_of(&matches, "another"), None);
|
||||||
|
let matches = app()
|
||||||
|
.clone()
|
||||||
|
.get_matches_from(vec!["test", "--single", "2'/3'"]);
|
||||||
|
assert_eq!(
|
||||||
|
derivation_of(&matches, "single"),
|
||||||
|
Some(DerivationPath {
|
||||||
|
account: 2,
|
||||||
|
change: Some(3)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
use crate::keypair::ASK_KEYWORD;
|
use crate::keypair::ASK_KEYWORD;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::{
|
||||||
use solana_sdk::pubkey::Pubkey;
|
hash::Hash,
|
||||||
use solana_sdk::signature::{read_keypair_file, Signature};
|
pubkey::Pubkey,
|
||||||
|
signature::{read_keypair_file, Signature},
|
||||||
|
};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
// Return an error if a pubkey cannot be parsed.
|
// Return an error if a pubkey cannot be parsed.
|
||||||
@@ -84,20 +86,6 @@ pub fn is_url(string: String) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_semver(semver: &str) -> Result<(), String> {
|
|
||||||
match semver::Version::parse(&semver) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(err) => Err(format!("{:?}", err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_release_channel(channel: &str) -> Result<(), String> {
|
|
||||||
match channel {
|
|
||||||
"edge" | "beta" | "stable" => Ok(()),
|
|
||||||
_ => Err(format!("Invalid release channel {}", channel)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_port(port: String) -> Result<(), String> {
|
pub fn is_port(port: String) -> Result<(), String> {
|
||||||
port.parse::<u16>()
|
port.parse::<u16>()
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
@@ -141,3 +129,47 @@ pub fn is_rfc3339_datetime(value: String) -> Result<(), String> {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| format!("{:?}", e))
|
.map_err(|e| format!("{:?}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_derivation(value: String) -> Result<(), String> {
|
||||||
|
let value = value.replace("'", "");
|
||||||
|
let mut parts = value.split('/');
|
||||||
|
let account = parts.next().unwrap();
|
||||||
|
account
|
||||||
|
.parse::<u16>()
|
||||||
|
.map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"Unable to parse derivation, provided: {}, err: {:?}",
|
||||||
|
account, e
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and_then(|_| {
|
||||||
|
if let Some(change) = parts.next() {
|
||||||
|
change.parse::<u16>().map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"Unable to parse derivation, provided: {}, err: {:?}",
|
||||||
|
change, e
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_derivation() {
|
||||||
|
assert_eq!(is_derivation("2".to_string()), Ok(()));
|
||||||
|
assert_eq!(is_derivation("0".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("65537".to_string()).is_err());
|
||||||
|
assert!(is_derivation("a/b".to_string()).is_err());
|
||||||
|
assert!(is_derivation("0/65537".to_string()).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -2,12 +2,9 @@ use crate::ArgConstant;
|
|||||||
use bip39::{Language, Mnemonic, Seed};
|
use bip39::{Language, Mnemonic, Seed};
|
||||||
use clap::values_t;
|
use clap::values_t;
|
||||||
use rpassword::prompt_password_stderr;
|
use rpassword::prompt_password_stderr;
|
||||||
use solana_sdk::{
|
use solana_sdk::signature::{
|
||||||
pubkey::Pubkey,
|
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
|
||||||
signature::{
|
KeypairUtil,
|
||||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
|
|
||||||
KeypairUtil,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
error,
|
error,
|
||||||
@@ -15,13 +12,32 @@ use std::{
|
|||||||
process::exit,
|
process::exit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub enum KeypairUrl {
|
||||||
|
Ask,
|
||||||
|
Filepath(String),
|
||||||
|
Usb(String),
|
||||||
|
Stdin,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_keypair_path(path: &str) -> KeypairUrl {
|
||||||
|
if path == "-" {
|
||||||
|
KeypairUrl::Stdin
|
||||||
|
} else if path == ASK_KEYWORD {
|
||||||
|
KeypairUrl::Ask
|
||||||
|
} else if path.starts_with("usb://") {
|
||||||
|
KeypairUrl::Usb(path.split_at(6).1.to_string())
|
||||||
|
} else {
|
||||||
|
KeypairUrl::Filepath(path.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
||||||
pub const ASK_KEYWORD: &str = "ASK";
|
pub const ASK_KEYWORD: &str = "ASK";
|
||||||
|
|
||||||
pub const ASK_SEED_PHRASE_ARG: ArgConstant<'static> = ArgConstant {
|
pub const ASK_SEED_PHRASE_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
long: "ask-seed-phrase",
|
long: "ask-seed-phrase",
|
||||||
name: "ask_seed_phrase",
|
name: "ask_seed_phrase",
|
||||||
help: "Securely recover a keypair using a seed phrase and optional passphrase",
|
help: "Recover a keypair using a seed phrase and optional passphrase",
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
@@ -32,8 +48,8 @@ pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
File,
|
|
||||||
Generated,
|
Generated,
|
||||||
|
Path,
|
||||||
SeedPhrase,
|
SeedPhrase,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,14 +96,14 @@ pub fn keypair_from_seed_phrase(
|
|||||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||||
} else {
|
} else {
|
||||||
let sanitized = sanitize_seed_phrase(seed_phrase);
|
let sanitized = sanitize_seed_phrase(seed_phrase);
|
||||||
let mnemonic = Mnemonic::from_phrase(sanitized, Language::English)?;
|
let mnemonic = Mnemonic::from_phrase(&sanitized, Language::English)?;
|
||||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||||
let seed = Seed::new(&mnemonic, &passphrase);
|
let seed = Seed::new(&mnemonic, &passphrase);
|
||||||
keypair_from_seed(seed.as_bytes())?
|
keypair_from_seed(seed.as_bytes())?
|
||||||
};
|
};
|
||||||
|
|
||||||
if confirm_pubkey {
|
if confirm_pubkey {
|
||||||
let pubkey = Pubkey::new(keypair.public.as_ref());
|
let pubkey = keypair.pubkey();
|
||||||
print!("Recovered pubkey `{:?}`. Continue? (y/n): ", pubkey);
|
print!("Recovered pubkey `{:?}`. Continue? (y/n): ", pubkey);
|
||||||
let _ignored = stdout().flush();
|
let _ignored = stdout().flush();
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
@@ -131,7 +147,12 @@ pub fn keypair_input(
|
|||||||
keypair_from_seed_phrase(keypair_name, skip_validation, true)
|
keypair_from_seed_phrase(keypair_name, skip_validation, true)
|
||||||
.map(|keypair| KeypairWithSource::new(keypair, Source::SeedPhrase))
|
.map(|keypair| KeypairWithSource::new(keypair, Source::SeedPhrase))
|
||||||
} else if let Some(keypair_file) = matches.value_of(keypair_match_name) {
|
} else if let Some(keypair_file) = matches.value_of(keypair_match_name) {
|
||||||
read_keypair_file(keypair_file).map(|keypair| KeypairWithSource::new(keypair, Source::File))
|
if keypair_file.starts_with("usb://") {
|
||||||
|
Ok(KeypairWithSource::new(Keypair::new(), Source::Path))
|
||||||
|
} else {
|
||||||
|
read_keypair_file(keypair_file)
|
||||||
|
.map(|keypair| KeypairWithSource::new(keypair, Source::Path))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(KeypairWithSource::new(Keypair::new(), Source::Generated))
|
Ok(KeypairWithSource::new(Keypair::new(), Source::Generated))
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-fixed-buf"
|
|
||||||
version = "0.22.10"
|
|
||||||
description = "A fixed-size byte array that supports bincode serde"
|
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
name = "solana-cli-config"
|
||||||
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
|
version = "0.23.5"
|
||||||
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/"
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.2.1"
|
dirs = "2.0.2"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "solana_fixed_buf"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
|
serde_yaml = "0.8.11"
|
@@ -1,8 +1,10 @@
|
|||||||
// Wallet settings that can be configured for long-term use
|
// Wallet settings that can be configured for long-term use
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::fs::{create_dir_all, File};
|
use std::{
|
||||||
use std::io::{self, Write};
|
fs::{create_dir_all, File},
|
||||||
use std::path::Path;
|
io::{self, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CONFIG_FILE: Option<String> = {
|
pub static ref CONFIG_FILE: Option<String> = {
|
4
cli-config/src/lib.rs
Normal file
4
cli-config/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
pub mod config;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user