Compare commits
177 Commits
document-r
...
v0.23.8
Author | SHA1 | Date | |
---|---|---|---|
db949b4059 | |||
2bc735bc85 | |||
dca0a864b6 | |||
f2f6828691 | |||
797e4b11e2 | |||
80206decf8 | |||
eb4d6912bd | |||
febc7c7ac0 | |||
f8add53a71 | |||
18b55bbfe1 | |||
85637e8f63 | |||
eb3b5d7382 | |||
1a4de4d3c4 | |||
100a11f061 | |||
6d431b8210 | |||
242afa7e6b | |||
536e01121f | |||
93bcb65d8b | |||
0743334486 | |||
93027fa067 | |||
8548c83e48 | |||
80dd5db628 | |||
2d0ee7e3dc | |||
df2b4d31b7 | |||
45612bc988 | |||
51ce98badd | |||
075e784bef | |||
c1b587c6e4 | |||
94e2d0b5c2 | |||
c2be9fdf0e | |||
c2b17c7d3f | |||
31544f2a82 | |||
3a88190e4e | |||
3f30354d1a | |||
11f15c0708 | |||
a83bf85bb3 | |||
02877814fa | |||
29cdfd6bc9 | |||
9dffc3abe4 | |||
b4eb81546e | |||
489fd3058f | |||
e5872ef1c1 | |||
cb9d18316a | |||
c3ac85828b | |||
5fbddd5894 | |||
90af35737d | |||
58cb21402b | |||
824b894977 | |||
2295a5e512 | |||
83a322a211 | |||
a008748d9d | |||
72cb0b7c9e | |||
ede3781f91 | |||
e3ac6fac1e | |||
e30561f8a0 | |||
8d59bef561 | |||
897e1fc5d6 | |||
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 |
829
Cargo.lock
generated
829
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ members = [
|
|||||||
"chacha",
|
"chacha",
|
||||||
"chacha-cuda",
|
"chacha-cuda",
|
||||||
"chacha-sys",
|
"chacha-sys",
|
||||||
|
"cli-config",
|
||||||
"client",
|
"client",
|
||||||
"core",
|
"core",
|
||||||
"faucet",
|
"faucet",
|
||||||
@ -42,6 +43,7 @@ members = [
|
|||||||
"archiver",
|
"archiver",
|
||||||
"archiver-lib",
|
"archiver-lib",
|
||||||
"archiver-utils",
|
"archiver-utils",
|
||||||
|
"remote-wallet",
|
||||||
"runtime",
|
"runtime",
|
||||||
"sdk",
|
"sdk",
|
||||||
"sdk-c",
|
"sdk-c",
|
||||||
|
@ -126,10 +126,7 @@ Remote Testnets
|
|||||||
---
|
---
|
||||||
|
|
||||||
We maintain several testnets:
|
We maintain several testnets:
|
||||||
|
* `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
|
||||||
* `testnet` - public stable testnet accessible via testnet.solana.com. Runs 24/7
|
|
||||||
* `testnet-beta` - public beta channel testnet accessible via beta.testnet.solana.com. Runs 24/7
|
|
||||||
* `testnet-edge` - public edge channel testnet accessible via edge.testnet.solana.com. Runs 24/7
|
|
||||||
|
|
||||||
## Deploy process
|
## Deploy process
|
||||||
|
|
||||||
|
@ -140,9 +140,9 @@ 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.
|
||||||
|
|
||||||
### Update software on testnet.solana.com
|
### Update software on devnet.solana.com
|
||||||
|
|
||||||
The testnet running on testnet.solana.com is set to use a fixed release tag
|
The testnet running on devnet.solana.com is set to use a fixed release tag
|
||||||
which is set in the Buildkite testnet-management pipeline.
|
which is set in the Buildkite testnet-management pipeline.
|
||||||
This tag needs to be updated and the testnet restarted after a new release
|
This tag needs to be updated and the testnet restarted after a new release
|
||||||
tag is created.
|
tag is created.
|
||||||
@ -182,4 +182,4 @@ TESTNET_OP=create-and-start
|
|||||||
### Alert the community
|
### Alert the community
|
||||||
|
|
||||||
Notify Discord users on #validator-support that a new release for
|
Notify Discord users on #validator-support that a new release for
|
||||||
testnet.solana.com is available
|
devnet.solana.com is available
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-archiver-lib"
|
name = "solana-archiver-lib"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
description = "Solana Archiver Library"
|
description = "Solana Archiver Library"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@ -15,22 +15,22 @@ ed25519-dalek = "=1.0.0-pre.1"
|
|||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
rand_chacha = "0.1.1"
|
rand_chacha = "0.1.1"
|
||||||
solana-client = { path = "../client", version = "0.23.0" }
|
solana-client = { path = "../client", version = "0.23.8" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "0.23.0" }
|
solana-storage-program = { path = "../programs/storage", version = "0.23.8" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_json = "1.0.44"
|
serde_json = "1.0.44"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
solana-chacha = { path = "../chacha", version = "0.23.0" }
|
solana-chacha = { path = "../chacha", version = "0.23.8" }
|
||||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.0" }
|
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.8" }
|
||||||
solana-ledger = { path = "../ledger", version = "0.23.0" }
|
solana-ledger = { path = "../ledger", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-perf = { path = "../perf", version = "0.23.0" }
|
solana-perf = { path = "../perf", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
solana-core = { path = "../core", version = "0.23.0" }
|
solana-core = { path = "../core", version = "0.23.8" }
|
||||||
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.0" }
|
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.8" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.23.0" }
|
solana-metrics = { path = "../metrics", version = "0.23.8" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4.0"
|
hex = "0.4.0"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use crate::result::ArchiverError;
|
use crate::result::ArchiverError;
|
||||||
use crossbeam_channel::unbounded;
|
use crossbeam_channel::unbounded;
|
||||||
use ed25519_dalek;
|
|
||||||
use rand::{thread_rng, Rng, SeedableRng};
|
use rand::{thread_rng, Rng, SeedableRng};
|
||||||
use rand_chacha::ChaChaRng;
|
use rand_chacha::ChaChaRng;
|
||||||
use solana_archiver_utils::sample_file;
|
use solana_archiver_utils::sample_file;
|
||||||
@ -16,6 +15,7 @@ use solana_core::{
|
|||||||
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},
|
||||||
|
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,
|
||||||
@ -36,7 +36,7 @@ use solana_sdk::{
|
|||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
message::Message,
|
||||||
signature::{Keypair, KeypairUtil, Signature},
|
signature::{Keypair, Signature, Signer},
|
||||||
timing::timestamp,
|
timing::timestamp,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
transport::TransportError,
|
transport::TransportError,
|
||||||
@ -87,11 +87,11 @@ struct ArchiverMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@ -195,13 +195,7 @@ 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, _) =
|
||||||
@ -390,7 +384,7 @@ impl Archiver {
|
|||||||
);
|
);
|
||||||
let message =
|
let message =
|
||||||
Message::new_with_payer(vec![ix], Some(&archiver_keypair.pubkey()));
|
Message::new_with_payer(vec![ix], Some(&archiver_keypair.pubkey()));
|
||||||
if let Err(e) = client.send_message(&[&archiver_keypair], message) {
|
if let Err(e) = client.send_message(&[archiver_keypair.as_ref()], message) {
|
||||||
error!("unable to redeem reward, tx failed: {:?}", e);
|
error!("unable to redeem reward, tx failed: {:?}", e);
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
@ -443,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();
|
||||||
@ -522,6 +516,8 @@ impl Archiver {
|
|||||||
let mut contact_info = node_info.clone();
|
let mut contact_info = node_info.clone();
|
||||||
contact_info.tvu = "0.0.0.0:0".parse().unwrap();
|
contact_info.tvu = "0.0.0.0:0".parse().unwrap();
|
||||||
contact_info.wallclock = timestamp();
|
contact_info.wallclock = timestamp();
|
||||||
|
// copy over the adopted shred_version from the entrypoint
|
||||||
|
contact_info.shred_version = cluster_info.read().unwrap().my_data().shred_version;
|
||||||
{
|
{
|
||||||
let mut cluster_info_w = cluster_info.write().unwrap();
|
let mut cluster_info_w = cluster_info.write().unwrap();
|
||||||
cluster_info_w.insert_self(contact_info);
|
cluster_info_w.insert_self(contact_info);
|
||||||
@ -675,7 +671,7 @@ impl Archiver {
|
|||||||
blockhash,
|
blockhash,
|
||||||
);
|
);
|
||||||
if let Err(err) = client.send_and_confirm_transaction(
|
if let Err(err) = client.send_and_confirm_transaction(
|
||||||
&[&archiver_keypair, &storage_keypair],
|
&[archiver_keypair.as_ref(), storage_keypair.as_ref()],
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
@ -701,7 +697,7 @@ impl Archiver {
|
|||||||
) -> Result<u64> {
|
) -> Result<u64> {
|
||||||
let rpc_peers = {
|
let rpc_peers = {
|
||||||
let cluster_info = cluster_info.read().unwrap();
|
let cluster_info = cluster_info.read().unwrap();
|
||||||
cluster_info.rpc_peers()
|
cluster_info.all_rpc_peers()
|
||||||
};
|
};
|
||||||
debug!("rpc peers: {:?}", rpc_peers);
|
debug!("rpc peers: {:?}", rpc_peers);
|
||||||
if !rpc_peers.is_empty() {
|
if !rpc_peers.is_empty() {
|
||||||
@ -757,7 +753,7 @@ impl Archiver {
|
|||||||
loop {
|
loop {
|
||||||
let rpc_peers = {
|
let rpc_peers = {
|
||||||
let cluster_info = cluster_info.read().unwrap();
|
let cluster_info = cluster_info.read().unwrap();
|
||||||
cluster_info.rpc_peers()
|
cluster_info.all_rpc_peers()
|
||||||
};
|
};
|
||||||
debug!("rpc peers: {:?}", rpc_peers);
|
debug!("rpc peers: {:?}", rpc_peers);
|
||||||
if !rpc_peers.is_empty() {
|
if !rpc_peers.is_empty() {
|
||||||
@ -812,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,
|
||||||
@ -832,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 {
|
||||||
@ -855,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()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-archiver-utils"
|
name = "solana-archiver-utils"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
description = "Solana Archiver Utils"
|
description = "Solana Archiver Utils"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@ -12,12 +12,12 @@ edition = "2018"
|
|||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
rand_chacha = "0.1.1"
|
rand_chacha = "0.1.1"
|
||||||
solana-chacha = { path = "../chacha", version = "0.23.0" }
|
solana-chacha = { path = "../chacha", version = "0.23.8" }
|
||||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.0" }
|
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.8" }
|
||||||
solana-ledger = { path = "../ledger", version = "0.23.0" }
|
solana-ledger = { path = "../ledger", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-perf = { path = "../perf", version = "0.23.0" }
|
solana-perf = { path = "../perf", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4.0"
|
hex = "0.4.0"
|
||||||
|
@ -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.23.0"
|
version = "0.23.8"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@ -10,11 +10,11 @@ homepage = "https://solana.com/"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
console = "0.9.1"
|
console = "0.9.1"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.8" }
|
||||||
solana-core = { path = "../core", version = "0.23.0" }
|
solana-core = { path = "../core", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.23.0" }
|
solana-metrics = { path = "../metrics", version = "0.23.8" }
|
||||||
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.0" }
|
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.8" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use solana_core::{
|
|||||||
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
||||||
contact_info::ContactInfo,
|
contact_info::ContactInfo,
|
||||||
};
|
};
|
||||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::KeypairUtil};
|
use solana_sdk::{commitment_config::CommitmentConfig, signature::Signer};
|
||||||
use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc};
|
use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -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.23.0"
|
version = "0.23.8"
|
||||||
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.23.0" }
|
solana-core = { path = "../core", version = "0.23.8" }
|
||||||
solana-ledger = { path = "../ledger", version = "0.23.0" }
|
solana-ledger = { path = "../ledger", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.23.0" }
|
solana-runtime = { path = "../runtime", version = "0.23.8" }
|
||||||
solana-measure = { path = "../measure", version = "0.23.0" }
|
solana-measure = { path = "../measure", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
crossbeam-channel = "0.3"
|
crossbeam-channel = "0.3"
|
||||||
|
@ -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.23.0"
|
version = "0.23.8"
|
||||||
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.23.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.8" }
|
||||||
solana-core = { path = "../core", version = "0.23.0" }
|
solana-core = { path = "../core", version = "0.23.8" }
|
||||||
solana-genesis = { path = "../genesis", version = "0.23.0" }
|
solana-genesis = { path = "../genesis", version = "0.23.8" }
|
||||||
solana-client = { path = "../client", version = "0.23.0" }
|
solana-client = { path = "../client", version = "0.23.8" }
|
||||||
solana-faucet = { path = "../faucet", version = "0.23.0" }
|
solana-faucet = { path = "../faucet", version = "0.23.8" }
|
||||||
solana-exchange-program = { path = "../programs/exchange", version = "0.23.0" }
|
solana-exchange-program = { path = "../programs/exchange", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.23.0" }
|
solana-metrics = { path = "../metrics", version = "0.23.8" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.23.0" }
|
solana-runtime = { path = "../runtime", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
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.23.0" }
|
solana-local-cluster = { path = "../local-cluster", version = "0.23.8" }
|
||||||
|
@ -15,7 +15,7 @@ use solana_sdk::{
|
|||||||
client::{Client, SyncClient},
|
client::{Client, SyncClient},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, KeypairUtil},
|
signature::{Keypair, Signer},
|
||||||
timing::{duration_as_ms, duration_as_s},
|
timing::{duration_as_ms, duration_as_s},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
{system_instruction, system_program},
|
{system_instruction, system_program},
|
||||||
@ -701,7 +701,7 @@ fn verify_funding_transfer<T: SyncClient + ?Sized>(
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
|
pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
|
||||||
let total = lamports * (dests.len() as u64 + 1);
|
let total = lamports * (dests.len() as u64 + 1);
|
||||||
let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
|
let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
|
||||||
let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
|
let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
|
||||||
@ -824,7 +824,11 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], accounts: &[Keypair]) {
|
pub fn create_token_accounts<T: Client>(
|
||||||
|
client: &T,
|
||||||
|
signers: &[Arc<Keypair>],
|
||||||
|
accounts: &[Keypair],
|
||||||
|
) {
|
||||||
let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect();
|
let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect();
|
||||||
|
|
||||||
while !notfunded.is_empty() {
|
while !notfunded.is_empty() {
|
||||||
@ -968,7 +972,12 @@ fn generate_keypairs(num: u64) -> Vec<Keypair> {
|
|||||||
rnd.gen_n_keypairs(num)
|
rnd.gen_n_keypairs(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn airdrop_lamports(client: &dyn Client, faucet_addr: &SocketAddr, id: &Keypair, amount: u64) {
|
pub fn airdrop_lamports<T: Client>(
|
||||||
|
client: &T,
|
||||||
|
faucet_addr: &SocketAddr,
|
||||||
|
id: &Keypair,
|
||||||
|
amount: u64,
|
||||||
|
) {
|
||||||
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent());
|
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent());
|
||||||
let balance = balance.unwrap_or(0);
|
let balance = balance.unwrap_or(0);
|
||||||
if balance >= amount {
|
if balance >= amount {
|
||||||
|
@ -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;
|
||||||
|
@ -5,7 +5,7 @@ pub mod order_book;
|
|||||||
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
|
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
|
||||||
use log::*;
|
use log::*;
|
||||||
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
||||||
use solana_sdk::signature::KeypairUtil;
|
use solana_sdk::signature::Signer;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
@ -10,12 +10,13 @@ use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
|||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_runtime::bank_client::BankClient;
|
use solana_runtime::bank_client::BankClient;
|
||||||
use solana_sdk::genesis_config::create_genesis_config;
|
use solana_sdk::genesis_config::create_genesis_config;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::sync::mpsc::channel;
|
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.23.0"
|
version = "0.23.8"
|
||||||
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.23.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.8" }
|
||||||
solana-core = { path = "../core", version = "0.23.0" }
|
solana-core = { path = "../core", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
|
@ -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.23.0"
|
version = "0.23.8"
|
||||||
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.23.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.8" }
|
||||||
solana-core = { path = "../core", version = "0.23.0" }
|
solana-core = { path = "../core", version = "0.23.8" }
|
||||||
solana-genesis = { path = "../genesis", version = "0.23.0" }
|
solana-genesis = { path = "../genesis", version = "0.23.8" }
|
||||||
solana-client = { path = "../client", version = "0.23.0" }
|
solana-client = { path = "../client", version = "0.23.8" }
|
||||||
solana-faucet = { path = "../faucet", version = "0.23.0" }
|
solana-faucet = { path = "../faucet", version = "0.23.8" }
|
||||||
solana-librapay = { path = "../programs/librapay", version = "0.23.0", optional = true }
|
solana-librapay = { path = "../programs/librapay", version = "0.23.8", optional = true }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.23.0" }
|
solana-metrics = { path = "../metrics", version = "0.23.8" }
|
||||||
solana-measure = { path = "../measure", version = "0.23.0" }
|
solana-measure = { path = "../measure", version = "0.23.8" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.23.0" }
|
solana-runtime = { path = "../runtime", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.0", optional = true }
|
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.8", 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.23.0" }
|
solana-local-cluster = { path = "../local-cluster", version = "0.23.8" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
move = ["solana-librapay", "solana-move-loader-program"]
|
move = ["solana-librapay", "solana-move-loader-program"]
|
||||||
|
@ -7,7 +7,7 @@ use solana_faucet::faucet::request_airdrop_transaction;
|
|||||||
#[cfg(feature = "move")]
|
#[cfg(feature = "move")]
|
||||||
use solana_librapay::{create_genesis, upload_mint_script, upload_payment_script};
|
use solana_librapay::{create_genesis, upload_mint_script, upload_payment_script};
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_metrics::{self, datapoint_debug};
|
use solana_metrics::{self, datapoint_info};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
client::Client,
|
client::Client,
|
||||||
clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE},
|
clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE},
|
||||||
@ -15,7 +15,7 @@ use solana_sdk::{
|
|||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, KeypairUtil},
|
signature::{Keypair, Signer},
|
||||||
system_instruction, system_transaction,
|
system_instruction, system_transaction,
|
||||||
timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp},
|
timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
@ -244,7 +244,7 @@ where
|
|||||||
|
|
||||||
fn metrics_submit_lamport_balance(lamport_balance: u64) {
|
fn metrics_submit_lamport_balance(lamport_balance: u64) {
|
||||||
info!("Token balance: {}", lamport_balance);
|
info!("Token balance: {}", lamport_balance);
|
||||||
datapoint_debug!(
|
datapoint_info!(
|
||||||
"bench-tps-lamport_balance",
|
"bench-tps-lamport_balance",
|
||||||
("balance", lamport_balance, i64)
|
("balance", lamport_balance, i64)
|
||||||
);
|
);
|
||||||
@ -375,7 +375,7 @@ fn generate_txs(
|
|||||||
duration_as_ms(&duration),
|
duration_as_ms(&duration),
|
||||||
blockhash,
|
blockhash,
|
||||||
);
|
);
|
||||||
datapoint_debug!(
|
datapoint_info!(
|
||||||
"bench-tps-generate_txs",
|
"bench-tps-generate_txs",
|
||||||
("duration", duration_as_us(&duration), i64)
|
("duration", duration_as_us(&duration), i64)
|
||||||
);
|
);
|
||||||
@ -481,7 +481,7 @@ fn do_tx_transfers<T: Client>(
|
|||||||
duration_as_ms(&transfer_start.elapsed()),
|
duration_as_ms(&transfer_start.elapsed()),
|
||||||
tx_len as f32 / duration_as_s(&transfer_start.elapsed()),
|
tx_len as f32 / duration_as_s(&transfer_start.elapsed()),
|
||||||
);
|
);
|
||||||
datapoint_debug!(
|
datapoint_info!(
|
||||||
"bench-tps-do_tx_transfers",
|
"bench-tps-do_tx_transfers",
|
||||||
("duration", duration_as_us(&transfer_start.elapsed()), i64),
|
("duration", duration_as_us(&transfer_start.elapsed()), i64),
|
||||||
("count", tx_len, i64)
|
("count", tx_len, i64)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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::LAMPORTS_PER_SOL;
|
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;
|
||||||
|
@ -4,7 +4,7 @@ use solana_bench_tps::cli;
|
|||||||
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||||
use solana_genesis::Base64Account;
|
use solana_genesis::Base64Account;
|
||||||
use solana_sdk::fee_calculator::FeeCalculator;
|
use solana_sdk::fee_calculator::FeeCalculator;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use solana_sdk::system_program;
|
use solana_sdk::system_program;
|
||||||
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
|
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use solana_faucet::faucet::run_local_faucet;
|
|||||||
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
||||||
#[cfg(feature = "move")]
|
#[cfg(feature = "move")]
|
||||||
use solana_sdk::move_loader::solana_move_loader_program;
|
use solana_sdk::move_loader::solana_move_loader_program;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use std::sync::{mpsc::channel, Arc};
|
use std::sync::{mpsc::channel, Arc};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
* [Introduction](introduction.md)
|
* [Introduction](introduction.md)
|
||||||
* [Using Solana from the Command-line](cli/README.md)
|
* [Using Solana from the Command-line](cli/README.md)
|
||||||
* [Command-line Usage](cli/usage.md)
|
* [Command-line Usage](cli/usage.md)
|
||||||
|
* [Remote Wallet](remote-wallet/README.md)
|
||||||
|
* [Ledger Hardware Wallet](remote-wallet/ledger.md)
|
||||||
* [Paper Wallet](paper-wallet/README.md)
|
* [Paper Wallet](paper-wallet/README.md)
|
||||||
* [Installation](paper-wallet/installation.md)
|
* [Installation](paper-wallet/installation.md)
|
||||||
* [Paper Wallet Usage](paper-wallet/usage.md)
|
* [Paper Wallet Usage](paper-wallet/usage.md)
|
||||||
|
@ -303,6 +303,9 @@ The result field will be an object with the following fields:
|
|||||||
* `fee: <u64>` - fee this transaction was charged, as u64 integer
|
* `fee: <u64>` - fee this transaction was charged, as u64 integer
|
||||||
* `preBalances: <array>` - 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>` - 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:
|
||||||
|
|
||||||
@ -827,7 +830,7 @@ The result field will be a JSON object with the following fields:
|
|||||||
// Request
|
// Request
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"solana-core": "0.23.0"},"id":1}
|
{"jsonrpc":"2.0","result":{"solana-core": "0.23.8"},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getVoteAccounts
|
### getVoteAccounts
|
||||||
|
@ -154,7 +154,7 @@ The stream will output a series of JSON objects:
|
|||||||
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`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint testnet.solana.com:8001 --faucet testnet.solana.com:9900 --duration 60 --tx_count 50
|
$ NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint devnet.solana.com:8001 --faucet devnet.solana.com:9900 --duration 60 --tx_count 50
|
||||||
```
|
```
|
||||||
|
|
||||||
You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet)
|
You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet)
|
||||||
|
@ -22,12 +22,6 @@ $ solana airdrop 2
|
|||||||
|
|
||||||
// Return
|
// Return
|
||||||
"2.00000000 SOL"
|
"2.00000000 SOL"
|
||||||
|
|
||||||
// Command
|
|
||||||
$ solana airdrop 123 --lamports
|
|
||||||
|
|
||||||
// Return
|
|
||||||
"123 lamports"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get Balance
|
### Get Balance
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -94,12 +94,13 @@ 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\)
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ FLAGS:
|
|||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
-d, --data_dir <PATH> Directory to store install data [default: .../Library/Application Support/solana]
|
-d, --data_dir <PATH> Directory to store install data [default: .../Library/Application Support/solana]
|
||||||
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://testnet.solana.com:8899]
|
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://devnet.solana.com:8899]
|
||||||
-p, --pubkey <PUBKEY> Public key of the update manifest [default: 9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp]
|
-p, --pubkey <PUBKEY> Public key of the update manifest [default: 9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ At present, the following commands support offline signing:
|
|||||||
|
|
||||||
To sign a transaction offline, pass the following arguments on the command line
|
To sign a transaction offline, pass the following arguments on the command line
|
||||||
1) `--sign-only`, prevents the client from submitting the signed transaction
|
1) `--sign-only`, prevents the client from submitting the signed transaction
|
||||||
to the network. Instead, the pubkey/signature pairs are printed to stdout.
|
to the network. Instead, the pubkey/signature pairs are printed to stdout.
|
||||||
2) `--blockhash BASE58_HASH`, allows the caller to specify the value used to
|
2) `--blockhash BASE58_HASH`, allows the caller to specify the value used to
|
||||||
fill the transaction's `recent_blockhash` field. This serves a number of
|
fill the transaction's `recent_blockhash` field. This serves a number of
|
||||||
purposes, namely:
|
purposes, namely:
|
||||||
@ -37,7 +37,7 @@ Command
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana@offline$ solana pay --sign-only --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
|
solana@offline$ solana pay --sign-only --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
|
||||||
recipient-keypair.json 1 SOL
|
recipient-keypair.json 1
|
||||||
```
|
```
|
||||||
|
|
||||||
Output
|
Output
|
||||||
@ -67,7 +67,7 @@ Command
|
|||||||
```bash
|
```bash
|
||||||
solana@online$ solana pay --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
|
solana@online$ solana pay --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
|
||||||
--signer FhtzLVsmcV7S5XqGD79ErgoseCLhZYmEZnz9kQg1Rp7j=4vC38p4bz7XyiXrk6HtaooUqwxTWKocf45cstASGtmrD398biNJnmTcUCVEojE7wVQvgdYbjHJqRFZPpzfCQpmUN
|
--signer FhtzLVsmcV7S5XqGD79ErgoseCLhZYmEZnz9kQg1Rp7j=4vC38p4bz7XyiXrk6HtaooUqwxTWKocf45cstASGtmrD398biNJnmTcUCVEojE7wVQvgdYbjHJqRFZPpzfCQpmUN
|
||||||
recipient-keypair.json 1 SOL
|
recipient-keypair.json 1
|
||||||
```
|
```
|
||||||
|
|
||||||
Output
|
Output
|
||||||
|
@ -36,7 +36,7 @@ A nonce account is created by first generating a new keypair, then create the ac
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen new -o nonce-keypair.json
|
solana-keygen new -o nonce-keypair.json
|
||||||
solana create-nonce-account nonce-keypair.json 1 SOL
|
solana create-nonce-account nonce-keypair.json 1
|
||||||
```
|
```
|
||||||
|
|
||||||
- Output
|
- Output
|
||||||
@ -64,7 +64,7 @@ presently stored nonce value with
|
|||||||
- Command
|
- Command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana 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 nonce-account nonce-keypair.json
|
solana nonce-account nonce-keypair.json
|
||||||
```
|
```
|
||||||
|
|
||||||
- Output
|
- Output
|
||||||
@ -127,7 +127,7 @@ Withdraw funds from a nonce account with
|
|||||||
- Command
|
- Command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana withdraw-from-nonce-account nonce-keypair.json ~/.config/solana/id.json 0.5 SOL
|
solana withdraw-from-nonce-account nonce-keypair.json ~/.config/solana/id.json 0.5
|
||||||
```
|
```
|
||||||
|
|
||||||
- Output
|
- Output
|
||||||
@ -151,7 +151,7 @@ Reassign the authority of a nonce account after creation with
|
|||||||
- Command
|
- Command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana authorize-nonce-account nonce-keypair.json nonce-authority.json
|
solana authorize-nonce-account nonce-keypair.json nonce-authority.json
|
||||||
```
|
```
|
||||||
|
|
||||||
- Output
|
- Output
|
||||||
@ -197,7 +197,7 @@ Alice will need some funds to create a nonce account and send to Bob. Airdrop
|
|||||||
her some SOL
|
her some SOL
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ solana airdrop -k alice.json 10 SOL
|
$ solana airdrop -k alice.json 10
|
||||||
10 SOL
|
10 SOL
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ has full authority over the nonce account
|
|||||||
{% endhint %}
|
{% endhint %}
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ solana create-nonce-account -k alice.json nonce.json 1 SOL
|
$ solana create-nonce-account -k alice.json nonce.json 1
|
||||||
3KPZr96BTsL3hqera9up82KAU462Gz31xjqJ6eHUAjF935Yf8i1kmfEbo6SVbNaACKE5z6gySrNjVRvmS8DcPuwV
|
3KPZr96BTsL3hqera9up82KAU462Gz31xjqJ6eHUAjF935Yf8i1kmfEbo6SVbNaACKE5z6gySrNjVRvmS8DcPuwV
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ Alice attempts to pay Bob, but takes too long to sign. The specified blockhash
|
|||||||
expires and the transaction fails
|
expires and the transaction fails
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1 SOL
|
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1
|
||||||
[2020-01-02T18:48:28.462911000Z ERROR solana_cli::cli] Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
[2020-01-02T18:48:28.462911000Z ERROR solana_cli::cli] Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
||||||
Error: Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
Error: Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
||||||
```
|
```
|
||||||
@ -236,13 +236,13 @@ Remember, `alice.json` is the [nonce authority](#nonce-authority) in this exampl
|
|||||||
{% endhint %}
|
{% endhint %}
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ solana 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
|
||||||
```
|
```
|
||||||
```bash
|
```bash
|
||||||
$ solana pay -k alice.json --blockhash F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 --nonce nonce.json bob.json 1 SOL
|
$ solana pay -k alice.json --blockhash F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 --nonce nonce.json bob.json 1
|
||||||
HR1368UKHVZyenmH7yVz5sBAijV6XAPeWbEiXEGVYQorRMcoijeNAbzZqEZiH8cDB8tk65ckqeegFjK8dHwNFgQ
|
HR1368UKHVZyenmH7yVz5sBAijV6XAPeWbEiXEGVYQorRMcoijeNAbzZqEZiH8cDB8tk65ckqeegFjK8dHwNFgQ
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ $ solana balance -k bob.json
|
|||||||
1 SOL
|
1 SOL
|
||||||
```
|
```
|
||||||
```bash
|
```bash
|
||||||
$ solana 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
|
||||||
|
@ -90,6 +90,91 @@ For full usage details run:
|
|||||||
```bash
|
```bash
|
||||||
solana-keygen pubkey --help
|
solana-keygen pubkey --help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Verifying the Keypair
|
||||||
|
|
||||||
|
A keypair can be verified by following a variation on the
|
||||||
|
[offline signing](../offline-signing/README.md) procedure with a dummy transaction.
|
||||||
|
|
||||||
|
### Create and Sign a Dummy Transaction
|
||||||
|
|
||||||
|
Use offline signing to acquire the signature of a dummy transaction that can
|
||||||
|
be verified in the next step. A 0 Lamport [transfer](../cli/usage.md#solana-transfer)
|
||||||
|
is used to prevent inadvertent loss of funds. Additionally, an improbable _blockhash_
|
||||||
|
value is specified, as well as using the address of the _system program_ for the `TO`
|
||||||
|
argument, to ensure the transaction would be rejected by the _cluster_ should
|
||||||
|
it be submitted in error.
|
||||||
|
|
||||||
|
Command
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana transfer 11111111111111111111111111111111 0 --sign-only \
|
||||||
|
--ask-seed-phrase keypair --blockhash 11111111111111111111111111111111
|
||||||
|
```
|
||||||
|
|
||||||
|
Prompt for seed phrase
|
||||||
|
|
||||||
|
```text
|
||||||
|
[keypair] seed phrase:
|
||||||
|
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||||
|
Recovered pubkey `AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi`. Continue? (y/n): y
|
||||||
|
```
|
||||||
|
|
||||||
|
Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
Blockhash: 11111111111111111111111111111111
|
||||||
|
Signers (Pubkey=Signature):
|
||||||
|
AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA
|
||||||
|
|
||||||
|
{"blockhash":"11111111111111111111111111111111","signers":["AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA"]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify the Signature
|
||||||
|
|
||||||
|
Using the _Signers_ output from the [previous step](#create-and-sign-a-dummy-transaction)
|
||||||
|
to reconstruct the transaction, this time specifying the _pubkey_ and _signature_
|
||||||
|
as in the submission step of [offline signing](../offline-signing/README.md). That is, the `--from` and
|
||||||
|
`--fee-payer` are explicitly set to the _pubkey_ rather than being taken from
|
||||||
|
the keypair (which is not queried this time).
|
||||||
|
|
||||||
|
Command
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana transfer 11111111111111111111111111111111 0 --sign-only --from AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi \
|
||||||
|
--signer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA \
|
||||||
|
--blockhash 11111111111111111111111111111111 --fee-payer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi
|
||||||
|
```
|
||||||
|
|
||||||
|
Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
Blockhash: 11111111111111111111111111111111
|
||||||
|
Signers (Pubkey=Signature):
|
||||||
|
AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA
|
||||||
|
|
||||||
|
{"blockhash":"11111111111111111111111111111111","signers":["AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA"]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### An Example of Failure
|
||||||
|
|
||||||
|
To simulate an error the [verification step](#verify-the-signature) is repeated,
|
||||||
|
but with a corrupted _signature_ (the last letter is changed from "A" to "B").
|
||||||
|
|
||||||
|
Command
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana transfer 11111111111111111111111111111111 0 --sign-only --from AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi \
|
||||||
|
--signer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWB \
|
||||||
|
--blockhash 11111111111111111111111111111111 --fee-payer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi
|
||||||
|
```
|
||||||
|
|
||||||
|
Output (Error)
|
||||||
|
|
||||||
|
```text
|
||||||
|
Error: BadParameter("Transaction construction failed, incorrect signature or public key provided")
|
||||||
|
```
|
||||||
|
|
||||||
## Checking Account Balance
|
## Checking Account Balance
|
||||||
|
|
||||||
All that is needed to check an account balance is the public key of an account.
|
All that is needed to check an account balance is the public key of an account.
|
||||||
@ -102,7 +187,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 config set --url <CLUSTER URL> # (i.e. http://testnet.solana.com:8899)
|
solana config set --url <CLUSTER URL> # (i.e. http://devnet.solana.com:8899)
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, to check the balance, run the following command:
|
Finally, to check the balance, run the following command:
|
||||||
@ -162,10 +247,10 @@ Refer to the following page for a comprehensive guide on running a validator:
|
|||||||
Solana CLI tooling supports secure keypair input for stake delegation. To do so,
|
Solana CLI tooling supports secure keypair input for stake delegation. To do so,
|
||||||
first create a stake account with some SOL. Use the special `ASK` keyword to
|
first create a stake account with some SOL. Use the special `ASK` keyword to
|
||||||
trigger a seed phrase input prompt for the stake account and use
|
trigger a seed phrase input prompt for the stake account and use
|
||||||
`--ask-seed-phrase keypair` to securely input the funding keypair.
|
`--keypair ASK` to securely input the funding keypair.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana create-stake-account ASK 1 SOL --ask-seed-phrase keypair
|
solana create-stake-account ASK 1 --keypair ASK
|
||||||
|
|
||||||
[stake_account] seed phrase: 🔒
|
[stake_account] seed phrase: 🔒
|
||||||
[stake_account] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
[stake_account] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||||
@ -173,11 +258,11 @@ solana create-stake-account ASK 1 SOL --ask-seed-phrase keypair
|
|||||||
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, to delegate that stake to a validator, use `--ask-seed-phrase keypair` to
|
Then, to delegate that stake to a validator, use `--keypair ASK` to
|
||||||
securely input the funding keypair.
|
securely input the funding keypair.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana delegate-stake --ask-seed-phrase keypair <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>
|
solana delegate-stake --keypair ASK <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>
|
||||||
|
|
||||||
[keypair] seed phrase: 🔒
|
[keypair] seed phrase: 🔒
|
||||||
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||||
|
@ -22,7 +22,7 @@ Each CTF test starts with an opaque entry point and a funded keypair. The test s
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
use crate::contact_info::ContactInfo;
|
use crate::contact_info::ContactInfo;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
pub fn test_this_behavior(
|
pub fn test_this_behavior(
|
||||||
entry_point_info: &ContactInfo,
|
entry_point_info: &ContactInfo,
|
||||||
funding_keypair: &Keypair,
|
funding_keypair: &Keypair,
|
||||||
|
12
book/src/remote-wallet/README.md
Normal file
12
book/src/remote-wallet/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Remote Wallet
|
||||||
|
|
||||||
|
This document describes how to use a remote wallet with the Solana CLI
|
||||||
|
tools. Currently, Solana supports:
|
||||||
|
- Ledger hardware wallet (Nano S)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Solana's remote-wallet integration provides Solana CLI methods to query a
|
||||||
|
device's BIP32 public keys and send messages to the device for secure signing.
|
||||||
|
|
||||||
|
{% page-ref page="ledger.md" %}
|
132
book/src/remote-wallet/ledger.md
Normal file
132
book/src/remote-wallet/ledger.md
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# Ledger Hardware Wallet
|
||||||
|
|
||||||
|
The Ledger Nano S hardware wallet offers secure storage of your Solana private
|
||||||
|
keys. The Solana Ledger app enables derivation of essentially infinite keys, and
|
||||||
|
secure transaction signing.
|
||||||
|
|
||||||
|
## Before You Begin
|
||||||
|
|
||||||
|
- [Initialize your Ledger Nano S](https://support.ledger.com/hc/en-us/articles/360000613793)
|
||||||
|
- [Install the latest device firmware](https://support.ledgerwallet.com/hc/en-us/articles/360002731113-Update-Ledger-Nano-S-firmware)
|
||||||
|
- [Install Ledger Live](https://support.ledger.com/hc/en-us/articles/360006395553/) software on your computer
|
||||||
|
|
||||||
|
## Install the Solana App on Ledger Nano S
|
||||||
|
|
||||||
|
1. Open the Manager in Ledger Live
|
||||||
|
2. Connect and unlock your Ledger Nano S
|
||||||
|
3. If asked, allow the manager on your device by pressing the right button
|
||||||
|
4. Find Solana in the app catalog and click Install
|
||||||
|
5. An installation window appears and your device will display Processing…
|
||||||
|
6. The app installation is confirmed
|
||||||
|
|
||||||
|
## Use Ledger Device with Solana CLI
|
||||||
|
|
||||||
|
1. Plug your Ledger device into your computer's USB port
|
||||||
|
2. Enter your pin and start the Solana app on the Ledger device
|
||||||
|
3. On your computer, run:
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana address --keypair usb://ledger
|
||||||
|
```
|
||||||
|
|
||||||
|
This confirms your Ledger device is connected properly and in the correct state
|
||||||
|
to interact with the Solana CLI. The command returns your Ledger's unique
|
||||||
|
*wallet key*. When you have multiple Nano S devices connected to the same
|
||||||
|
computer, you can use your wallet key to specify which Ledger hardware wallet
|
||||||
|
you want to use. Run the same command again, but this time, with its fully
|
||||||
|
qualified URL:
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana address --keypair usb://ledger/nano-s/<WALLET_KEY>
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm it prints the same key as when you entered just `usb://ledger`.
|
||||||
|
|
||||||
|
### Ledger Device URLs
|
||||||
|
|
||||||
|
Solana defines a format for the URL protocol "usb://" to uniquely locate any Solana key on
|
||||||
|
any remote wallet connected to your computer.
|
||||||
|
|
||||||
|
The URL has the form, where square brackets denote optional fields:
|
||||||
|
|
||||||
|
```text
|
||||||
|
usb://ledger[/<LEDGER_TYPE>[/<WALLET_KEY>]][?key=<DERIVATION_PATH>]
|
||||||
|
```
|
||||||
|
|
||||||
|
`LEDGER_TYPE` is optional and defaults to the value "nano-s". If the value is provided,
|
||||||
|
it must be "nano-s" without quotes, the only supported Ledger device at this time.
|
||||||
|
|
||||||
|
`WALLET_KEY` is used to disambiguate multiple Nano S devices. Every Ledger has
|
||||||
|
a unique master key and from that key derives a separate unique key per app.
|
||||||
|
|
||||||
|
`DERVIATION_PATH` is used to navigate to Solana keys within your Ledger hardware
|
||||||
|
wallet. The path has the form `<ACCOUNT>[/<CHANGE>]`, where each `ACCOUNT` and
|
||||||
|
`CHANGE` are postive integers.
|
||||||
|
|
||||||
|
All derivation paths implicitly include the prefix `44'/501'`, which indicates
|
||||||
|
the path follows the [BIP44 specifications](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
|
||||||
|
and that any dervied keys are Solana keys (Coin type 501). The single quote
|
||||||
|
indicates a "hardened" derivation. Because Solana uses Ed25519 keypairs, all
|
||||||
|
derivations are hardened and therefore adding the quote is optional and
|
||||||
|
unnecessary.
|
||||||
|
|
||||||
|
For example, a complete Ledger device path might be:
|
||||||
|
|
||||||
|
```text
|
||||||
|
usb://ledger/nano-s/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Set CLI Configuration
|
||||||
|
|
||||||
|
If you want to set a Ledger key as the default signer for CLI commands, use the
|
||||||
|
[CLI configuration settings](../cli/usage.md#solana-config):
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana config set --keypair <LEDGER_URL>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana config set --keypair usb://ledger?key=0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Account Balance
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana balance --keypair usb://ledger?key=12345
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with the default signer:
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana balance
|
||||||
|
```
|
||||||
|
|
||||||
|
### Send SOL via Ledger Device
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana transfer <RECIPIENT> <AMOUNT> --from <LEDGER_URL>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with the default signer:
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana transfer <RECIPIENT> <AMOUNT>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delegate Stake with Ledger Device
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana delegate-stake <STAKE_ACCOUNT> <VOTER_ID> --keypair <LEDGER_URL>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with the default signer:
|
||||||
|
|
||||||
|
```text
|
||||||
|
solana delegate-stake <STAKE_ACCOUNT> <VOTER_ID>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Email maintainers@solana.com
|
@ -8,9 +8,9 @@ Please note some of the information and instructions described here may change i
|
|||||||
|
|
||||||
Archivers are specialized light clients. They download a part of the ledger \(a.k.a Segment\) and store it. They earn rewards for storing segments.
|
Archivers are specialized light clients. They download a part of the ledger \(a.k.a Segment\) and store it. They earn rewards for storing segments.
|
||||||
|
|
||||||
The testnet features a validator running at testnet.solana.com, which serves as the entrypoint to the cluster for your archiver node.
|
The testnet features a validator running at devnet.solana.com, which serves as the entrypoint to the cluster for your archiver node.
|
||||||
|
|
||||||
Additionally there is a blockexplorer available at [http://testnet.solana.com/](http://testnet.solana.com/).
|
Additionally there is a blockexplorer available at [http://devnet.solana.com/](http://devnet.solana.com/).
|
||||||
|
|
||||||
The testnet is configured to reset the ledger daily, or sooner should the hourly automated cluster sanity test fail.
|
The testnet is configured to reset the ledger daily, or sooner should the hourly automated cluster sanity test fail.
|
||||||
|
|
||||||
@ -29,10 +29,10 @@ Before starting an archiver node, sanity check that the cluster is accessible to
|
|||||||
Fetch the current transaction count over JSON RPC:
|
Fetch the current transaction count over JSON RPC:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://testnet.solana.com:8899
|
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://devnet.solana.com:8899
|
||||||
```
|
```
|
||||||
|
|
||||||
Inspect the blockexplorer at [http://testnet.solana.com/](http://testnet.solana.com/) for activity.
|
Inspect the blockexplorer at [http://devnet.solana.com/](http://devnet.solana.com/) for activity.
|
||||||
|
|
||||||
View the [metrics dashboard](https://metrics.solana.com:3000/d/testnet-beta/testnet-monitor-beta?var-testnet=testnet) for more detail on cluster activity.
|
View the [metrics dashboard](https://metrics.solana.com:3000/d/testnet-beta/testnet-monitor-beta?var-testnet=testnet) for more detail on cluster activity.
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ Download the binaries by navigating to [https://github.com/solana-labs/solana/re
|
|||||||
Try running following command to join the gossip network and view all the other nodes in the cluster:
|
Try running following command to join the gossip network and view all the other nodes in the cluster:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||||
# Press ^C to exit
|
# Press ^C to exit
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ Use solana-keygen to show the public keys for each of the keypairs, they will be
|
|||||||
```text
|
```text
|
||||||
Then set up the storage accounts for your archiver by running:
|
Then set up the storage accounts for your archiver by running:
|
||||||
```bash
|
```bash
|
||||||
solana --keypair archiver-keypair.json airdrop 100000 lamports
|
solana --keypair archiver-keypair.json airdrop .0001
|
||||||
solana --keypair archiver-keypair.json create-archiver-storage-account $ARCHIVER_IDENTITY $STORAGE_IDENTITY
|
solana --keypair archiver-keypair.json create-archiver-storage-account $ARCHIVER_IDENTITY $STORAGE_IDENTITY
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ Note: Every time the testnet restarts, run the steps to setup the archiver accou
|
|||||||
To start the archiver:
|
To start the archiver:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-archiver --entrypoint testnet.solana.com:8001 --identity-keypair archiver-keypair.json --storage-keypair storage-keypair.json --ledger archiver-ledger
|
solana-archiver --entrypoint devnet.solana.com:8001 --identity-keypair archiver-keypair.json --storage-keypair storage-keypair.json --ledger archiver-ledger
|
||||||
```
|
```
|
||||||
|
|
||||||
## Verify Archiver Setup
|
## Verify Archiver Setup
|
||||||
@ -146,7 +146,7 @@ solana-archiver --entrypoint testnet.solana.com:8001 --identity-keypair archiver
|
|||||||
From another console, confirm the IP address and **identity pubkey** of your archiver is visible in the gossip network by running:
|
From another console, confirm the IP address and **identity pubkey** of your archiver is visible in the gossip network by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||||
```
|
```
|
||||||
|
|
||||||
Provide the **storage account pubkey** to the `solana 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:
|
||||||
|
@ -13,9 +13,7 @@ serve as the entrypoint to the cluster for your validator.
|
|||||||
|
|
||||||
Current testnet entrypoints:
|
Current testnet entrypoints:
|
||||||
|
|
||||||
* Stable, testnet.solana.com
|
* Developer testnet, devnet.solana.com
|
||||||
* Beta, beta.testnet.solana.com
|
|
||||||
* Edge, edge.testnet.solana.com
|
|
||||||
|
|
||||||
Solana may launch special testnets for validator participation; we will provide
|
Solana may launch special testnets for validator participation; we will provide
|
||||||
you with a specific entrypoint URL to use.
|
you with a specific entrypoint URL to use.
|
||||||
|
@ -6,7 +6,7 @@ Confirm the IP address and **identity pubkey** of your validator is visible in
|
|||||||
the gossip network by running:
|
the gossip network by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||||
```
|
```
|
||||||
|
|
||||||
## Check Your Balance
|
## Check Your Balance
|
||||||
@ -35,13 +35,13 @@ cluster, as well as the health of the cluster:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Similar to solana-gossip, you should see your validator in the list of cluster nodes
|
# Similar to solana-gossip, you should see your validator in the list of cluster nodes
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://testnet.solana.com:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://devnet.solana.com:8899
|
||||||
# If your validator is properly voting, it should appear in the list of `current` vote accounts. If staked, `stake` should be > 0
|
# If your validator is properly voting, it should appear in the list of `current` vote accounts. If staked, `stake` should be > 0
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://testnet.solana.com:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://devnet.solana.com:8899
|
||||||
# Returns the current leader schedule
|
# Returns the current leader schedule
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://testnet.solana.com:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://devnet.solana.com:8899
|
||||||
# Returns info about the current epoch. slotIndex should progress on subsequent calls.
|
# Returns info about the current epoch. slotIndex should progress on subsequent calls.
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://testnet.solana.com:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://devnet.solana.com:8899
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ solana-keygen new -o ~/validator-stake-keypair.json
|
|||||||
Now delegate 1 SOL to your validator by first creating your stake account:
|
Now delegate 1 SOL to your validator by first creating your stake account:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana create-stake-account ~/validator-stake-keypair.json 1 SOL
|
solana create-stake-account ~/validator-stake-keypair.json 1
|
||||||
```
|
```
|
||||||
|
|
||||||
and then delegating that stake to your validator:
|
and then delegating that stake to your validator:
|
||||||
@ -83,7 +83,6 @@ To monitor your validator during its warmup period:
|
|||||||
|
|
||||||
* 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 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 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 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 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 ####`
|
||||||
|
@ -6,11 +6,11 @@ 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 config set --url http://testnet.solana.com:8899
|
solana config set --url http://devnet.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
|
||||||
`--url` argument with a command, eg: `solana --url http://beta.testnet.solana.com:8899 balance`\)
|
`--url` argument with a command, eg: `solana --url http://beta.devnet.solana.com:8899 balance`\)
|
||||||
|
|
||||||
## Confirm The Testnet Is Reachable
|
## Confirm The Testnet Is Reachable
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ Try running following command to join the gossip network and view all the other
|
|||||||
nodes in the cluster:
|
nodes in the cluster:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||||
# Press ^C to exit
|
# Press ^C to exit
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ You should see the following output:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
Wallet Config Updated: /home/solana/.config/solana/wallet/config.yml
|
Wallet Config Updated: /home/solana/.config/solana/wallet/config.yml
|
||||||
* url: http://testnet.solana.com:8899
|
* url: http://devnet.solana.com:8899
|
||||||
* keypair: /home/solana/validator-keypair.json
|
* keypair: /home/solana/validator-keypair.json
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ Connect to a testnet cluster by running:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-validator --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json \
|
solana-validator --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json \
|
||||||
--ledger ~/validator-ledger --rpc-port 8899 --entrypoint testnet.solana.com:8001 \
|
--ledger ~/validator-ledger --rpc-port 8899 --entrypoint devnet.solana.com:8001 \
|
||||||
--limit-ledger-size
|
--limit-ledger-size
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ Confirm your validator connected to the network by opening a new terminal and
|
|||||||
running:
|
running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||||
```
|
```
|
||||||
|
|
||||||
If your validator is connected, its public key and IP address will appear in the list.
|
If your validator is connected, its public key and IP address will appear in the list.
|
||||||
|
@ -5,8 +5,7 @@ that serves as an entrypoint to the cluster.
|
|||||||
|
|
||||||
Current testnet entrypoints:
|
Current testnet entrypoints:
|
||||||
|
|
||||||
* Stable: testnet.solana.com
|
* Stable: devnet.solana.com
|
||||||
* Beta: beta.testnet.solana.com
|
|
||||||
|
|
||||||
Application developers should target the Stable testnet. Key differences
|
Application developers should target the Stable testnet. Key differences
|
||||||
between the Stable testnet and what will be mainnet:
|
between the Stable testnet and what will be mainnet:
|
||||||
@ -28,13 +27,13 @@ You can submit a JSON-RPC request to see the specific software version of the
|
|||||||
cluster. Use this to specify [the software version to install](validator-software.md).
|
cluster. Use this to specify [the software version to install](validator-software.md).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' testnet.solana.com:8899
|
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' devnet.solana.com:8899
|
||||||
```
|
```
|
||||||
Example result:
|
Example result:
|
||||||
`{"jsonrpc":"2.0","result":{"solana-core":"0.21.0"},"id":1}`
|
`{"jsonrpc":"2.0","result":{"solana-core":"0.21.0"},"id":1}`
|
||||||
|
|
||||||
## Using a Different Testnet
|
## Using a Different Testnet
|
||||||
|
|
||||||
This guide is written in the context of testnet.solana.com, our most stable
|
This guide is written in the context of devnet.solana.com, our most stable
|
||||||
cluster. To participate in another testnet, modify the commands in the following
|
cluster. To participate in another testnet, modify the commands in the following
|
||||||
pages, replacing `testnet.solana.com` with your desired testnet.
|
pages, replacing `devnet.solana.com` with your desired testnet.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-chacha-cuda"
|
name = "solana-chacha-cuda"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
description = "Solana Chacha Cuda APIs"
|
description = "Solana Chacha Cuda APIs"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@ -10,12 +10,12 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.0" }
|
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.8" }
|
||||||
solana-chacha = { path = "../chacha", version = "0.23.0" }
|
solana-chacha = { path = "../chacha", version = "0.23.8" }
|
||||||
solana-ledger = { path = "../ledger", version = "0.23.0" }
|
solana-ledger = { path = "../ledger", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-perf = { path = "../perf", version = "0.23.0" }
|
solana-perf = { path = "../perf", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.2.1"
|
hex-literal = "0.2.1"
|
||||||
|
@ -118,7 +118,7 @@ mod tests {
|
|||||||
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;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-chacha-sys"
|
name = "solana-chacha-sys"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
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"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-chacha"
|
name = "solana-chacha"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
description = "Solana Chacha APIs"
|
description = "Solana Chacha APIs"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@ -12,11 +12,11 @@ edition = "2018"
|
|||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
rand_chacha = "0.1.1"
|
rand_chacha = "0.1.1"
|
||||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.0" }
|
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.8" }
|
||||||
solana-ledger = { path = "../ledger", version = "0.23.0" }
|
solana-ledger = { path = "../ledger", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-perf = { path = "../perf", version = "0.23.0" }
|
solana-perf = { path = "../perf", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.2.1"
|
hex-literal = "0.2.1"
|
||||||
|
@ -81,7 +81,7 @@ mod tests {
|
|||||||
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::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
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;
|
||||||
|
@ -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.1
|
||||||
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.1
|
||||||
|
|
||||||
# Add Google Protocol Buffers for Libra's metrics library.
|
# Add Google Protocol Buffers for Libra's metrics library.
|
||||||
ENV PROTOC_VERSION 3.8.0
|
ENV PROTOC_VERSION 3.8.0
|
||||||
@ -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 \
|
||||||
|
@ -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.1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||||
else
|
else
|
||||||
nightly_version=2019-12-19
|
nightly_version=2020-02-27
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
@ -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}"
|
||||||
|
@ -234,7 +234,7 @@ sanity() {
|
|||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
NO_INSTALL_CHECK=1 \
|
NO_INSTALL_CHECK=1 \
|
||||||
ci/testnet-sanity.sh edge-testnet-solana-com gce -P us-west1-b
|
ci/testnet-sanity.sh edge-devnet-solana-com gce -P us-west1-b
|
||||||
maybe_deploy_software
|
maybe_deploy_software
|
||||||
)
|
)
|
||||||
;;
|
;;
|
||||||
@ -249,7 +249,7 @@ sanity() {
|
|||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
NO_INSTALL_CHECK=1 \
|
NO_INSTALL_CHECK=1 \
|
||||||
ci/testnet-sanity.sh beta-testnet-solana-com gce -P us-west1-b
|
ci/testnet-sanity.sh beta-devnet-solana-com gce -P us-west1-b
|
||||||
maybe_deploy_software --deploy-if-newer
|
maybe_deploy_software --deploy-if-newer
|
||||||
)
|
)
|
||||||
;;
|
;;
|
||||||
@ -263,7 +263,7 @@ sanity() {
|
|||||||
testnet)
|
testnet)
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
ci/testnet-sanity.sh testnet-solana-com gce -P us-west1-b
|
ci/testnet-sanity.sh devnet-solana-com gce -P us-west1-b
|
||||||
)
|
)
|
||||||
;;
|
;;
|
||||||
testnet-perf)
|
testnet-perf)
|
||||||
@ -327,9 +327,9 @@ deploy() {
|
|||||||
testnet-edge)
|
testnet-edge)
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
ci/testnet-deploy.sh -p edge-testnet-solana-com -C gce -z us-west1-b \
|
ci/testnet-deploy.sh -p edge-devnet-solana-com -C gce -z us-west1-b \
|
||||||
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
|
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
|
||||||
-a edge-testnet-solana-com --letsencrypt edge.testnet.solana.com \
|
-a edge-devnet-solana-com --letsencrypt edge.devnet.solana.com \
|
||||||
--limit-ledger-size \
|
--limit-ledger-size \
|
||||||
${skipCreate:+-e} \
|
${skipCreate:+-e} \
|
||||||
${skipStart:+-s} \
|
${skipStart:+-s} \
|
||||||
@ -352,9 +352,9 @@ deploy() {
|
|||||||
testnet-beta)
|
testnet-beta)
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
ci/testnet-deploy.sh -p beta-testnet-solana-com -C gce -z us-west1-b \
|
ci/testnet-deploy.sh -p beta-devnet-solana-com -C gce -z us-west1-b \
|
||||||
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
|
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
|
||||||
-a beta-testnet-solana-com --letsencrypt beta.testnet.solana.com \
|
-a beta-devnet-solana-com --letsencrypt beta.devnet.solana.com \
|
||||||
--limit-ledger-size \
|
--limit-ledger-size \
|
||||||
${skipCreate:+-e} \
|
${skipCreate:+-e} \
|
||||||
${skipStart:+-s} \
|
${skipStart:+-s} \
|
||||||
@ -377,9 +377,9 @@ deploy() {
|
|||||||
testnet)
|
testnet)
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
ci/testnet-deploy.sh -p testnet-solana-com -C gce -z us-west1-b \
|
ci/testnet-deploy.sh -p devnet-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 devnet.solana.com \
|
||||||
--limit-ledger-size \
|
--limit-ledger-size \
|
||||||
${skipCreate:+-e} \
|
${skipCreate:+-e} \
|
||||||
${skipStart:+-s} \
|
${skipStart:+-s} \
|
||||||
@ -389,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)
|
||||||
@ -455,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
|
||||||
@ -540,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.23.0"
|
version = "0.23.8"
|
||||||
description = "Solana utilities for the clap"
|
description = "Solana utilities for the clap"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@ -11,8 +11,8 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
rpassword = "4.0"
|
rpassword = "4.0"
|
||||||
semver = "0.9.0"
|
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
tiny-bip39 = "0.7.0"
|
tiny-bip39 = "0.7.0"
|
||||||
url = "2.1.0"
|
url = "2.1.0"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
use crate::keypair::{keypair_from_seed_phrase, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG};
|
use crate::keypair::{
|
||||||
|
keypair_from_seed_phrase, signer_from_path, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||||
|
};
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use solana_remote_wallet::remote_wallet::{DerivationPath, RemoteWalletManager};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
clock::UnixTimestamp,
|
clock::UnixTimestamp,
|
||||||
native_token::sol_to_lamports,
|
native_token::sol_to_lamports,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{read_keypair_file, Keypair, KeypairUtil, Signature},
|
signature::{read_keypair_file, Keypair, Signature, Signer},
|
||||||
};
|
};
|
||||||
use std::str::FromStr;
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
|
||||||
// Return parsed values from matches at `name`
|
// Return parsed values from matches at `name`
|
||||||
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
|
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
|
||||||
@ -92,14 +95,36 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn amount_of(matches: &ArgMatches<'_>, name: &str, unit: &str) -> Option<u64> {
|
// Return a signer from matches at `name`
|
||||||
if matches.value_of(unit) == Some("lamports") {
|
#[allow(clippy::type_complexity)]
|
||||||
value_of(matches, name)
|
pub fn signer_of(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
name: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<(Option<Box<dyn Signer>>, Option<Pubkey>), Box<dyn std::error::Error>> {
|
||||||
|
if let Some(location) = matches.value_of(name) {
|
||||||
|
let signer = signer_from_path(matches, location, name, wallet_manager)?;
|
||||||
|
let signer_pubkey = signer.pubkey();
|
||||||
|
Ok((Some(signer), Some(signer_pubkey)))
|
||||||
} else {
|
} else {
|
||||||
value_of(matches, name).map(sol_to_lamports)
|
Ok((None, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
|
||||||
|
value_of(matches, name).map(sol_to_lamports)
|
||||||
|
}
|
||||||
|
|
||||||
|
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().map(|account| account.parse::<u32>().unwrap());
|
||||||
|
let change = parts.next().map(|change| change.parse::<u32>().unwrap());
|
||||||
|
DerivationPath { account, change }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -258,23 +283,56 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_amount_of() {
|
fn test_lamports_of_sol() {
|
||||||
let matches = app()
|
let matches = app()
|
||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "--single", "50", "--unit", "lamports"]);
|
.get_matches_from(vec!["test", "--single", "50"]);
|
||||||
assert_eq!(amount_of(&matches, "single", "unit"), Some(50));
|
assert_eq!(lamports_of_sol(&matches, "single"), Some(50000000000));
|
||||||
assert_eq!(amount_of(&matches, "multiple", "unit"), None);
|
assert_eq!(lamports_of_sol(&matches, "multiple"), None);
|
||||||
let matches = app()
|
let matches = app()
|
||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "--single", "50", "--unit", "SOL"]);
|
.get_matches_from(vec!["test", "--single", "1.5"]);
|
||||||
assert_eq!(amount_of(&matches, "single", "unit"), Some(50000000000));
|
assert_eq!(lamports_of_sol(&matches, "single"), Some(1500000000));
|
||||||
|
assert_eq!(lamports_of_sol(&matches, "multiple"), None);
|
||||||
let matches = app()
|
let matches = app()
|
||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "SOL"]);
|
.get_matches_from(vec!["test", "--single", "0.03"]);
|
||||||
assert_eq!(amount_of(&matches, "single", "unit"), Some(1500000000));
|
assert_eq!(lamports_of_sol(&matches, "single"), Some(30000000));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_derivation_of() {
|
||||||
let matches = app()
|
let matches = app()
|
||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "lamports"]);
|
.get_matches_from(vec!["test", "--single", "2/3"]);
|
||||||
assert_eq!(amount_of(&matches, "single", "unit"), None);
|
assert_eq!(
|
||||||
|
derivation_of(&matches, "single"),
|
||||||
|
Some(DerivationPath {
|
||||||
|
account: Some(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: Some(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: Some(2),
|
||||||
|
change: Some(3)
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use crate::keypair::ASK_KEYWORD;
|
use crate::keypair::{parse_keypair_path, KeypairUrl, 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.
|
||||||
@ -48,6 +50,13 @@ pub fn is_pubkey_or_keypair_or_ask_keyword(string: String) -> Result<(), String>
|
|||||||
is_pubkey(string.clone()).or_else(|_| is_keypair_or_ask_keyword(string))
|
is_pubkey(string.clone()).or_else(|_| is_keypair_or_ask_keyword(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_valid_signer(string: String) -> Result<(), String> {
|
||||||
|
match parse_keypair_path(&string) {
|
||||||
|
KeypairUrl::Filepath(path) => is_keypair(path),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return an error if string cannot be parsed as pubkey=signature string
|
// Return an error if string cannot be parsed as pubkey=signature string
|
||||||
pub fn is_pubkey_sig(string: String) -> Result<(), String> {
|
pub fn is_pubkey_sig(string: String) -> Result<(), String> {
|
||||||
let mut signer = string.split('=');
|
let mut signer = string.split('=');
|
||||||
@ -84,20 +93,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 +136,48 @@ 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::<u32>()
|
||||||
|
.map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"Unable to parse derivation, provided: {}, err: {:?}",
|
||||||
|
account, e
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and_then(|_| {
|
||||||
|
if let Some(change) = parts.next() {
|
||||||
|
change.parse::<u32>().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("65537".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("4294967296".to_string()).is_err());
|
||||||
|
assert!(is_derivation("a/b".to_string()).is_err());
|
||||||
|
assert!(is_derivation("0/4294967296".to_string()).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,20 +1,114 @@
|
|||||||
use crate::ArgConstant;
|
use crate::{
|
||||||
|
input_parsers::{derivation_of, pubkeys_sigs_of},
|
||||||
|
offline::SIGNER_ARG,
|
||||||
|
ArgConstant,
|
||||||
|
};
|
||||||
use bip39::{Language, Mnemonic, Seed};
|
use bip39::{Language, Mnemonic, Seed};
|
||||||
use clap::values_t;
|
use clap::{values_t, ArgMatches, Error, ErrorKind};
|
||||||
use rpassword::prompt_password_stderr;
|
use rpassword::prompt_password_stderr;
|
||||||
|
use solana_remote_wallet::{
|
||||||
|
remote_keypair::generate_remote_keypair,
|
||||||
|
remote_wallet::{RemoteWalletError, RemoteWalletManager},
|
||||||
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{
|
signature::{
|
||||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
|
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
|
||||||
KeypairUtil,
|
read_keypair_file, Keypair, Presigner, Signature, Signer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
error,
|
error,
|
||||||
io::{stdin, stdout, Write},
|
io::{stdin, stdout, Write},
|
||||||
process::exit,
|
process::exit,
|
||||||
|
str::FromStr,
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub enum KeypairUrl {
|
||||||
|
Ask,
|
||||||
|
Filepath(String),
|
||||||
|
Usb(String),
|
||||||
|
Stdin,
|
||||||
|
Pubkey(Pubkey),
|
||||||
|
}
|
||||||
|
|
||||||
|
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.to_string())
|
||||||
|
} else if let Ok(pubkey) = Pubkey::from_str(path) {
|
||||||
|
KeypairUrl::Pubkey(pubkey)
|
||||||
|
} else {
|
||||||
|
KeypairUrl::Filepath(path.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn presigner_from_pubkey_sigs(
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
signers: &[(Pubkey, Signature)],
|
||||||
|
) -> Option<Presigner> {
|
||||||
|
signers.iter().find_map(|(signer, sig)| {
|
||||||
|
if *signer == *pubkey {
|
||||||
|
Some(Presigner::new(signer, sig))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signer_from_path(
|
||||||
|
matches: &ArgMatches,
|
||||||
|
path: &str,
|
||||||
|
keypair_name: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
|
||||||
|
match parse_keypair_path(path) {
|
||||||
|
KeypairUrl::Ask => {
|
||||||
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
|
Ok(Box::new(keypair_from_seed_phrase(
|
||||||
|
keypair_name,
|
||||||
|
skip_validation,
|
||||||
|
false,
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
KeypairUrl::Filepath(path) => Ok(Box::new(read_keypair_file(&path)?)),
|
||||||
|
KeypairUrl::Stdin => {
|
||||||
|
let mut stdin = std::io::stdin();
|
||||||
|
Ok(Box::new(read_keypair(&mut stdin)?))
|
||||||
|
}
|
||||||
|
KeypairUrl::Usb(path) => {
|
||||||
|
if let Some(wallet_manager) = wallet_manager {
|
||||||
|
Ok(Box::new(generate_remote_keypair(
|
||||||
|
path,
|
||||||
|
derivation_of(matches, "derivation_path"),
|
||||||
|
wallet_manager,
|
||||||
|
matches.is_present("confirm_key"),
|
||||||
|
)?))
|
||||||
|
} else {
|
||||||
|
Err(RemoteWalletError::NoDeviceFound.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeypairUrl::Pubkey(pubkey) => {
|
||||||
|
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
|
||||||
|
if let Some(presigner) = presigner {
|
||||||
|
Ok(Box::new(presigner))
|
||||||
|
} else {
|
||||||
|
Err(Error::with_description(
|
||||||
|
"Missing signature for supplied pubkey",
|
||||||
|
ErrorKind::MissingRequiredArgument,
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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";
|
||||||
|
|
||||||
@ -32,8 +126,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,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +181,7 @@ pub fn keypair_from_seed_phrase(
|
|||||||
};
|
};
|
||||||
|
|
||||||
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 +225,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))
|
||||||
}
|
}
|
||||||
|
@ -26,3 +26,4 @@ pub struct ArgConstant<'a> {
|
|||||||
pub mod input_parsers;
|
pub mod input_parsers;
|
||||||
pub mod input_validators;
|
pub mod input_validators;
|
||||||
pub mod keypair;
|
pub mod keypair;
|
||||||
|
pub mod offline;
|
||||||
|
19
clap-utils/src/offline.rs
Normal file
19
clap-utils/src/offline.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use crate::ArgConstant;
|
||||||
|
|
||||||
|
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
|
name: "blockhash",
|
||||||
|
long: "blockhash",
|
||||||
|
help: "Use the supplied blockhash",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SIGN_ONLY_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
|
name: "sign_only",
|
||||||
|
long: "sign-only",
|
||||||
|
help: "Sign the transaction offline",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
|
name: "signer",
|
||||||
|
long: "signer",
|
||||||
|
help: "Provide a public-key/signature pair for the transaction",
|
||||||
|
};
|
16
cli-config/Cargo.toml
Normal file
16
cli-config/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
name = "solana-cli-config"
|
||||||
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
|
version = "0.23.8"
|
||||||
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
homepage = "https://solana.com/"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dirs = "2.0.2"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
serde = "1.0.104"
|
||||||
|
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;
|
@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli"
|
name = "solana-cli"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
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/"
|
||||||
@ -17,7 +17,6 @@ criterion-stats = "0.3.0"
|
|||||||
ctrlc = { version = "3.1.3", features = ["termination"] }
|
ctrlc = { version = "3.1.3", features = ["termination"] }
|
||||||
console = "0.9.1"
|
console = "0.9.1"
|
||||||
dirs = "2.0.2"
|
dirs = "2.0.2"
|
||||||
lazy_static = "1.4.0"
|
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
indicatif = "0.13.0"
|
indicatif = "0.13.0"
|
||||||
humantime = "2.0.0"
|
humantime = "2.0.0"
|
||||||
@ -27,25 +26,27 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.44"
|
serde_json = "1.0.44"
|
||||||
serde_yaml = "0.8.11"
|
solana-budget-program = { path = "../programs/budget", version = "0.23.8" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "0.23.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.8" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
|
solana-cli-config = { path = "../cli-config", version = "0.23.8" }
|
||||||
solana-client = { path = "../client", version = "0.23.0" }
|
solana-client = { path = "../client", version = "0.23.8" }
|
||||||
solana-config-program = { path = "../programs/config", version = "0.23.0" }
|
solana-config-program = { path = "../programs/config", version = "0.23.8" }
|
||||||
solana-faucet = { path = "../faucet", version = "0.23.0" }
|
solana-faucet = { path = "../faucet", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.23.0" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-runtime = { path = "../runtime", version = "0.23.8" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "0.23.0" }
|
solana-stake-program = { path = "../programs/stake", version = "0.23.8" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "0.23.0" }
|
solana-storage-program = { path = "../programs/storage", version = "0.23.8" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "0.23.0" }
|
solana-vote-program = { path = "../programs/vote", version = "0.23.8" }
|
||||||
|
solana-vote-signer = { path = "../vote-signer", version = "0.23.8" }
|
||||||
|
titlecase = "1.1.0"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-core = { path = "../core", version = "0.23.0" }
|
solana-core = { path = "../core", version = "0.23.8" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "0.23.0" }
|
solana-budget-program = { path = "../programs/budget", version = "0.23.8" }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
1682
cli/src/cli.rs
1682
cli/src/cli.rs
File diff suppressed because it is too large
Load Diff
@ -8,21 +8,25 @@ use crate::{
|
|||||||
use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||||
use console::{style, Emoji};
|
use console::{style, Emoji};
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use solana_clap_utils::{input_parsers::*, input_validators::*};
|
use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_from_path};
|
||||||
use solana_client::{rpc_client::RpcClient, rpc_response::RpcVoteAccountInfo};
|
use solana_client::{rpc_client::RpcClient, rpc_response::RpcVoteAccountInfo};
|
||||||
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
clock::{self, Slot},
|
clock::{self, Slot},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
epoch_schedule::{Epoch, EpochSchedule},
|
epoch_schedule::Epoch,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, KeypairUtil},
|
signature::{Keypair, Signer},
|
||||||
system_transaction,
|
system_instruction,
|
||||||
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
|
sync::Arc,
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
@ -67,6 +71,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.help("Slot number of the block to query")
|
.help("Slot number of the block to query")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.subcommand(SubCommand::with_name("leader-schedule").about("Display leader schedule"))
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("epoch-info")
|
SubCommand::with_name("epoch-info")
|
||||||
.about("Get information about the current epoch")
|
.about("Get information about the current epoch")
|
||||||
@ -215,11 +220,15 @@ pub fn parse_catchup(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliErro
|
|||||||
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
|
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::Catchup { node_pubkey },
|
command: CliCommand::Catchup { node_pubkey },
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_cluster_ping(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let lamports = value_t_or_exit!(matches, "lamports", u64);
|
let lamports = value_t_or_exit!(matches, "lamports", u64);
|
||||||
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
|
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
|
||||||
let count = if matches.is_present("count") {
|
let count = if matches.is_present("count") {
|
||||||
@ -241,7 +250,12 @@ pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Cl
|
|||||||
timeout,
|
timeout,
|
||||||
commitment_config,
|
commitment_config,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: vec![signer_from_path(
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
"keypair",
|
||||||
|
wallet_manager,
|
||||||
|
)?],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +263,7 @@ pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
|
|||||||
let slot = value_t_or_exit!(matches, "slot", u64);
|
let slot = value_t_or_exit!(matches, "slot", u64);
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetBlockTime { slot },
|
command: CliCommand::GetBlockTime { slot },
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +275,7 @@ pub fn parse_get_epoch_info(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
|
|||||||
};
|
};
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetEpochInfo { commitment_config },
|
command: CliCommand::GetEpochInfo { commitment_config },
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +287,7 @@ pub fn parse_get_slot(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliErr
|
|||||||
};
|
};
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetSlot { commitment_config },
|
command: CliCommand::GetSlot { commitment_config },
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +299,7 @@ pub fn parse_get_transaction_count(matches: &ArgMatches<'_>) -> Result<CliComman
|
|||||||
};
|
};
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetTransactionCount { commitment_config },
|
command: CliCommand::GetTransactionCount { commitment_config },
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +312,7 @@ pub fn parse_show_stakes(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Cli
|
|||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
vote_account_pubkeys,
|
vote_account_pubkeys,
|
||||||
},
|
},
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +321,7 @@ pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
|
|||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowValidators { use_lamports_unit },
|
command: CliCommand::ShowValidators { use_lamports_unit },
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,20 +334,6 @@ fn new_spinner_progress_bar() -> ProgressBar {
|
|||||||
progress_bar
|
progress_bar
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Aggregate epoch credit stats and return (total credits, total slots, total epochs)
|
|
||||||
pub fn aggregate_epoch_credits(
|
|
||||||
epoch_credits: &[(Epoch, u64, u64)],
|
|
||||||
epoch_schedule: &EpochSchedule,
|
|
||||||
) -> (u64, u64, u64) {
|
|
||||||
epoch_credits
|
|
||||||
.iter()
|
|
||||||
.fold((0, 0, 0), |acc, (epoch, credits, prev_credits)| {
|
|
||||||
let credits_earned = credits - prev_credits;
|
|
||||||
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
|
|
||||||
(acc.0 + credits_earned, acc.1 + slots_in_epoch, acc.2 + 1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_catchup(rpc_client: &RpcClient, node_pubkey: &Pubkey) -> ProcessResult {
|
pub fn process_catchup(rpc_client: &RpcClient, node_pubkey: &Pubkey) -> ProcessResult {
|
||||||
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
||||||
|
|
||||||
@ -406,6 +406,41 @@ pub fn process_fees(rpc_client: &RpcClient) -> ProcessResult {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
|
||||||
|
let epoch_info = rpc_client.get_epoch_info()?;
|
||||||
|
let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
|
||||||
|
|
||||||
|
let leader_schedule = rpc_client.get_leader_schedule(Some(first_slot_in_epoch))?;
|
||||||
|
if leader_schedule.is_none() {
|
||||||
|
return Err(format!(
|
||||||
|
"Unable to fetch leader schedule for slot {}",
|
||||||
|
first_slot_in_epoch
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
let leader_schedule = leader_schedule.unwrap();
|
||||||
|
|
||||||
|
let mut leader_per_slot_index = Vec::new();
|
||||||
|
for (pubkey, leader_slots) in leader_schedule.iter() {
|
||||||
|
for slot_index in leader_slots.iter() {
|
||||||
|
if *slot_index >= leader_per_slot_index.len() {
|
||||||
|
leader_per_slot_index.resize(*slot_index + 1, "?");
|
||||||
|
}
|
||||||
|
leader_per_slot_index[*slot_index] = pubkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (slot_index, leader) in leader_per_slot_index.iter().enumerate() {
|
||||||
|
println!(
|
||||||
|
" {:<15} {:<44}",
|
||||||
|
first_slot_in_epoch + slot_index as u64,
|
||||||
|
leader
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok("".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process_get_block_time(rpc_client: &RpcClient, slot: Slot) -> ProcessResult {
|
pub fn process_get_block_time(rpc_client: &RpcClient, slot: Slot) -> ProcessResult {
|
||||||
let timestamp = rpc_client.get_block_time(slot)?;
|
let timestamp = rpc_client.get_block_time(slot)?;
|
||||||
Ok(timestamp.to_string())
|
Ok(timestamp.to_string())
|
||||||
@ -429,11 +464,11 @@ pub fn process_get_epoch_info(
|
|||||||
let start_slot = epoch_info.absolute_slot - epoch_info.slot_index;
|
let start_slot = epoch_info.absolute_slot - epoch_info.slot_index;
|
||||||
let end_slot = start_slot + epoch_info.slots_in_epoch;
|
let end_slot = start_slot + epoch_info.slots_in_epoch;
|
||||||
println_name_value(
|
println_name_value(
|
||||||
"Epoch slot range:",
|
"Epoch Slot Range:",
|
||||||
&format!("[{}..{})", start_slot, end_slot),
|
&format!("[{}..{})", start_slot, end_slot),
|
||||||
);
|
);
|
||||||
println_name_value(
|
println_name_value(
|
||||||
"Epoch completed percent:",
|
"Epoch Completed Percent:",
|
||||||
&format!(
|
&format!(
|
||||||
"{:>3.3}%",
|
"{:>3.3}%",
|
||||||
epoch_info.slot_index as f64 / epoch_info.slots_in_epoch as f64 * 100_f64
|
epoch_info.slot_index as f64 / epoch_info.slots_in_epoch as f64 * 100_f64
|
||||||
@ -441,14 +476,14 @@ pub fn process_get_epoch_info(
|
|||||||
);
|
);
|
||||||
let remaining_slots_in_epoch = epoch_info.slots_in_epoch - epoch_info.slot_index;
|
let remaining_slots_in_epoch = epoch_info.slots_in_epoch - epoch_info.slot_index;
|
||||||
println_name_value(
|
println_name_value(
|
||||||
"Epoch completed slots:",
|
"Epoch Completed Slots:",
|
||||||
&format!(
|
&format!(
|
||||||
"{}/{} ({} remaining)",
|
"{}/{} ({} remaining)",
|
||||||
epoch_info.slot_index, epoch_info.slots_in_epoch, remaining_slots_in_epoch
|
epoch_info.slot_index, epoch_info.slots_in_epoch, remaining_slots_in_epoch
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
println_name_value(
|
println_name_value(
|
||||||
"Epoch completed time:",
|
"Epoch Completed Time:",
|
||||||
&format!(
|
&format!(
|
||||||
"{}/{} ({} remaining)",
|
"{}/{} ({} remaining)",
|
||||||
slot_to_human_time(epoch_info.slot_index),
|
slot_to_human_time(epoch_info.slot_index),
|
||||||
@ -478,7 +513,7 @@ pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliComman
|
|||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowBlockProduction { epoch, slot_limit },
|
command: CliCommand::ShowBlockProduction { epoch, slot_limit },
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -673,8 +708,8 @@ pub fn process_ping(
|
|||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let to = Keypair::new().pubkey();
|
let to = Keypair::new().pubkey();
|
||||||
|
|
||||||
println_name_value("Source account:", &config.keypair.pubkey().to_string());
|
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
||||||
println_name_value("Destination account:", &to.to_string());
|
println_name_value("Destination Account:", &to.to_string());
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let (signal_sender, signal_receiver) = std::sync::mpsc::channel();
|
let (signal_sender, signal_receiver) = std::sync::mpsc::channel();
|
||||||
@ -692,11 +727,13 @@ pub fn process_ping(
|
|||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_new_blockhash(&last_blockhash)?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_new_blockhash(&last_blockhash)?;
|
||||||
last_blockhash = recent_blockhash;
|
last_blockhash = recent_blockhash;
|
||||||
|
|
||||||
let transaction =
|
let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
|
||||||
system_transaction::transfer(&config.keypair, &to, lamports, recent_blockhash);
|
let message = Message::new(vec![ix]);
|
||||||
|
let mut transaction = Transaction::new_unsigned(message);
|
||||||
|
transaction.try_sign(&config.signers, recent_blockhash)?;
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&transaction.message,
|
&transaction.message,
|
||||||
)?;
|
)?;
|
||||||
@ -864,7 +901,7 @@ pub fn process_show_stakes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool) -> ProcessResult {
|
pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool) -> ProcessResult {
|
||||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
let epoch_info = rpc_client.get_epoch_info()?;
|
||||||
let vote_accounts = rpc_client.get_vote_accounts()?;
|
let vote_accounts = rpc_client.get_vote_accounts()?;
|
||||||
let total_active_stake = vote_accounts
|
let total_active_stake = vote_accounts
|
||||||
.current
|
.current
|
||||||
@ -913,7 +950,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
|
|||||||
"Commission",
|
"Commission",
|
||||||
"Last Vote",
|
"Last Vote",
|
||||||
"Root Block",
|
"Root Block",
|
||||||
"Uptime",
|
"Credits",
|
||||||
"Active Stake",
|
"Active Stake",
|
||||||
))
|
))
|
||||||
.bold()
|
.bold()
|
||||||
@ -921,7 +958,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
|
|||||||
|
|
||||||
fn print_vote_account(
|
fn print_vote_account(
|
||||||
vote_account: RpcVoteAccountInfo,
|
vote_account: RpcVoteAccountInfo,
|
||||||
epoch_schedule: &EpochSchedule,
|
current_epoch: Epoch,
|
||||||
total_active_stake: f64,
|
total_active_stake: f64,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
delinquent: bool,
|
delinquent: bool,
|
||||||
@ -934,17 +971,6 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uptime(epoch_credits: Vec<(Epoch, u64, u64)>, epoch_schedule: &EpochSchedule) -> String {
|
|
||||||
let (total_credits, total_slots, _) =
|
|
||||||
aggregate_epoch_credits(&epoch_credits, &epoch_schedule);
|
|
||||||
if total_slots > 0 {
|
|
||||||
let total_uptime = 100_f64 * total_credits as f64 / total_slots as f64;
|
|
||||||
format!("{:.2}%", total_uptime)
|
|
||||||
} else {
|
|
||||||
"-".into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{} {:<44} {:<44} {:>9}% {:>8} {:>10} {:>7} {}",
|
"{} {:<44} {:<44} {:>9}% {:>8} {:>10} {:>7} {}",
|
||||||
if delinquent {
|
if delinquent {
|
||||||
@ -957,7 +983,15 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
|
|||||||
vote_account.commission,
|
vote_account.commission,
|
||||||
non_zero_or_dash(vote_account.last_vote),
|
non_zero_or_dash(vote_account.last_vote),
|
||||||
non_zero_or_dash(vote_account.root_slot),
|
non_zero_or_dash(vote_account.root_slot),
|
||||||
uptime(vote_account.epoch_credits, epoch_schedule),
|
vote_account
|
||||||
|
.epoch_credits
|
||||||
|
.iter()
|
||||||
|
.find_map(|(epoch, credits, _)| if *epoch == current_epoch {
|
||||||
|
Some(*credits)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or(0),
|
||||||
if vote_account.activated_stake > 0 {
|
if vote_account.activated_stake > 0 {
|
||||||
format!(
|
format!(
|
||||||
"{} ({:.2}%)",
|
"{} ({:.2}%)",
|
||||||
@ -973,7 +1007,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
|
|||||||
for vote_account in vote_accounts.current.into_iter() {
|
for vote_account in vote_accounts.current.into_iter() {
|
||||||
print_vote_account(
|
print_vote_account(
|
||||||
vote_account,
|
vote_account,
|
||||||
&epoch_schedule,
|
epoch_info.epoch,
|
||||||
total_active_stake,
|
total_active_stake,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
false,
|
false,
|
||||||
@ -982,7 +1016,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
|
|||||||
for vote_account in vote_accounts.delinquent.into_iter() {
|
for vote_account in vote_accounts.delinquent.into_iter() {
|
||||||
print_vote_account(
|
print_vote_account(
|
||||||
vote_account,
|
vote_account,
|
||||||
&epoch_schedule,
|
epoch_info.epoch,
|
||||||
total_active_stake,
|
total_active_stake,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
true,
|
true,
|
||||||
@ -996,28 +1030,38 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::cli::{app, parse_command};
|
use crate::cli::{app, parse_command};
|
||||||
|
use solana_sdk::signature::{write_keypair, Keypair};
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
|
fn make_tmp_file() -> (String, NamedTempFile) {
|
||||||
|
let tmp_file = NamedTempFile::new().unwrap();
|
||||||
|
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_command() {
|
fn test_parse_command() {
|
||||||
let test_commands = app("test", "desc", "version");
|
let test_commands = app("test", "desc", "version");
|
||||||
|
let default_keypair = Keypair::new();
|
||||||
|
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
let test_cluster_version = test_commands
|
let test_cluster_version = test_commands
|
||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "cluster-version"]);
|
.get_matches_from(vec!["test", "cluster-version"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_cluster_version).unwrap(),
|
parse_command(&test_cluster_version, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::ClusterVersion,
|
command: CliCommand::ClusterVersion,
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
|
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_fees).unwrap(),
|
parse_command(&test_fees, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::Fees,
|
command: CliCommand::Fees,
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1027,10 +1071,10 @@ mod tests {
|
|||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "block-time", &slot.to_string()]);
|
.get_matches_from(vec!["test", "block-time", &slot.to_string()]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_get_block_time).unwrap(),
|
parse_command(&test_get_block_time, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetBlockTime { slot },
|
command: CliCommand::GetBlockTime { slot },
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1038,12 +1082,12 @@ mod tests {
|
|||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "epoch-info"]);
|
.get_matches_from(vec!["test", "epoch-info"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_get_epoch_info).unwrap(),
|
parse_command(&test_get_epoch_info, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetEpochInfo {
|
command: CliCommand::GetEpochInfo {
|
||||||
commitment_config: CommitmentConfig::recent(),
|
commitment_config: CommitmentConfig::recent(),
|
||||||
},
|
},
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1051,21 +1095,21 @@ mod tests {
|
|||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "genesis-hash"]);
|
.get_matches_from(vec!["test", "genesis-hash"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_get_genesis_hash).unwrap(),
|
parse_command(&test_get_genesis_hash, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetGenesisHash,
|
command: CliCommand::GetGenesisHash,
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
|
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_get_slot).unwrap(),
|
parse_command(&test_get_slot, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetSlot {
|
command: CliCommand::GetSlot {
|
||||||
commitment_config: CommitmentConfig::recent(),
|
commitment_config: CommitmentConfig::recent(),
|
||||||
},
|
},
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1073,12 +1117,12 @@ mod tests {
|
|||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "transaction-count"]);
|
.get_matches_from(vec!["test", "transaction-count"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_transaction_count).unwrap(),
|
parse_command(&test_transaction_count, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetTransactionCount {
|
command: CliCommand::GetTransactionCount {
|
||||||
commitment_config: CommitmentConfig::recent(),
|
commitment_config: CommitmentConfig::recent(),
|
||||||
},
|
},
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1094,7 +1138,7 @@ mod tests {
|
|||||||
"--confirmed",
|
"--confirmed",
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_ping).unwrap(),
|
parse_command(&test_ping, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::Ping {
|
command: CliCommand::Ping {
|
||||||
lamports: 1,
|
lamports: 1,
|
||||||
@ -1103,7 +1147,7 @@ mod tests {
|
|||||||
timeout: Duration::from_secs(3),
|
timeout: Duration::from_secs(3),
|
||||||
commitment_config: CommitmentConfig::default(),
|
commitment_config: CommitmentConfig::default(),
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![default_keypair.into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod cluster_query;
|
pub mod cluster_query;
|
||||||
pub mod config;
|
|
||||||
pub mod display;
|
pub mod display;
|
||||||
pub mod nonce;
|
pub mod nonce;
|
||||||
|
pub mod offline;
|
||||||
pub mod stake;
|
pub mod stake;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod validator_info;
|
pub mod validator_info;
|
||||||
|
141
cli/src/main.rs
141
cli/src/main.rs
@ -2,20 +2,17 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches
|
|||||||
use console::style;
|
use console::style;
|
||||||
|
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_validators::is_url,
|
input_parsers::derivation_of,
|
||||||
keypair::{
|
input_validators::{is_derivation, is_url},
|
||||||
self, keypair_input, KeypairWithSource, ASK_SEED_PHRASE_ARG,
|
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||||
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliError},
|
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
|
||||||
config::{self, Config},
|
|
||||||
display::{println_name_value, println_name_value_or},
|
display::{println_name_value, println_name_value_or},
|
||||||
};
|
};
|
||||||
use solana_sdk::signature::read_keypair_file;
|
use solana_cli_config::config::{Config, CONFIG_FILE};
|
||||||
|
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
|
||||||
use std::error;
|
use std::{error, sync::Arc};
|
||||||
|
|
||||||
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
|
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
|
||||||
let parse_args = match matches.subcommand() {
|
let parse_args = match matches.subcommand() {
|
||||||
@ -24,21 +21,25 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
if let Some(config_file) = matches.value_of("config_file") {
|
if let Some(config_file) = matches.value_of("config_file") {
|
||||||
let config = Config::load(config_file).unwrap_or_default();
|
let config = Config::load(config_file).unwrap_or_default();
|
||||||
if let Some(field) = subcommand_matches.value_of("specific_setting") {
|
if let Some(field) = subcommand_matches.value_of("specific_setting") {
|
||||||
let (value, default_value) = match field {
|
let (field_name, value, default_value) = match field {
|
||||||
"url" => (config.url, CliConfig::default_json_rpc_url()),
|
"url" => ("RPC URL", config.url, CliConfig::default_json_rpc_url()),
|
||||||
"keypair" => (config.keypair_path, CliConfig::default_keypair_path()),
|
"keypair" => (
|
||||||
|
"Key Path",
|
||||||
|
config.keypair_path,
|
||||||
|
CliConfig::default_keypair_path(),
|
||||||
|
),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
println_name_value_or(&format!("* {}:", field), &value, &default_value);
|
println_name_value_or(&format!("{}:", field_name), &value, &default_value);
|
||||||
} else {
|
} else {
|
||||||
println_name_value("Wallet Config:", config_file);
|
println_name_value("Config File:", config_file);
|
||||||
println_name_value_or(
|
println_name_value_or(
|
||||||
"* url:",
|
"RPC URL:",
|
||||||
&config.url,
|
&config.url,
|
||||||
&CliConfig::default_json_rpc_url(),
|
&CliConfig::default_json_rpc_url(),
|
||||||
);
|
);
|
||||||
println_name_value_or(
|
println_name_value_or(
|
||||||
"* keypair:",
|
"Keypair Path:",
|
||||||
&config.keypair_path,
|
&config.keypair_path,
|
||||||
&CliConfig::default_keypair_path(),
|
&CliConfig::default_keypair_path(),
|
||||||
);
|
);
|
||||||
@ -61,9 +62,9 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
config.keypair_path = keypair.to_string();
|
config.keypair_path = keypair.to_string();
|
||||||
}
|
}
|
||||||
config.save(config_file)?;
|
config.save(config_file)?;
|
||||||
println_name_value("Wallet Config Updated:", config_file);
|
println_name_value("Config File:", config_file);
|
||||||
println_name_value("* url:", &config.url);
|
println_name_value("RPC URL:", &config.url);
|
||||||
println_name_value("* keypair:", &config.keypair_path);
|
println_name_value("Keypair Path:", &config.keypair_path);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
||||||
@ -79,7 +80,10 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
Ok(parse_args)
|
Ok(parse_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::Error>> {
|
pub fn parse_args<'a>(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
wallet_manager: Option<Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<(CliConfig<'a>, CliSigners), Box<dyn error::Error>> {
|
||||||
let config = if let Some(config_file) = matches.value_of("config_file") {
|
let config = if let Some(config_file) = matches.value_of("config_file") {
|
||||||
Config::load(config_file).unwrap_or_default()
|
Config::load(config_file).unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
@ -94,57 +98,29 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
|
|||||||
default.json_rpc_url
|
default.json_rpc_url
|
||||||
};
|
};
|
||||||
|
|
||||||
let CliCommandInfo {
|
let default_signer_path = if matches.is_present("keypair") {
|
||||||
command,
|
matches.value_of("keypair").unwrap().to_string()
|
||||||
require_keypair,
|
} else if config.keypair_path != "" {
|
||||||
} = parse_command(&matches)?;
|
config.keypair_path
|
||||||
|
|
||||||
let (keypair, keypair_path) = if require_keypair {
|
|
||||||
let KeypairWithSource { keypair, source } = keypair_input(&matches, "keypair")?;
|
|
||||||
match source {
|
|
||||||
keypair::Source::File => (
|
|
||||||
keypair,
|
|
||||||
Some(matches.value_of("keypair").unwrap().to_string()),
|
|
||||||
),
|
|
||||||
keypair::Source::SeedPhrase => (keypair, None),
|
|
||||||
keypair::Source::Generated => {
|
|
||||||
let keypair_path = if config.keypair_path != "" {
|
|
||||||
config.keypair_path
|
|
||||||
} else {
|
|
||||||
let default_keypair_path = CliConfig::default_keypair_path();
|
|
||||||
if !std::path::Path::new(&default_keypair_path).exists() {
|
|
||||||
return Err(CliError::KeypairFileNotFound(format!(
|
|
||||||
"Generate a new keypair at {} with `solana-keygen new`",
|
|
||||||
default_keypair_path
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
default_keypair_path
|
|
||||||
};
|
|
||||||
|
|
||||||
let keypair = read_keypair_file(&keypair_path).or_else(|err| {
|
|
||||||
Err(CliError::BadParameter(format!(
|
|
||||||
"{}: Unable to open keypair file: {}",
|
|
||||||
err, keypair_path
|
|
||||||
)))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
(keypair, Some(keypair_path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let default = CliConfig::default();
|
CliConfig::default_keypair_path()
|
||||||
(default.keypair, None)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(CliConfig {
|
let CliCommandInfo { command, signers } =
|
||||||
command,
|
parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
|
||||||
json_rpc_url,
|
|
||||||
keypair,
|
Ok((
|
||||||
keypair_path,
|
CliConfig {
|
||||||
rpc_client: None,
|
command,
|
||||||
verbose: matches.is_present("verbose"),
|
json_rpc_url,
|
||||||
})
|
signers: vec![],
|
||||||
|
keypair_path: default_signer_path,
|
||||||
|
derivation_path: derivation_of(matches, "derivation_path"),
|
||||||
|
rpc_client: None,
|
||||||
|
verbose: matches.is_present("verbose"),
|
||||||
|
},
|
||||||
|
signers,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn error::Error>> {
|
fn main() -> Result<(), Box<dyn error::Error>> {
|
||||||
@ -162,7 +138,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.global(true)
|
.global(true)
|
||||||
.help("Configuration file to use");
|
.help("Configuration file to use");
|
||||||
if let Some(ref config_file) = *config::CONFIG_FILE {
|
if let Some(ref config_file) = *CONFIG_FILE {
|
||||||
arg.default_value(&config_file)
|
arg.default_value(&config_file)
|
||||||
} else {
|
} else {
|
||||||
arg
|
arg
|
||||||
@ -185,7 +161,16 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.value_name("PATH")
|
.value_name("PATH")
|
||||||
.global(true)
|
.global(true)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("/path/to/id.json"),
|
.help("/path/to/id.json or usb://remote/wallet/path"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("derivation_path")
|
||||||
|
.long("derivation-path")
|
||||||
|
.value_name("ACCOUNT or ACCOUNT/CHANGE")
|
||||||
|
.global(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(is_derivation)
|
||||||
|
.help("Derivation path to use: m/44'/501'/ACCOUNT'/CHANGE'; default key is device base pubkey: m/44'/501'/0'")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("verbose")
|
Arg::with_name("verbose")
|
||||||
@ -194,15 +179,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.global(true)
|
.global(true)
|
||||||
.help("Show extra information header"),
|
.help("Show extra information header"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name(ASK_SEED_PHRASE_ARG.name)
|
|
||||||
.long(ASK_SEED_PHRASE_ARG.long)
|
|
||||||
.value_name("KEYPAIR NAME")
|
|
||||||
.global(true)
|
|
||||||
.takes_value(true)
|
|
||||||
.possible_values(&["keypair"])
|
|
||||||
.help(ASK_SEED_PHRASE_ARG.help),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
||||||
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
||||||
@ -240,7 +216,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
if parse_settings(&matches)? {
|
if parse_settings(&matches)? {
|
||||||
let config = parse_args(&matches)?;
|
let wallet_manager = maybe_wallet_manager()?;
|
||||||
|
|
||||||
|
let (mut config, signers) = parse_args(&matches, wallet_manager)?;
|
||||||
|
config.signers = signers.iter().map(|s| s.as_ref()).collect();
|
||||||
let result = process_command(&config)?;
|
let result = process_command(&config)?;
|
||||||
println!("{}", result);
|
println!("{}", result);
|
||||||
}
|
}
|
||||||
|
384
cli/src/nonce.rs
384
cli/src/nonce.rs
@ -1,18 +1,21 @@
|
|||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
build_balance_message, check_account_for_fee, check_unique_pubkeys,
|
build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
|
||||||
log_instruction_custom_error, required_lamports_from, CliCommand, CliCommandInfo, CliConfig,
|
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
|
||||||
CliError, ProcessResult, SigningAuthority,
|
SignerIndex,
|
||||||
};
|
};
|
||||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
use solana_clap_utils::{input_parsers::*, input_validators::*, ArgConstant};
|
use solana_clap_utils::{
|
||||||
|
input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant,
|
||||||
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
message::Message,
|
||||||
nonce_state::{Meta, NonceState},
|
nonce_state::{Meta, NonceState},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, KeypairUtil},
|
|
||||||
system_instruction::{
|
system_instruction::{
|
||||||
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
|
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
|
||||||
create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError,
|
create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError,
|
||||||
@ -21,6 +24,7 @@ use solana_sdk::{
|
|||||||
system_program,
|
system_program,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum CliNonceError {
|
pub enum CliNonceError {
|
||||||
@ -55,7 +59,7 @@ pub fn nonce_arg<'a, 'b>() -> Arg<'a, 'b> {
|
|||||||
.long(NONCE_ARG.long)
|
.long(NONCE_ARG.long)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("PUBKEY")
|
.value_name("PUBKEY")
|
||||||
.requires("blockhash")
|
.requires(BLOCKHASH_ARG.name)
|
||||||
.validator(is_pubkey)
|
.validator(is_pubkey)
|
||||||
.help(NONCE_ARG.help)
|
.help(NONCE_ARG.help)
|
||||||
}
|
}
|
||||||
@ -64,8 +68,8 @@ pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
|
|||||||
Arg::with_name(NONCE_AUTHORITY_ARG.name)
|
Arg::with_name(NONCE_AUTHORITY_ARG.name)
|
||||||
.long(NONCE_AUTHORITY_ARG.long)
|
.long(NONCE_AUTHORITY_ARG.long)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("KEYPAIR or PUBKEY")
|
.value_name("KEYPAIR or PUBKEY or REMOTE WALLET PATH")
|
||||||
.validator(is_pubkey_or_keypair_or_ask_keyword)
|
.validator(is_valid_signer)
|
||||||
.help(NONCE_AUTHORITY_ARG.help)
|
.help(NONCE_AUTHORITY_ARG.help)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,15 +124,7 @@ impl NonceSubCommands for App<'_, '_> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
.validator(is_amount)
|
.validator(is_amount)
|
||||||
.help("The amount to load the nonce account with (default unit SOL)"),
|
.help("The amount to load the nonce account with, in SOL"),
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("unit")
|
|
||||||
.index(3)
|
|
||||||
.value_name("UNIT")
|
|
||||||
.takes_value(true)
|
|
||||||
.possible_values(&["SOL", "lamports"])
|
|
||||||
.help("Specify unit to use for request"),
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(NONCE_AUTHORITY_ARG.name)
|
Arg::with_name(NONCE_AUTHORITY_ARG.name)
|
||||||
@ -215,58 +211,68 @@ impl NonceSubCommands for App<'_, '_> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
.validator(is_amount)
|
.validator(is_amount)
|
||||||
.help("The amount to withdraw from the nonce account (default unit SOL)"),
|
.help("The amount to withdraw from the nonce account, in SOL"),
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("unit")
|
|
||||||
.index(4)
|
|
||||||
.value_name("UNIT")
|
|
||||||
.takes_value(true)
|
|
||||||
.possible_values(&["SOL", "lamports"])
|
|
||||||
.help("Specify unit to use for request"),
|
|
||||||
)
|
)
|
||||||
.arg(nonce_authority_arg()),
|
.arg(nonce_authority_arg()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_authorize_nonce_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_authorize_nonce_account(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
|
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
|
||||||
let new_authority = pubkey_of(matches, "new_authority").unwrap();
|
let new_authority = pubkey_of(matches, "new_authority").unwrap();
|
||||||
let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) {
|
let (nonce_authority, nonce_authority_pubkey) =
|
||||||
Some(SigningAuthority::new_from_matches(
|
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||||
&matches,
|
|
||||||
NONCE_AUTHORITY_ARG.name,
|
let payer_provided = None;
|
||||||
None,
|
let signer_info = generate_unique_signers(
|
||||||
)?)
|
vec![payer_provided, nonce_authority],
|
||||||
} else {
|
matches,
|
||||||
None
|
default_signer_path,
|
||||||
};
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::AuthorizeNonceAccount {
|
command: CliCommand::AuthorizeNonceAccount {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority,
|
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||||
new_authority,
|
new_authority,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_nonce_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_nonce_create_account(
|
||||||
let nonce_account = keypair_of(matches, "nonce_account_keypair").unwrap();
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let (nonce_account, nonce_account_pubkey) =
|
||||||
|
signer_of(matches, "nonce_account_keypair", wallet_manager)?;
|
||||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||||
let nonce_authority = pubkey_of(matches, NONCE_AUTHORITY_ARG.name);
|
let nonce_authority = pubkey_of(matches, NONCE_AUTHORITY_ARG.name);
|
||||||
|
|
||||||
|
let payer_provided = None;
|
||||||
|
let signer_info = generate_unique_signers(
|
||||||
|
vec![payer_provided, nonce_account],
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::CreateNonceAccount {
|
command: CliCommand::CreateNonceAccount {
|
||||||
nonce_account: nonce_account.into(),
|
nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
|
||||||
seed,
|
seed,
|
||||||
nonce_authority,
|
nonce_authority,
|
||||||
lamports,
|
lamports,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,28 +281,33 @@ pub fn parse_get_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliEr
|
|||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetNonce(nonce_account_pubkey),
|
command: CliCommand::GetNonce(nonce_account_pubkey),
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_new_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_new_nonce(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
|
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
|
||||||
let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) {
|
let (nonce_authority, nonce_authority_pubkey) =
|
||||||
Some(SigningAuthority::new_from_matches(
|
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||||
&matches,
|
|
||||||
NONCE_AUTHORITY_ARG.name,
|
let payer_provided = None;
|
||||||
None,
|
let signer_info = generate_unique_signers(
|
||||||
)?)
|
vec![payer_provided, nonce_authority],
|
||||||
} else {
|
matches,
|
||||||
None
|
default_signer_path,
|
||||||
};
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::NewNonce {
|
command: CliCommand::NewNonce {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority,
|
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,34 +320,37 @@ pub fn parse_show_nonce_account(matches: &ArgMatches<'_>) -> Result<CliCommandIn
|
|||||||
nonce_account_pubkey,
|
nonce_account_pubkey,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
},
|
},
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_withdraw_from_nonce_account(
|
pub fn parse_withdraw_from_nonce_account(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
|
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
|
||||||
let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
|
let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
|
||||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||||
let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) {
|
let (nonce_authority, nonce_authority_pubkey) =
|
||||||
Some(SigningAuthority::new_from_matches(
|
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||||
&matches,
|
|
||||||
NONCE_AUTHORITY_ARG.name,
|
let payer_provided = None;
|
||||||
None,
|
let signer_info = generate_unique_signers(
|
||||||
)?)
|
vec![payer_provided, nonce_authority],
|
||||||
} else {
|
matches,
|
||||||
None
|
default_signer_path,
|
||||||
};
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::WithdrawFromNonceAccount {
|
command: CliCommand::WithdrawFromNonceAccount {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority,
|
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,41 +386,36 @@ pub fn process_authorize_nonce_account(
|
|||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
nonce_account: &Pubkey,
|
nonce_account: &Pubkey,
|
||||||
nonce_authority: Option<&SigningAuthority>,
|
nonce_authority: SignerIndex,
|
||||||
new_authority: &Pubkey,
|
new_authority: &Pubkey,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
let nonce_authority = nonce_authority
|
let nonce_authority = config.signers[nonce_authority];
|
||||||
.map(|a| a.keypair())
|
|
||||||
.unwrap_or(&config.keypair);
|
|
||||||
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
|
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
|
||||||
let mut tx = Transaction::new_signed_with_payer(
|
let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
|
||||||
vec![ix],
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
Some(&config.keypair.pubkey()),
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
&[&config.keypair, nonce_authority],
|
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result =
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
|
||||||
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
|
|
||||||
log_instruction_custom_error::<NonceError>(result)
|
log_instruction_custom_error::<NonceError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_create_nonce_account(
|
pub fn process_create_nonce_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
nonce_account: &Keypair,
|
nonce_account: SignerIndex,
|
||||||
seed: Option<String>,
|
seed: Option<String>,
|
||||||
nonce_authority: Option<Pubkey>,
|
nonce_authority: Option<Pubkey>,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let nonce_account_pubkey = nonce_account.pubkey();
|
let nonce_account_pubkey = config.signers[nonce_account].pubkey();
|
||||||
let nonce_account_address = if let Some(seed) = seed.clone() {
|
let nonce_account_address = if let Some(seed) = seed.clone() {
|
||||||
create_address_with_seed(&nonce_account_pubkey, &seed, &system_program::id())?
|
create_address_with_seed(&nonce_account_pubkey, &seed, &system_program::id())?
|
||||||
} else {
|
} else {
|
||||||
@ -414,7 +423,7 @@ pub fn process_create_nonce_account(
|
|||||||
};
|
};
|
||||||
|
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(&config.keypair.pubkey(), "cli keypair".to_string()),
|
(&config.signers[0].pubkey(), "cli keypair".to_string()),
|
||||||
(&nonce_account_address, "nonce_account".to_string()),
|
(&nonce_account_address, "nonce_account".to_string()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -441,20 +450,20 @@ pub fn process_create_nonce_account(
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.pubkey());
|
let nonce_authority = nonce_authority.unwrap_or_else(|| config.signers[0].pubkey());
|
||||||
|
|
||||||
let ixs = if let Some(seed) = seed {
|
let ixs = if let Some(seed) = seed {
|
||||||
create_nonce_account_with_seed(
|
create_nonce_account_with_seed(
|
||||||
&config.keypair.pubkey(), // from
|
&config.signers[0].pubkey(), // from
|
||||||
&nonce_account_address, // to
|
&nonce_account_address, // to
|
||||||
&nonce_account_pubkey, // base
|
&nonce_account_pubkey, // base
|
||||||
&seed, // seed
|
&seed, // seed
|
||||||
&nonce_authority,
|
&nonce_authority,
|
||||||
lamports,
|
lamports,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
create_nonce_account(
|
create_nonce_account(
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&nonce_account_pubkey,
|
&nonce_account_pubkey,
|
||||||
&nonce_authority,
|
&nonce_authority,
|
||||||
lamports,
|
lamports,
|
||||||
@ -463,25 +472,17 @@ pub fn process_create_nonce_account(
|
|||||||
|
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
let signers = if nonce_account_pubkey != config.keypair.pubkey() {
|
let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
|
||||||
vec![&config.keypair, nonce_account] // both must sign if `from` and `to` differ
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
} else {
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
vec![&config.keypair] // when stake_account == config.keypair and there's a seed, we only need one signature
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tx = Transaction::new_signed_with_payer(
|
|
||||||
ixs,
|
|
||||||
Some(&config.keypair.pubkey()),
|
|
||||||
&signers,
|
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &signers);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
|
||||||
log_instruction_custom_error::<SystemError>(result)
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,10 +510,10 @@ pub fn process_new_nonce(
|
|||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
nonce_account: &Pubkey,
|
nonce_account: &Pubkey,
|
||||||
nonce_authority: Option<&SigningAuthority>,
|
nonce_authority: SignerIndex,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(&config.keypair.pubkey(), "cli keypair".to_string()),
|
(&config.signers[0].pubkey(), "cli keypair".to_string()),
|
||||||
(&nonce_account, "nonce_account_pubkey".to_string()),
|
(&nonce_account, "nonce_account_pubkey".to_string()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -523,25 +524,20 @@ pub fn process_new_nonce(
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let nonce_authority = nonce_authority
|
let nonce_authority = config.signers[nonce_authority];
|
||||||
.map(|a| a.keypair())
|
|
||||||
.unwrap_or(&config.keypair);
|
|
||||||
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
|
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
let mut tx = Transaction::new_signed_with_payer(
|
let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
|
||||||
vec![ix],
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
Some(&config.keypair.pubkey()),
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
&[&config.keypair, nonce_authority],
|
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result =
|
let result =
|
||||||
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
|
rpc_client.send_and_confirm_transaction(&mut tx, &[config.signers[0], nonce_authority]);
|
||||||
log_instruction_custom_error::<SystemError>(result)
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,11 +556,11 @@ pub fn process_show_nonce_account(
|
|||||||
}
|
}
|
||||||
let print_account = |data: Option<(Meta, Hash)>| {
|
let print_account = |data: Option<(Meta, Hash)>| {
|
||||||
println!(
|
println!(
|
||||||
"balance: {}",
|
"Balance: {}",
|
||||||
build_balance_message(nonce_account.lamports, use_lamports_unit, true)
|
build_balance_message(nonce_account.lamports, use_lamports_unit, true)
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"minimum balance required: {}",
|
"Minimum Balance Required: {}",
|
||||||
build_balance_message(
|
build_balance_message(
|
||||||
rpc_client.get_minimum_balance_for_rent_exemption(NonceState::size())?,
|
rpc_client.get_minimum_balance_for_rent_exemption(NonceState::size())?,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
@ -573,12 +569,12 @@ pub fn process_show_nonce_account(
|
|||||||
);
|
);
|
||||||
match data {
|
match data {
|
||||||
Some((meta, hash)) => {
|
Some((meta, hash)) => {
|
||||||
println!("nonce: {}", hash);
|
println!("Nonce: {}", hash);
|
||||||
println!("authority: {}", meta.nonce_authority);
|
println!("Authority: {}", meta.nonce_authority);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("nonce: uninitialized");
|
println!("Nonce: uninitialized");
|
||||||
println!("authority: uninitialized");
|
println!("Authority: uninitialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
@ -598,35 +594,29 @@ pub fn process_withdraw_from_nonce_account(
|
|||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
nonce_account: &Pubkey,
|
nonce_account: &Pubkey,
|
||||||
nonce_authority: Option<&SigningAuthority>,
|
nonce_authority: SignerIndex,
|
||||||
destination_account_pubkey: &Pubkey,
|
destination_account_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
let nonce_authority = nonce_authority
|
let nonce_authority = config.signers[nonce_authority];
|
||||||
.map(|a| a.keypair())
|
|
||||||
.unwrap_or(&config.keypair);
|
|
||||||
let ix = withdraw_nonce_account(
|
let ix = withdraw_nonce_account(
|
||||||
nonce_account,
|
nonce_account,
|
||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
);
|
);
|
||||||
let mut tx = Transaction::new_signed_with_payer(
|
let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
|
||||||
vec![ix],
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
Some(&config.keypair.pubkey()),
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
&[&config.keypair, nonce_authority],
|
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result =
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
|
||||||
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
|
|
||||||
log_instruction_custom_error::<NonceError>(result)
|
log_instruction_custom_error::<NonceError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,7 +628,7 @@ mod tests {
|
|||||||
account::Account,
|
account::Account,
|
||||||
hash::hash,
|
hash::hash,
|
||||||
nonce_state::{Meta as NonceMeta, NonceState},
|
nonce_state::{Meta as NonceMeta, NonceState},
|
||||||
signature::{read_keypair_file, write_keypair},
|
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
||||||
system_program,
|
system_program,
|
||||||
};
|
};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
@ -651,6 +641,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_command() {
|
fn test_parse_command() {
|
||||||
let test_commands = app("test", "desc", "version");
|
let test_commands = app("test", "desc", "version");
|
||||||
|
let default_keypair = Keypair::new();
|
||||||
|
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
let nonce_account_keypair = Keypair::new();
|
let nonce_account_keypair = Keypair::new();
|
||||||
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
|
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
@ -669,14 +662,14 @@ mod tests {
|
|||||||
&Pubkey::default().to_string(),
|
&Pubkey::default().to_string(),
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_authorize_nonce_account).unwrap(),
|
parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::AuthorizeNonceAccount {
|
command: CliCommand::AuthorizeNonceAccount {
|
||||||
nonce_account: nonce_account_pubkey,
|
nonce_account: nonce_account_pubkey,
|
||||||
nonce_authority: None,
|
nonce_authority: 0,
|
||||||
new_authority: Pubkey::default(),
|
new_authority: Pubkey::default(),
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -690,16 +683,17 @@ mod tests {
|
|||||||
&authority_keypair_file,
|
&authority_keypair_file,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_authorize_nonce_account).unwrap(),
|
parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::AuthorizeNonceAccount {
|
command: CliCommand::AuthorizeNonceAccount {
|
||||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||||
nonce_authority: Some(
|
nonce_authority: 1,
|
||||||
read_keypair_file(&authority_keypair_file).unwrap().into()
|
|
||||||
),
|
|
||||||
new_authority: Pubkey::default(),
|
new_authority: Pubkey::default(),
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
read_keypair_file(&authority_keypair_file).unwrap().into()
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -709,18 +703,20 @@ mod tests {
|
|||||||
"create-nonce-account",
|
"create-nonce-account",
|
||||||
&keypair_file,
|
&keypair_file,
|
||||||
"50",
|
"50",
|
||||||
"lamports",
|
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_nonce_account).unwrap(),
|
parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateNonceAccount {
|
command: CliCommand::CreateNonceAccount {
|
||||||
nonce_account: read_keypair_file(&keypair_file).unwrap().into(),
|
nonce_account: 1,
|
||||||
seed: None,
|
seed: None,
|
||||||
nonce_authority: None,
|
nonce_authority: None,
|
||||||
lamports: 50,
|
lamports: 50_000_000_000,
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
read_keypair_file(&keypair_file).unwrap().into()
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -730,22 +726,22 @@ mod tests {
|
|||||||
"create-nonce-account",
|
"create-nonce-account",
|
||||||
&keypair_file,
|
&keypair_file,
|
||||||
"50",
|
"50",
|
||||||
"lamports",
|
|
||||||
"--nonce-authority",
|
"--nonce-authority",
|
||||||
&authority_keypair_file,
|
&authority_keypair_file,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_nonce_account).unwrap(),
|
parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateNonceAccount {
|
command: CliCommand::CreateNonceAccount {
|
||||||
nonce_account: read_keypair_file(&keypair_file).unwrap().into(),
|
nonce_account: 1,
|
||||||
seed: None,
|
seed: None,
|
||||||
nonce_authority: Some(
|
nonce_authority: Some(nonce_authority_keypair.pubkey()),
|
||||||
read_keypair_file(&authority_keypair_file).unwrap().pubkey()
|
lamports: 50_000_000_000,
|
||||||
),
|
|
||||||
lamports: 50,
|
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
read_keypair_file(&keypair_file).unwrap().into()
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -756,10 +752,10 @@ mod tests {
|
|||||||
&nonce_account_string,
|
&nonce_account_string,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_get_nonce).unwrap(),
|
parse_command(&test_get_nonce, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetNonce(nonce_account_keypair.pubkey(),),
|
command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -770,13 +766,13 @@ mod tests {
|
|||||||
.get_matches_from(vec!["test", "new-nonce", &keypair_file]);
|
.get_matches_from(vec!["test", "new-nonce", &keypair_file]);
|
||||||
let nonce_account = read_keypair_file(&keypair_file).unwrap();
|
let nonce_account = read_keypair_file(&keypair_file).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_new_nonce).unwrap(),
|
parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::NewNonce {
|
command: CliCommand::NewNonce {
|
||||||
nonce_account: nonce_account.pubkey(),
|
nonce_account: nonce_account.pubkey(),
|
||||||
nonce_authority: None,
|
nonce_authority: 0,
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -790,15 +786,16 @@ mod tests {
|
|||||||
]);
|
]);
|
||||||
let nonce_account = read_keypair_file(&keypair_file).unwrap();
|
let nonce_account = read_keypair_file(&keypair_file).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_new_nonce).unwrap(),
|
parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::NewNonce {
|
command: CliCommand::NewNonce {
|
||||||
nonce_account: nonce_account.pubkey(),
|
nonce_account: nonce_account.pubkey(),
|
||||||
nonce_authority: Some(
|
nonce_authority: 1,
|
||||||
read_keypair_file(&authority_keypair_file).unwrap().into()
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
read_keypair_file(&authority_keypair_file).unwrap().into()
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -809,13 +806,13 @@ mod tests {
|
|||||||
&nonce_account_string,
|
&nonce_account_string,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_show_nonce_account).unwrap(),
|
parse_command(&test_show_nonce_account, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::ShowNonceAccount {
|
command: CliCommand::ShowNonceAccount {
|
||||||
nonce_account_pubkey: nonce_account_keypair.pubkey(),
|
nonce_account_pubkey: nonce_account_keypair.pubkey(),
|
||||||
use_lamports_unit: false,
|
use_lamports_unit: false,
|
||||||
},
|
},
|
||||||
require_keypair: false
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -826,18 +823,22 @@ mod tests {
|
|||||||
&keypair_file,
|
&keypair_file,
|
||||||
&nonce_account_string,
|
&nonce_account_string,
|
||||||
"42",
|
"42",
|
||||||
"lamports",
|
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_withdraw_from_nonce_account).unwrap(),
|
parse_command(
|
||||||
|
&test_withdraw_from_nonce_account,
|
||||||
|
&default_keypair_file,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::WithdrawFromNonceAccount {
|
command: CliCommand::WithdrawFromNonceAccount {
|
||||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||||
nonce_authority: None,
|
nonce_authority: 0,
|
||||||
destination_account_pubkey: nonce_account_pubkey,
|
destination_account_pubkey: nonce_account_pubkey,
|
||||||
lamports: 42
|
lamports: 42_000_000_000
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -847,18 +848,22 @@ mod tests {
|
|||||||
&keypair_file,
|
&keypair_file,
|
||||||
&nonce_account_string,
|
&nonce_account_string,
|
||||||
"42",
|
"42",
|
||||||
"SOL",
|
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_withdraw_from_nonce_account).unwrap(),
|
parse_command(
|
||||||
|
&test_withdraw_from_nonce_account,
|
||||||
|
&default_keypair_file,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::WithdrawFromNonceAccount {
|
command: CliCommand::WithdrawFromNonceAccount {
|
||||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||||
nonce_authority: None,
|
nonce_authority: 0,
|
||||||
destination_account_pubkey: nonce_account_pubkey,
|
destination_account_pubkey: nonce_account_pubkey,
|
||||||
lamports: 42000000000
|
lamports: 42000000000
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -869,22 +874,27 @@ mod tests {
|
|||||||
&keypair_file,
|
&keypair_file,
|
||||||
&nonce_account_string,
|
&nonce_account_string,
|
||||||
"42",
|
"42",
|
||||||
"lamports",
|
|
||||||
"--nonce-authority",
|
"--nonce-authority",
|
||||||
&authority_keypair_file,
|
&authority_keypair_file,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_withdraw_from_nonce_account).unwrap(),
|
parse_command(
|
||||||
|
&test_withdraw_from_nonce_account,
|
||||||
|
&default_keypair_file,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::WithdrawFromNonceAccount {
|
command: CliCommand::WithdrawFromNonceAccount {
|
||||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||||
nonce_authority: Some(
|
nonce_authority: 1,
|
||||||
read_keypair_file(&authority_keypair_file).unwrap().into()
|
|
||||||
),
|
|
||||||
destination_account_pubkey: nonce_account_pubkey,
|
destination_account_pubkey: nonce_account_pubkey,
|
||||||
lamports: 42
|
lamports: 42_000_000_000
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
read_keypair_file(&authority_keypair_file).unwrap().into()
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
253
cli/src/offline.rs
Normal file
253
cli/src/offline.rs
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
use clap::{App, Arg, ArgMatches};
|
||||||
|
use serde_json::Value;
|
||||||
|
use solana_clap_utils::{
|
||||||
|
input_parsers::value_of,
|
||||||
|
input_validators::{is_hash, is_pubkey_sig},
|
||||||
|
offline::{BLOCKHASH_ARG, SIGNER_ARG, SIGN_ONLY_ARG},
|
||||||
|
};
|
||||||
|
use solana_client::rpc_client::RpcClient;
|
||||||
|
use solana_sdk::{fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, signature::Signature};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum BlockhashQuery {
|
||||||
|
None(Hash, FeeCalculator),
|
||||||
|
FeeCalculator(Hash),
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockhashQuery {
|
||||||
|
pub fn new(blockhash: Option<Hash>, sign_only: bool) -> Self {
|
||||||
|
match blockhash {
|
||||||
|
Some(hash) if sign_only => Self::None(hash, FeeCalculator::default()),
|
||||||
|
Some(hash) if !sign_only => Self::FeeCalculator(hash),
|
||||||
|
None if !sign_only => Self::All,
|
||||||
|
_ => panic!("Cannot resolve blockhash"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self {
|
||||||
|
let blockhash = value_of(matches, BLOCKHASH_ARG.name);
|
||||||
|
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||||
|
BlockhashQuery::new(blockhash, sign_only)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_blockhash_fee_calculator(
|
||||||
|
&self,
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
|
||||||
|
let (hash, fee_calc) = match self {
|
||||||
|
BlockhashQuery::None(hash, fee_calc) => (Some(hash), Some(fee_calc)),
|
||||||
|
BlockhashQuery::FeeCalculator(hash) => (Some(hash), None),
|
||||||
|
BlockhashQuery::All => (None, None),
|
||||||
|
};
|
||||||
|
if None == fee_calc {
|
||||||
|
let (cluster_hash, fee_calc) = rpc_client.get_recent_blockhash()?;
|
||||||
|
Ok((*hash.unwrap_or(&cluster_hash), fee_calc))
|
||||||
|
} else {
|
||||||
|
Ok((*hash.unwrap(), fee_calc.unwrap().clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BlockhashQuery {
|
||||||
|
fn default() -> Self {
|
||||||
|
BlockhashQuery::All
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||||
|
Arg::with_name(BLOCKHASH_ARG.name)
|
||||||
|
.long(BLOCKHASH_ARG.long)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("BLOCKHASH")
|
||||||
|
.validator(is_hash)
|
||||||
|
.help(BLOCKHASH_ARG.help)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign_only_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||||
|
Arg::with_name(SIGN_ONLY_ARG.name)
|
||||||
|
.long(SIGN_ONLY_ARG.long)
|
||||||
|
.takes_value(false)
|
||||||
|
.requires(BLOCKHASH_ARG.name)
|
||||||
|
.help(SIGN_ONLY_ARG.help)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signer_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||||
|
Arg::with_name(SIGNER_ARG.name)
|
||||||
|
.long(SIGNER_ARG.long)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("BASE58_PUBKEY=BASE58_SIG")
|
||||||
|
.validator(is_pubkey_sig)
|
||||||
|
.requires(BLOCKHASH_ARG.name)
|
||||||
|
.multiple(true)
|
||||||
|
.help(SIGNER_ARG.help)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait OfflineArgs {
|
||||||
|
fn offline_args(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OfflineArgs for App<'_, '_> {
|
||||||
|
fn offline_args(self) -> Self {
|
||||||
|
self.arg(blockhash_arg())
|
||||||
|
.arg(sign_only_arg())
|
||||||
|
.arg(signer_arg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_sign_only_reply_string(reply: &str) -> (Hash, Vec<(Pubkey, Signature)>) {
|
||||||
|
let object: Value = serde_json::from_str(&reply).unwrap();
|
||||||
|
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
|
||||||
|
let blockhash = blockhash_str.parse::<Hash>().unwrap();
|
||||||
|
let signer_strings = object.get("signers").unwrap().as_array().unwrap();
|
||||||
|
let signers = signer_strings
|
||||||
|
.iter()
|
||||||
|
.map(|signer_string| {
|
||||||
|
let mut signer = signer_string.as_str().unwrap().split('=');
|
||||||
|
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
|
||||||
|
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
|
||||||
|
(key, sig)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
(blockhash, signers)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use clap::App;
|
||||||
|
use serde_json::{self, json, Value};
|
||||||
|
use solana_client::{
|
||||||
|
rpc_request::RpcRequest,
|
||||||
|
rpc_response::{Response, RpcResponseContext},
|
||||||
|
};
|
||||||
|
use solana_sdk::{fee_calculator::FeeCalculator, hash::hash};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blockhashspec_new_ok() {
|
||||||
|
let blockhash = hash(&[1u8]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::new(Some(blockhash), true),
|
||||||
|
BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::new(Some(blockhash), false),
|
||||||
|
BlockhashQuery::FeeCalculator(blockhash),
|
||||||
|
);
|
||||||
|
assert_eq!(BlockhashQuery::new(None, false), BlockhashQuery::All,);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_blockhashspec_new_fail() {
|
||||||
|
BlockhashQuery::new(None, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blockhashspec_new_from_matches_ok() {
|
||||||
|
let test_commands = App::new("blockhashspec_test").offline_args();
|
||||||
|
let blockhash = hash(&[1u8]);
|
||||||
|
let blockhash_string = blockhash.to_string();
|
||||||
|
|
||||||
|
let matches = test_commands.clone().get_matches_from(vec![
|
||||||
|
"blockhashspec_test",
|
||||||
|
"--blockhash",
|
||||||
|
&blockhash_string,
|
||||||
|
"--sign-only",
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::new_from_matches(&matches),
|
||||||
|
BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let matches = test_commands.clone().get_matches_from(vec![
|
||||||
|
"blockhashspec_test",
|
||||||
|
"--blockhash",
|
||||||
|
&blockhash_string,
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::new_from_matches(&matches),
|
||||||
|
BlockhashQuery::FeeCalculator(blockhash),
|
||||||
|
);
|
||||||
|
|
||||||
|
let matches = test_commands
|
||||||
|
.clone()
|
||||||
|
.get_matches_from(vec!["blockhashspec_test"]);
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::new_from_matches(&matches),
|
||||||
|
BlockhashQuery::All,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_blockhashspec_new_from_matches_fail() {
|
||||||
|
let test_commands = App::new("blockhashspec_test")
|
||||||
|
.arg(blockhash_arg())
|
||||||
|
// We can really only hit this case unless the arg requirements
|
||||||
|
// are broken, so unset the requires() to recreate that condition
|
||||||
|
.arg(sign_only_arg().requires(""));
|
||||||
|
|
||||||
|
let matches = test_commands
|
||||||
|
.clone()
|
||||||
|
.get_matches_from(vec!["blockhashspec_test", "--sign-only"]);
|
||||||
|
BlockhashQuery::new_from_matches(&matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blockhashspec_get_blockhash_fee_calc() {
|
||||||
|
let test_blockhash = hash(&[0u8]);
|
||||||
|
let rpc_blockhash = hash(&[1u8]);
|
||||||
|
let rpc_fee_calc = FeeCalculator::new(42, 42);
|
||||||
|
let get_recent_blockhash_response = json!(Response {
|
||||||
|
context: RpcResponseContext { slot: 1 },
|
||||||
|
value: json!((
|
||||||
|
Value::String(rpc_blockhash.to_string()),
|
||||||
|
serde_json::to_value(rpc_fee_calc.clone()).unwrap()
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
let mut mocks = HashMap::new();
|
||||||
|
mocks.insert(
|
||||||
|
RpcRequest::GetRecentBlockhash,
|
||||||
|
get_recent_blockhash_response.clone(),
|
||||||
|
);
|
||||||
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::All
|
||||||
|
.get_blockhash_fee_calculator(&rpc_client)
|
||||||
|
.unwrap(),
|
||||||
|
(rpc_blockhash, rpc_fee_calc.clone()),
|
||||||
|
);
|
||||||
|
let mut mocks = HashMap::new();
|
||||||
|
mocks.insert(
|
||||||
|
RpcRequest::GetRecentBlockhash,
|
||||||
|
get_recent_blockhash_response.clone(),
|
||||||
|
);
|
||||||
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::FeeCalculator(test_blockhash)
|
||||||
|
.get_blockhash_fee_calculator(&rpc_client)
|
||||||
|
.unwrap(),
|
||||||
|
(test_blockhash, rpc_fee_calc.clone()),
|
||||||
|
);
|
||||||
|
let mut mocks = HashMap::new();
|
||||||
|
mocks.insert(
|
||||||
|
RpcRequest::GetRecentBlockhash,
|
||||||
|
get_recent_blockhash_response.clone(),
|
||||||
|
);
|
||||||
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
|
assert_eq!(
|
||||||
|
BlockhashQuery::None(test_blockhash, FeeCalculator::default())
|
||||||
|
.get_blockhash_fee_calculator(&rpc_client)
|
||||||
|
.unwrap(),
|
||||||
|
(test_blockhash, FeeCalculator::default()),
|
||||||
|
);
|
||||||
|
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||||
|
assert!(BlockhashQuery::All
|
||||||
|
.get_blockhash_fee_calculator(&rpc_client)
|
||||||
|
.is_err());
|
||||||
|
}
|
||||||
|
}
|
2091
cli/src/stake.rs
2091
cli/src/stake.rs
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,17 @@
|
|||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, CliCommand,
|
check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, CliCommand,
|
||||||
CliCommandInfo, CliConfig, CliError, ProcessResult,
|
CliCommandInfo, CliConfig, CliError, ProcessResult, SignerIndex,
|
||||||
};
|
};
|
||||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
use solana_clap_utils::{input_parsers::*, input_validators::*};
|
use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_from_path};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut, message::Message, pubkey::Pubkey, signature::KeypairUtil,
|
account_utils::StateMut, message::Message, pubkey::Pubkey, system_instruction::SystemError,
|
||||||
system_instruction::SystemError, transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_storage_program::storage_instruction::{self, StorageAccountType};
|
use solana_storage_program::storage_instruction::{self, StorageAccountType};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait StorageSubCommands {
|
pub trait StorageSubCommands {
|
||||||
fn storage_subcommands(self) -> Self;
|
fn storage_subcommands(self) -> Self;
|
||||||
@ -99,35 +100,49 @@ impl StorageSubCommands for App<'_, '_> {
|
|||||||
|
|
||||||
pub fn parse_storage_create_archiver_account(
|
pub fn parse_storage_create_archiver_account(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
|
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
|
||||||
let storage_account = keypair_of(matches, "storage_account").unwrap();
|
let storage_account = keypair_of(matches, "storage_account").unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::CreateStorageAccount {
|
command: CliCommand::CreateStorageAccount {
|
||||||
account_owner,
|
account_owner,
|
||||||
storage_account: storage_account.into(),
|
storage_account: 1,
|
||||||
account_type: StorageAccountType::Archiver,
|
account_type: StorageAccountType::Archiver,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: vec![
|
||||||
|
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?,
|
||||||
|
storage_account.into(),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_storage_create_validator_account(
|
pub fn parse_storage_create_validator_account(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
|
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
|
||||||
let storage_account = keypair_of(matches, "storage_account").unwrap();
|
let storage_account = keypair_of(matches, "storage_account").unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::CreateStorageAccount {
|
command: CliCommand::CreateStorageAccount {
|
||||||
account_owner,
|
account_owner,
|
||||||
storage_account: storage_account.into(),
|
storage_account: 1,
|
||||||
account_type: StorageAccountType::Validator,
|
account_type: StorageAccountType::Validator,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: vec![
|
||||||
|
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?,
|
||||||
|
storage_account.into(),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_storage_claim_reward(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let node_account_pubkey = pubkey_of(matches, "node_account_pubkey").unwrap();
|
let node_account_pubkey = pubkey_of(matches, "node_account_pubkey").unwrap();
|
||||||
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
|
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
@ -135,7 +150,12 @@ pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommand
|
|||||||
node_account_pubkey,
|
node_account_pubkey,
|
||||||
storage_account_pubkey,
|
storage_account_pubkey,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: vec![signer_from_path(
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
"keypair",
|
||||||
|
wallet_manager,
|
||||||
|
)?],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,20 +165,21 @@ pub fn parse_storage_get_account_command(
|
|||||||
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
|
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowStorageAccount(storage_account_pubkey),
|
command: CliCommand::ShowStorageAccount(storage_account_pubkey),
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_create_storage_account(
|
pub fn process_create_storage_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
|
storage_account: SignerIndex,
|
||||||
account_owner: &Pubkey,
|
account_owner: &Pubkey,
|
||||||
storage_account: &Keypair,
|
|
||||||
account_type: StorageAccountType,
|
account_type: StorageAccountType,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
|
let storage_account = config.signers[storage_account];
|
||||||
let storage_account_pubkey = storage_account.pubkey();
|
let storage_account_pubkey = storage_account.pubkey();
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(&config.keypair.pubkey(), "cli keypair".to_string()),
|
(&config.signers[0].pubkey(), "cli keypair".to_string()),
|
||||||
(
|
(
|
||||||
&storage_account_pubkey,
|
&storage_account_pubkey,
|
||||||
"storage_account_pubkey".to_string(),
|
"storage_account_pubkey".to_string(),
|
||||||
@ -183,7 +204,7 @@ pub fn process_create_storage_account(
|
|||||||
.max(1);
|
.max(1);
|
||||||
|
|
||||||
let ixs = storage_instruction::create_storage_account(
|
let ixs = storage_instruction::create_storage_account(
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&account_owner,
|
&account_owner,
|
||||||
&storage_account_pubkey,
|
&storage_account_pubkey,
|
||||||
required_balance,
|
required_balance,
|
||||||
@ -191,19 +212,16 @@ pub fn process_create_storage_account(
|
|||||||
);
|
);
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
let mut tx = Transaction::new_signed_instructions(
|
let message = Message::new(ixs);
|
||||||
&[&config.keypair, &storage_account],
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
ixs,
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result =
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
|
||||||
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, &storage_account]);
|
|
||||||
log_instruction_custom_error::<SystemError>(result)
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,13 +235,13 @@ pub fn process_claim_storage_reward(
|
|||||||
|
|
||||||
let instruction =
|
let instruction =
|
||||||
storage_instruction::claim_reward(node_account_pubkey, storage_account_pubkey);
|
storage_instruction::claim_reward(node_account_pubkey, storage_account_pubkey);
|
||||||
let signers = [&config.keypair];
|
let signers = [config.signers[0]];
|
||||||
let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey()));
|
let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey()));
|
||||||
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
let mut tx = Transaction::new(&signers, message, recent_blockhash);
|
tx.try_sign(&signers, recent_blockhash)?;
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
@ -251,7 +269,7 @@ pub fn process_show_storage_account(
|
|||||||
CliError::RpcRequestError(format!("Unable to deserialize storage account: {:?}", err))
|
CliError::RpcRequestError(format!("Unable to deserialize storage account: {:?}", err))
|
||||||
})?;
|
})?;
|
||||||
println!("{:#?}", storage_contract);
|
println!("{:#?}", storage_contract);
|
||||||
println!("account lamports: {}", account.lamports);
|
println!("Account Lamports: {}", account.lamports);
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +277,7 @@ pub fn process_show_storage_account(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::cli::{app, parse_command};
|
use crate::cli::{app, parse_command};
|
||||||
use solana_sdk::signature::write_keypair;
|
use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
fn make_tmp_file() -> (String, NamedTempFile) {
|
fn make_tmp_file() -> (String, NamedTempFile) {
|
||||||
@ -273,6 +291,10 @@ mod tests {
|
|||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = Pubkey::new_rand();
|
||||||
let pubkey_string = pubkey.to_string();
|
let pubkey_string = pubkey.to_string();
|
||||||
|
|
||||||
|
let default_keypair = Keypair::new();
|
||||||
|
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
let storage_account_keypair = Keypair::new();
|
let storage_account_keypair = Keypair::new();
|
||||||
write_keypair(&storage_account_keypair, tmp_file.as_file_mut()).unwrap();
|
write_keypair(&storage_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
@ -284,14 +306,22 @@ mod tests {
|
|||||||
&keypair_file,
|
&keypair_file,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_archiver_storage_account).unwrap(),
|
parse_command(
|
||||||
|
&test_create_archiver_storage_account,
|
||||||
|
&default_keypair_file,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateStorageAccount {
|
command: CliCommand::CreateStorageAccount {
|
||||||
account_owner: pubkey,
|
account_owner: pubkey,
|
||||||
storage_account: storage_account_keypair.into(),
|
storage_account: 1,
|
||||||
account_type: StorageAccountType::Archiver,
|
account_type: StorageAccountType::Archiver,
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
storage_account_keypair.into()
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -308,14 +338,22 @@ mod tests {
|
|||||||
&keypair_file,
|
&keypair_file,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_validator_storage_account).unwrap(),
|
parse_command(
|
||||||
|
&test_create_validator_storage_account,
|
||||||
|
&default_keypair_file,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateStorageAccount {
|
command: CliCommand::CreateStorageAccount {
|
||||||
account_owner: pubkey,
|
account_owner: pubkey,
|
||||||
storage_account: storage_account_keypair.into(),
|
storage_account: 1,
|
||||||
account_type: StorageAccountType::Validator,
|
account_type: StorageAccountType::Validator,
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
storage_account_keypair.into()
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -326,13 +364,13 @@ mod tests {
|
|||||||
&storage_account_string,
|
&storage_account_string,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_claim_storage_reward).unwrap(),
|
parse_command(&test_claim_storage_reward, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::ClaimStorageReward {
|
command: CliCommand::ClaimStorageReward {
|
||||||
node_account_pubkey: pubkey,
|
node_account_pubkey: pubkey,
|
||||||
storage_account_pubkey,
|
storage_account_pubkey,
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,21 @@ use serde_json::{Map, Value};
|
|||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_parsers::pubkey_of,
|
input_parsers::pubkey_of,
|
||||||
input_validators::{is_pubkey, is_url},
|
input_validators::{is_pubkey, is_url},
|
||||||
|
keypair::signer_from_path,
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
|
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
|
||||||
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, KeypairUtil},
|
signature::{Keypair, Signer},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
|
use std::{error, sync::Arc};
|
||||||
use std::error;
|
use titlecase::titlecase;
|
||||||
|
|
||||||
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
|
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
|
||||||
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
|
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
|
||||||
@ -224,17 +226,26 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_validator_info_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_validator_info_command(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let info_pubkey = pubkey_of(matches, "info_pubkey");
|
let info_pubkey = pubkey_of(matches, "info_pubkey");
|
||||||
// Prepare validator info
|
// Prepare validator info
|
||||||
let validator_info = parse_args(&matches);
|
let validator_info = parse_args(matches);
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::SetValidatorInfo {
|
command: CliCommand::SetValidatorInfo {
|
||||||
validator_info,
|
validator_info,
|
||||||
force_keybase: matches.is_present("force"),
|
force_keybase: matches.is_present("force"),
|
||||||
info_pubkey,
|
info_pubkey,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers: vec![signer_from_path(
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
"keypair",
|
||||||
|
wallet_manager,
|
||||||
|
)?],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +255,7 @@ pub fn parse_get_validator_info_command(
|
|||||||
let info_pubkey = pubkey_of(matches, "info_pubkey");
|
let info_pubkey = pubkey_of(matches, "info_pubkey");
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetValidatorInfo(info_pubkey),
|
command: CliCommand::GetValidatorInfo(info_pubkey),
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +268,7 @@ pub fn process_set_validator_info(
|
|||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
// Validate keybase username
|
// Validate keybase username
|
||||||
if let Some(string) = validator_info.get("keybaseUsername") {
|
if let Some(string) = validator_info.get("keybaseUsername") {
|
||||||
let result = verify_keybase(&config.keypair.pubkey(), &string);
|
let result = verify_keybase(&config.signers[0].pubkey(), &string);
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
if force_keybase {
|
if force_keybase {
|
||||||
println!("--force supplied, ignoring: {:?}", result);
|
println!("--force supplied, ignoring: {:?}", result);
|
||||||
@ -282,7 +293,7 @@ pub fn process_set_validator_info(
|
|||||||
})
|
})
|
||||||
.find(|(pubkey, account)| {
|
.find(|(pubkey, account)| {
|
||||||
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
|
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
|
||||||
validator_pubkey == config.keypair.pubkey()
|
validator_pubkey == config.signers[0].pubkey()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create validator-info keypair to use if info_pubkey not provided or does not exist
|
// Create validator-info keypair to use if info_pubkey not provided or does not exist
|
||||||
@ -300,8 +311,8 @@ pub fn process_set_validator_info(
|
|||||||
.poll_get_balance_with_commitment(&info_pubkey, CommitmentConfig::default())
|
.poll_get_balance_with_commitment(&info_pubkey, CommitmentConfig::default())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let keys = vec![(id(), false), (config.keypair.pubkey(), true)];
|
let keys = vec![(id(), false), (config.signers[0].pubkey(), true)];
|
||||||
let (message, signers): (Message, Vec<&Keypair>) = if balance == 0 {
|
let (message, signers): (Message, Vec<&dyn Signer>) = if balance == 0 {
|
||||||
if info_pubkey != info_keypair.pubkey() {
|
if info_pubkey != info_keypair.pubkey() {
|
||||||
println!(
|
println!(
|
||||||
"Account {:?} does not exist. Generating new keypair...",
|
"Account {:?} does not exist. Generating new keypair...",
|
||||||
@ -311,12 +322,12 @@ pub fn process_set_validator_info(
|
|||||||
}
|
}
|
||||||
println!(
|
println!(
|
||||||
"Publishing info for Validator {:?}",
|
"Publishing info for Validator {:?}",
|
||||||
config.keypair.pubkey()
|
config.signers[0].pubkey()
|
||||||
);
|
);
|
||||||
let lamports = rpc_client
|
let lamports = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?;
|
.get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?;
|
||||||
let mut instructions = config_instruction::create_account::<ValidatorInfo>(
|
let mut instructions = config_instruction::create_account::<ValidatorInfo>(
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&info_keypair.pubkey(),
|
&info_keypair.pubkey(),
|
||||||
lamports,
|
lamports,
|
||||||
keys.clone(),
|
keys.clone(),
|
||||||
@ -327,13 +338,13 @@ pub fn process_set_validator_info(
|
|||||||
keys,
|
keys,
|
||||||
&validator_info,
|
&validator_info,
|
||||||
)]);
|
)]);
|
||||||
let signers = vec![&config.keypair, &info_keypair];
|
let signers = vec![config.signers[0], &info_keypair];
|
||||||
let message = Message::new(instructions);
|
let message = Message::new(instructions);
|
||||||
(message, signers)
|
(message, signers)
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"Updating Validator {:?} info at: {:?}",
|
"Updating Validator {:?} info at: {:?}",
|
||||||
config.keypair.pubkey(),
|
config.signers[0].pubkey(),
|
||||||
info_pubkey
|
info_pubkey
|
||||||
);
|
);
|
||||||
let instructions = vec![config_instruction::store(
|
let instructions = vec![config_instruction::store(
|
||||||
@ -342,17 +353,18 @@ pub fn process_set_validator_info(
|
|||||||
keys,
|
keys,
|
||||||
&validator_info,
|
&validator_info,
|
||||||
)];
|
)];
|
||||||
let message = Message::new_with_payer(instructions, Some(&config.keypair.pubkey()));
|
let message = Message::new_with_payer(instructions, Some(&config.signers[0].pubkey()));
|
||||||
let signers = vec![&config.keypair];
|
let signers = vec![config.signers[0]];
|
||||||
(message, signers)
|
(message, signers)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Submit transaction
|
// Submit transaction
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
let mut tx = Transaction::new(&signers, message, recent_blockhash);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
tx.try_sign(&signers, recent_blockhash)?;
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
@ -390,9 +402,12 @@ pub fn process_get_validator_info(rpc_client: &RpcClient, pubkey: Option<Pubkey>
|
|||||||
parse_validator_info(&validator_info_pubkey, &validator_info_account)?;
|
parse_validator_info(&validator_info_pubkey, &validator_info_account)?;
|
||||||
println!();
|
println!();
|
||||||
println_name_value("Validator Identity Pubkey:", &validator_pubkey.to_string());
|
println_name_value("Validator Identity Pubkey:", &validator_pubkey.to_string());
|
||||||
println_name_value(" info pubkey:", &validator_info_pubkey.to_string());
|
println_name_value(" Info Pubkey:", &validator_info_pubkey.to_string());
|
||||||
for (key, value) in validator_info.iter() {
|
for (key, value) in validator_info.iter() {
|
||||||
println_name_value(&format!(" {}:", key), &value.as_str().unwrap_or("?"));
|
println_name_value(
|
||||||
|
&format!(" {}:", titlecase(key)),
|
||||||
|
&value.as_str().unwrap_or("?"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
321
cli/src/vote.rs
321
cli/src/vote.rs
@ -1,19 +1,16 @@
|
|||||||
use crate::{
|
use crate::cli::{
|
||||||
cli::{
|
build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
|
||||||
build_balance_message, check_account_for_fee, check_unique_pubkeys,
|
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, CliSignerInfo,
|
||||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
ProcessResult,
|
||||||
ProcessResult,
|
|
||||||
},
|
|
||||||
cluster_query::aggregate_epoch_credits,
|
|
||||||
};
|
};
|
||||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||||
use solana_clap_utils::{input_parsers::*, input_validators::*};
|
use solana_clap_utils::{input_parsers::*, input_validators::*};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Keypair,
|
|
||||||
signature::KeypairUtil,
|
|
||||||
system_instruction::{create_address_with_seed, SystemError},
|
system_instruction::{create_address_with_seed, SystemError},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
@ -21,6 +18,7 @@ use solana_vote_program::{
|
|||||||
vote_instruction::{self, VoteError},
|
vote_instruction::{self, VoteError},
|
||||||
vote_state::{VoteAuthorize, VoteInit, VoteState},
|
vote_state::{VoteAuthorize, VoteInit, VoteState},
|
||||||
};
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait VoteSubCommands {
|
pub trait VoteSubCommands {
|
||||||
fn vote_subcommands(self) -> Self;
|
fn vote_subcommands(self) -> Self;
|
||||||
@ -176,84 +174,91 @@ impl VoteSubCommands for App<'_, '_> {
|
|||||||
.help("Display balance in lamports instead of SOL"),
|
.help("Display balance in lamports instead of SOL"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
|
||||||
SubCommand::with_name("uptime")
|
|
||||||
.about("Show the uptime of a validator, based on epoch voting history")
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("vote_account_pubkey")
|
|
||||||
.index(1)
|
|
||||||
.value_name("VOTE ACCOUNT PUBKEY")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.validator(is_pubkey_or_keypair)
|
|
||||||
.help("Vote account pubkey"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("span")
|
|
||||||
.long("span")
|
|
||||||
.value_name("NUM OF EPOCHS")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("Number of recent epochs to examine"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("aggregate")
|
|
||||||
.long("aggregate")
|
|
||||||
.help("Aggregate uptime data across span"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_vote_create_account(
|
||||||
let vote_account = keypair_of(matches, "vote_account").unwrap();
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let (vote_account, _) = signer_of(matches, "vote_account", wallet_manager)?;
|
||||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||||
let identity_pubkey = pubkey_of(matches, "identity_pubkey").unwrap();
|
let identity_pubkey = pubkey_of(matches, "identity_pubkey").unwrap();
|
||||||
let commission = value_t_or_exit!(matches, "commission", u8);
|
let commission = value_t_or_exit!(matches, "commission", u8);
|
||||||
let authorized_voter = pubkey_of(matches, "authorized_voter");
|
let authorized_voter = pubkey_of(matches, "authorized_voter");
|
||||||
let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer");
|
let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer");
|
||||||
|
|
||||||
|
let payer_provided = None;
|
||||||
|
let CliSignerInfo { signers } = generate_unique_signers(
|
||||||
|
vec![payer_provided, vote_account],
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::CreateVoteAccount {
|
command: CliCommand::CreateVoteAccount {
|
||||||
vote_account: vote_account.into(),
|
|
||||||
seed,
|
seed,
|
||||||
node_pubkey: identity_pubkey,
|
node_pubkey: identity_pubkey,
|
||||||
authorized_voter,
|
authorized_voter,
|
||||||
authorized_withdrawer,
|
authorized_withdrawer,
|
||||||
commission,
|
commission,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_vote_authorize(
|
pub fn parse_vote_authorize(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
vote_authorize: VoteAuthorize,
|
vote_authorize: VoteAuthorize,
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
||||||
let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap();
|
let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap();
|
||||||
|
|
||||||
|
let authorized_voter_provided = None;
|
||||||
|
let CliSignerInfo { signers } = generate_unique_signers(
|
||||||
|
vec![authorized_voter_provided],
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::VoteAuthorize {
|
command: CliCommand::VoteAuthorize {
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
new_authorized_pubkey,
|
new_authorized_pubkey,
|
||||||
vote_authorize,
|
vote_authorize,
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_vote_update_validator(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_vote_update_validator(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
||||||
let new_identity_pubkey = pubkey_of(matches, "new_identity_pubkey").unwrap();
|
let new_identity_pubkey = pubkey_of(matches, "new_identity_pubkey").unwrap();
|
||||||
let authorized_voter = keypair_of(matches, "authorized_voter").unwrap();
|
let (authorized_voter, _) = signer_of(matches, "authorized_voter", wallet_manager)?;
|
||||||
|
|
||||||
|
let payer_provided = None;
|
||||||
|
let CliSignerInfo { signers } = generate_unique_signers(
|
||||||
|
vec![payer_provided, authorized_voter],
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::VoteUpdateValidator {
|
command: CliCommand::VoteUpdateValidator {
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
new_identity_pubkey,
|
new_identity_pubkey,
|
||||||
authorized_voter: authorized_voter.into(),
|
|
||||||
},
|
},
|
||||||
require_keypair: true,
|
signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,38 +272,20 @@ pub fn parse_vote_get_account_command(
|
|||||||
pubkey: vote_account_pubkey,
|
pubkey: vote_account_pubkey,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
},
|
},
|
||||||
require_keypair: false,
|
signers: vec![],
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_vote_uptime_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
||||||
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
|
||||||
let aggregate = matches.is_present("aggregate");
|
|
||||||
let span = if matches.is_present("span") {
|
|
||||||
Some(value_t_or_exit!(matches, "span", u64))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
Ok(CliCommandInfo {
|
|
||||||
command: CliCommand::Uptime {
|
|
||||||
pubkey: vote_account_pubkey,
|
|
||||||
aggregate,
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
require_keypair: false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_create_vote_account(
|
pub fn process_create_vote_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
vote_account: &Keypair,
|
|
||||||
seed: &Option<String>,
|
seed: &Option<String>,
|
||||||
identity_pubkey: &Pubkey,
|
identity_pubkey: &Pubkey,
|
||||||
authorized_voter: &Option<Pubkey>,
|
authorized_voter: &Option<Pubkey>,
|
||||||
authorized_withdrawer: &Option<Pubkey>,
|
authorized_withdrawer: &Option<Pubkey>,
|
||||||
commission: u8,
|
commission: u8,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
|
let vote_account = config.signers[1];
|
||||||
let vote_account_pubkey = vote_account.pubkey();
|
let vote_account_pubkey = vote_account.pubkey();
|
||||||
let vote_account_address = if let Some(seed) = seed {
|
let vote_account_address = if let Some(seed) = seed {
|
||||||
create_address_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
|
create_address_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
|
||||||
@ -306,7 +293,7 @@ pub fn process_create_vote_account(
|
|||||||
vote_account_pubkey
|
vote_account_pubkey
|
||||||
};
|
};
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(&config.keypair.pubkey(), "cli keypair".to_string()),
|
(&config.signers[0].pubkey(), "cli keypair".to_string()),
|
||||||
(&vote_account_address, "vote_account".to_string()),
|
(&vote_account_address, "vote_account".to_string()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -340,16 +327,16 @@ pub fn process_create_vote_account(
|
|||||||
|
|
||||||
let ixs = if let Some(seed) = seed {
|
let ixs = if let Some(seed) = seed {
|
||||||
vote_instruction::create_account_with_seed(
|
vote_instruction::create_account_with_seed(
|
||||||
&config.keypair.pubkey(), // from
|
&config.signers[0].pubkey(), // from
|
||||||
&vote_account_address, // to
|
&vote_account_address, // to
|
||||||
&vote_account_pubkey, // base
|
&vote_account_pubkey, // base
|
||||||
seed, // seed
|
seed, // seed
|
||||||
&vote_init,
|
&vote_init,
|
||||||
required_balance,
|
required_balance,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
vote_instruction::create_account(
|
vote_instruction::create_account(
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
&vote_init,
|
&vote_init,
|
||||||
required_balance,
|
required_balance,
|
||||||
@ -357,20 +344,16 @@ pub fn process_create_vote_account(
|
|||||||
};
|
};
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
let signers = if vote_account_pubkey != config.keypair.pubkey() {
|
let message = Message::new(ixs);
|
||||||
vec![&config.keypair, vote_account] // both must sign if `from` and `to` differ
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
} else {
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
vec![&config.keypair] // when stake_account == config.keypair and there's a seed, we only need one signature
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tx = Transaction::new_signed_instructions(&signers, ixs, recent_blockhash);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &signers);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
|
||||||
log_instruction_custom_error::<SystemError>(result)
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,25 +370,22 @@ pub fn process_vote_authorize(
|
|||||||
)?;
|
)?;
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
let ixs = vec![vote_instruction::authorize(
|
let ixs = vec![vote_instruction::authorize(
|
||||||
vote_account_pubkey, // vote account to update
|
vote_account_pubkey, // vote account to update
|
||||||
&config.keypair.pubkey(), // current authorized voter
|
&config.signers[0].pubkey(), // current authorized voter
|
||||||
new_authorized_pubkey, // new vote signer/withdrawer
|
new_authorized_pubkey, // new vote signer/withdrawer
|
||||||
vote_authorize, // vote or withdraw
|
vote_authorize, // vote or withdraw
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let mut tx = Transaction::new_signed_with_payer(
|
let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
|
||||||
ixs,
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
Some(&config.keypair.pubkey()),
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
&[&config.keypair],
|
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[config.signers[0]]);
|
||||||
log_instruction_custom_error::<VoteError>(result)
|
log_instruction_custom_error::<VoteError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,8 +394,8 @@ pub fn process_vote_update_validator(
|
|||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
new_identity_pubkey: &Pubkey,
|
new_identity_pubkey: &Pubkey,
|
||||||
authorized_voter: &Keypair,
|
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
|
let authorized_voter = config.signers[1];
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
||||||
(new_identity_pubkey, "new_identity_pubkey".to_string()),
|
(new_identity_pubkey, "new_identity_pubkey".to_string()),
|
||||||
@ -427,19 +407,16 @@ pub fn process_vote_update_validator(
|
|||||||
new_identity_pubkey,
|
new_identity_pubkey,
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let mut tx = Transaction::new_signed_with_payer(
|
let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
|
||||||
ixs,
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
Some(&config.keypair.pubkey()),
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
&[&config.keypair, authorized_voter],
|
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
check_account_for_fee(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
|
||||||
log_instruction_custom_error::<VoteError>(result)
|
log_instruction_custom_error::<VoteError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,25 +453,25 @@ pub fn process_show_vote_account(
|
|||||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"account balance: {}",
|
"Account Balance: {}",
|
||||||
build_balance_message(vote_account.lamports, use_lamports_unit, true)
|
build_balance_message(vote_account.lamports, use_lamports_unit, true)
|
||||||
);
|
);
|
||||||
println!("validator identity: {}", vote_state.node_pubkey);
|
println!("Validator Identity: {}", vote_state.node_pubkey);
|
||||||
println!("authorized voter: {}", vote_state.authorized_voter);
|
println!("Authorized Voter: {}", vote_state.authorized_voter);
|
||||||
println!(
|
println!(
|
||||||
"authorized withdrawer: {}",
|
"Authorized Withdrawer: {}",
|
||||||
vote_state.authorized_withdrawer
|
vote_state.authorized_withdrawer
|
||||||
);
|
);
|
||||||
println!("credits: {}", vote_state.credits());
|
println!("Credits: {}", vote_state.credits());
|
||||||
println!("commission: {}%", vote_state.commission);
|
println!("Commission: {}%", vote_state.commission);
|
||||||
println!(
|
println!(
|
||||||
"root slot: {}",
|
"Root Slot: {}",
|
||||||
match vote_state.root_slot {
|
match vote_state.root_slot {
|
||||||
Some(slot) => slot.to_string(),
|
Some(slot) => slot.to_string(),
|
||||||
None => "~".to_string(),
|
None => "~".to_string(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
println!("recent timestamp: {:?}", vote_state.last_timestamp);
|
println!("Recent Timestamp: {:?}", vote_state.last_timestamp);
|
||||||
if !vote_state.votes.is_empty() {
|
if !vote_state.votes.is_empty() {
|
||||||
println!("recent votes:");
|
println!("recent votes:");
|
||||||
for vote in &vote_state.votes {
|
for vote in &vote_state.votes {
|
||||||
@ -504,7 +481,7 @@ pub fn process_show_vote_account(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("epoch voting history:");
|
println!("Epoch Voting History:");
|
||||||
for (epoch, credits, prev_credits) in vote_state.epoch_credits() {
|
for (epoch, credits, prev_credits) in vote_state.epoch_credits() {
|
||||||
let credits_earned = credits - prev_credits;
|
let credits_earned = credits - prev_credits;
|
||||||
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
|
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
|
||||||
@ -517,65 +494,11 @@ pub fn process_show_vote_account(
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_uptime(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
_config: &CliConfig,
|
|
||||||
vote_account_pubkey: &Pubkey,
|
|
||||||
aggregate: bool,
|
|
||||||
span: Option<u64>,
|
|
||||||
) -> ProcessResult {
|
|
||||||
let (_vote_account, vote_state) = get_vote_account(rpc_client, vote_account_pubkey)?;
|
|
||||||
|
|
||||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
|
||||||
|
|
||||||
println!("validator identity: {}", vote_state.node_pubkey);
|
|
||||||
println!("authorized voter: {}", vote_state.authorized_voter);
|
|
||||||
if !vote_state.votes.is_empty() {
|
|
||||||
println!("uptime:");
|
|
||||||
|
|
||||||
let epoch_credits: Vec<(u64, u64, u64)> = if let Some(x) = span {
|
|
||||||
vote_state
|
|
||||||
.epoch_credits()
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.take(x as usize)
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
vote_state.epoch_credits().iter().rev().cloned().collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
if aggregate {
|
|
||||||
let (total_credits, total_slots, epochs) =
|
|
||||||
aggregate_epoch_credits(&epoch_credits, &epoch_schedule);
|
|
||||||
if total_slots > 0 {
|
|
||||||
let total_uptime = 100_f64 * total_credits as f64 / total_slots as f64;
|
|
||||||
println!("{:.2}% over {} epochs", total_uptime, epochs);
|
|
||||||
} else {
|
|
||||||
println!("Insufficient voting history available");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (epoch, credits, prev_credits) in epoch_credits {
|
|
||||||
let credits_earned = credits - prev_credits;
|
|
||||||
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch);
|
|
||||||
let uptime = credits_earned as f64 / slots_in_epoch as f64;
|
|
||||||
println!("- epoch: {} {:.2}% uptime", epoch, uptime * 100_f64,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(x) = span {
|
|
||||||
if x > vote_state.epoch_credits().len() as u64 {
|
|
||||||
println!("(span longer than available epochs)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::cli::{app, parse_command};
|
use crate::cli::{app, parse_command};
|
||||||
use solana_sdk::signature::write_keypair;
|
use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
fn make_tmp_file() -> (String, NamedTempFile) {
|
fn make_tmp_file() -> (String, NamedTempFile) {
|
||||||
@ -593,6 +516,10 @@ mod tests {
|
|||||||
let pubkey2 = keypair2.pubkey();
|
let pubkey2 = keypair2.pubkey();
|
||||||
let pubkey2_string = pubkey2.to_string();
|
let pubkey2_string = pubkey2.to_string();
|
||||||
|
|
||||||
|
let default_keypair = Keypair::new();
|
||||||
|
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
"vote-authorize-voter",
|
"vote-authorize-voter",
|
||||||
@ -600,14 +527,14 @@ mod tests {
|
|||||||
&pubkey2_string,
|
&pubkey2_string,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_authorize_voter).unwrap(),
|
parse_command(&test_authorize_voter, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::VoteAuthorize {
|
command: CliCommand::VoteAuthorize {
|
||||||
vote_account_pubkey: pubkey,
|
vote_account_pubkey: pubkey,
|
||||||
new_authorized_pubkey: pubkey2,
|
new_authorized_pubkey: pubkey2,
|
||||||
vote_authorize: VoteAuthorize::Voter
|
vote_authorize: VoteAuthorize::Voter
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -626,17 +553,19 @@ mod tests {
|
|||||||
"10",
|
"10",
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_vote_account).unwrap(),
|
parse_command(&test_create_vote_account, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateVoteAccount {
|
command: CliCommand::CreateVoteAccount {
|
||||||
vote_account: keypair.into(),
|
|
||||||
seed: None,
|
seed: None,
|
||||||
node_pubkey,
|
node_pubkey,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
authorized_withdrawer: None,
|
authorized_withdrawer: None,
|
||||||
commission: 10,
|
commission: 10,
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
Box::new(keypair)
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -651,17 +580,19 @@ mod tests {
|
|||||||
&node_pubkey_string,
|
&node_pubkey_string,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_vote_account2).unwrap(),
|
parse_command(&test_create_vote_account2, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateVoteAccount {
|
command: CliCommand::CreateVoteAccount {
|
||||||
vote_account: keypair.into(),
|
|
||||||
seed: None,
|
seed: None,
|
||||||
node_pubkey,
|
node_pubkey,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
authorized_withdrawer: None,
|
authorized_withdrawer: None,
|
||||||
commission: 100,
|
commission: 100,
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
Box::new(keypair)
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -680,17 +611,19 @@ mod tests {
|
|||||||
&authed.to_string(),
|
&authed.to_string(),
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_vote_account3).unwrap(),
|
parse_command(&test_create_vote_account3, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateVoteAccount {
|
command: CliCommand::CreateVoteAccount {
|
||||||
vote_account: keypair.into(),
|
|
||||||
seed: None,
|
seed: None,
|
||||||
node_pubkey,
|
node_pubkey,
|
||||||
authorized_voter: Some(authed),
|
authorized_voter: Some(authed),
|
||||||
authorized_withdrawer: None,
|
authorized_withdrawer: None,
|
||||||
commission: 100
|
commission: 100
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
Box::new(keypair)
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -707,17 +640,19 @@ mod tests {
|
|||||||
&authed.to_string(),
|
&authed.to_string(),
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_vote_account4).unwrap(),
|
parse_command(&test_create_vote_account4, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateVoteAccount {
|
command: CliCommand::CreateVoteAccount {
|
||||||
vote_account: keypair.into(),
|
|
||||||
seed: None,
|
seed: None,
|
||||||
node_pubkey,
|
node_pubkey,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
authorized_withdrawer: Some(authed),
|
authorized_withdrawer: Some(authed),
|
||||||
commission: 100
|
commission: 100
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
Box::new(keypair)
|
||||||
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -729,38 +664,16 @@ mod tests {
|
|||||||
&keypair_file,
|
&keypair_file,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_update_validator).unwrap(),
|
parse_command(&test_update_validator, &default_keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::VoteUpdateValidator {
|
command: CliCommand::VoteUpdateValidator {
|
||||||
vote_account_pubkey: pubkey,
|
vote_account_pubkey: pubkey,
|
||||||
new_identity_pubkey: pubkey2,
|
new_identity_pubkey: pubkey2,
|
||||||
authorized_voter: solana_sdk::signature::read_keypair_file(&keypair_file)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
},
|
},
|
||||||
require_keypair: true
|
signers: vec![
|
||||||
}
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
);
|
Box::new(read_keypair_file(&keypair_file).unwrap())
|
||||||
|
],
|
||||||
// Test Uptime Subcommand
|
|
||||||
let pubkey = Pubkey::new_rand();
|
|
||||||
let matches = test_commands.clone().get_matches_from(vec![
|
|
||||||
"test",
|
|
||||||
"uptime",
|
|
||||||
&pubkey.to_string(),
|
|
||||||
"--span",
|
|
||||||
"4",
|
|
||||||
"--aggregate",
|
|
||||||
]);
|
|
||||||
assert_eq!(
|
|
||||||
parse_command(&matches).unwrap(),
|
|
||||||
CliCommandInfo {
|
|
||||||
command: CliCommand::Uptime {
|
|
||||||
pubkey,
|
|
||||||
aggregate: true,
|
|
||||||
span: Some(4)
|
|
||||||
},
|
|
||||||
require_keypair: false
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
|||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::validator::new_validator_for_tests;
|
use solana_core::validator::new_validator_for_tests;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{bpf_loader, pubkey::Pubkey};
|
use solana_sdk::{bpf_loader, pubkey::Pubkey, signature::Keypair};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{remove_dir_all, File},
|
fs::{remove_dir_all, File},
|
||||||
io::Read,
|
io::Read,
|
||||||
@ -38,13 +38,15 @@ fn test_cli_deploy_program() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut config = CliConfig::default();
|
let mut config = CliConfig::default();
|
||||||
|
let keypair = Keypair::new();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.command = CliCommand::Airdrop {
|
config.command = CliCommand::Airdrop {
|
||||||
faucet_host: None,
|
faucet_host: None,
|
||||||
faucet_port: faucet_addr.port(),
|
faucet_port: faucet_addr.port(),
|
||||||
|
pubkey: None,
|
||||||
lamports: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing
|
lamports: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing
|
||||||
use_lamports_unit: true,
|
|
||||||
};
|
};
|
||||||
|
config.signers = vec![&keypair];
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
config.command = CliCommand::Deploy(pathbuf.to_str().unwrap().to_string());
|
config.command = CliCommand::Deploy(pathbuf.to_str().unwrap().to_string());
|
||||||
|
@ -1,29 +1,17 @@
|
|||||||
use solana_cli::cli::{
|
use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig};
|
||||||
process_command, request_and_confirm_airdrop, CliCommand, CliConfig, SigningAuthority,
|
|
||||||
};
|
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{read_keypair_file, write_keypair, Keypair, KeypairUtil},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
system_instruction::create_address_with_seed,
|
system_instruction::create_address_with_seed,
|
||||||
system_program,
|
system_program,
|
||||||
};
|
};
|
||||||
use std::fs::remove_dir_all;
|
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
|
||||||
use std::sync::mpsc::channel;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use solana_core::validator::new_validator_for_tests;
|
use solana_core::validator::new_validator_for_tests;
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
|
|
||||||
fn make_tmp_file() -> (String, NamedTempFile) {
|
|
||||||
let tmp_file = NamedTempFile::new().unwrap();
|
|
||||||
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
||||||
(0..5).for_each(|tries| {
|
(0..5).for_each(|tries| {
|
||||||
@ -46,26 +34,9 @@ fn test_nonce() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let mut config_payer = CliConfig::default();
|
full_battery_tests(&rpc_client, &faucet_addr, json_rpc_url, None, false);
|
||||||
config_payer.json_rpc_url =
|
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
|
||||||
|
|
||||||
let mut config_nonce = CliConfig::default();
|
|
||||||
config_nonce.json_rpc_url =
|
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
|
||||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
|
||||||
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
|
|
||||||
|
|
||||||
full_battery_tests(
|
|
||||||
&rpc_client,
|
|
||||||
&faucet_addr,
|
|
||||||
&mut config_payer,
|
|
||||||
&mut config_nonce,
|
|
||||||
&keypair_file,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@ -79,25 +50,14 @@ fn test_nonce_with_seed() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
let mut config_payer = CliConfig::default();
|
|
||||||
config_payer.json_rpc_url =
|
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
|
||||||
|
|
||||||
let mut config_nonce = CliConfig::default();
|
|
||||||
config_nonce.json_rpc_url =
|
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
|
||||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
|
||||||
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
|
|
||||||
|
|
||||||
full_battery_tests(
|
full_battery_tests(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&mut config_payer,
|
json_rpc_url,
|
||||||
&mut config_nonce,
|
|
||||||
&keypair_file,
|
|
||||||
Some(String::from("seed")),
|
Some(String::from("seed")),
|
||||||
None,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
@ -112,78 +72,73 @@ fn test_nonce_with_authority() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let mut config_payer = CliConfig::default();
|
full_battery_tests(&rpc_client, &faucet_addr, json_rpc_url, None, true);
|
||||||
config_payer.json_rpc_url =
|
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
|
||||||
|
|
||||||
let mut config_nonce = CliConfig::default();
|
|
||||||
config_nonce.json_rpc_url =
|
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
|
||||||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
|
||||||
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
|
|
||||||
|
|
||||||
let nonce_authority = Keypair::new();
|
|
||||||
let (authority_keypair_file, mut tmp_file2) = make_tmp_file();
|
|
||||||
write_keypair(&nonce_authority, tmp_file2.as_file_mut()).unwrap();
|
|
||||||
|
|
||||||
full_battery_tests(
|
|
||||||
&rpc_client,
|
|
||||||
&faucet_addr,
|
|
||||||
&mut config_payer,
|
|
||||||
&mut config_nonce,
|
|
||||||
&nonce_keypair_file,
|
|
||||||
None,
|
|
||||||
Some(&authority_keypair_file),
|
|
||||||
);
|
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_keypair_from_option(keypair_file: &Option<&str>) -> Option<SigningAuthority> {
|
|
||||||
keypair_file.map(|akf| read_keypair_file(&akf).unwrap().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn full_battery_tests(
|
fn full_battery_tests(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
faucet_addr: &std::net::SocketAddr,
|
faucet_addr: &std::net::SocketAddr,
|
||||||
config_payer: &mut CliConfig,
|
json_rpc_url: String,
|
||||||
config_nonce: &mut CliConfig,
|
|
||||||
nonce_keypair_file: &str,
|
|
||||||
seed: Option<String>,
|
seed: Option<String>,
|
||||||
authority_keypair_file: Option<&str>,
|
use_nonce_authority: bool,
|
||||||
) {
|
) {
|
||||||
|
let mut config_payer = CliConfig::default();
|
||||||
|
config_payer.json_rpc_url = json_rpc_url.clone();
|
||||||
|
let payer = Keypair::new();
|
||||||
|
config_payer.signers = vec![&payer];
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_payer.keypair.pubkey(),
|
&config_payer.signers[0].pubkey(),
|
||||||
2000,
|
2000,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_balance(2000, &rpc_client, &config_payer.keypair.pubkey());
|
check_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
|
|
||||||
|
let mut config_nonce = CliConfig::default();
|
||||||
|
config_nonce.json_rpc_url = json_rpc_url;
|
||||||
|
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
|
config_nonce.signers = vec![&nonce_keypair];
|
||||||
|
|
||||||
let nonce_account = if let Some(seed) = seed.as_ref() {
|
let nonce_account = if let Some(seed) = seed.as_ref() {
|
||||||
create_address_with_seed(&config_nonce.keypair.pubkey(), seed, &system_program::id())
|
create_address_with_seed(
|
||||||
.unwrap()
|
&config_nonce.signers[0].pubkey(),
|
||||||
|
seed,
|
||||||
|
&system_program::id(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
} else {
|
} else {
|
||||||
config_nonce.keypair.pubkey()
|
nonce_keypair.pubkey()
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonce_authority = Keypair::new();
|
||||||
|
let optional_authority = if use_nonce_authority {
|
||||||
|
Some(nonce_authority.pubkey())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
|
config_payer.signers.push(&nonce_keypair);
|
||||||
config_payer.command = CliCommand::CreateNonceAccount {
|
config_payer.command = CliCommand::CreateNonceAccount {
|
||||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
nonce_account: 1,
|
||||||
seed,
|
seed,
|
||||||
nonce_authority: read_keypair_from_option(&authority_keypair_file)
|
nonce_authority: optional_authority,
|
||||||
.map(|na: SigningAuthority| na.pubkey()),
|
|
||||||
lamports: 1000,
|
lamports: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey());
|
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
check_balance(1000, &rpc_client, &nonce_account);
|
check_balance(1000, &rpc_client, &nonce_account);
|
||||||
|
|
||||||
// Get nonce
|
// Get nonce
|
||||||
|
config_payer.signers.pop();
|
||||||
config_payer.command = CliCommand::GetNonce(nonce_account);
|
config_payer.command = CliCommand::GetNonce(nonce_account);
|
||||||
let first_nonce_string = process_command(&config_payer).unwrap();
|
let first_nonce_string = process_command(&config_payer).unwrap();
|
||||||
let first_nonce = first_nonce_string.parse::<Hash>().unwrap();
|
let first_nonce = first_nonce_string.parse::<Hash>().unwrap();
|
||||||
@ -195,14 +150,24 @@ fn full_battery_tests(
|
|||||||
|
|
||||||
assert_eq!(first_nonce, second_nonce);
|
assert_eq!(first_nonce, second_nonce);
|
||||||
|
|
||||||
|
let mut authorized_signers: Vec<&dyn Signer> = vec![&payer];
|
||||||
|
let index = if use_nonce_authority {
|
||||||
|
authorized_signers.push(&nonce_authority);
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
// New nonce
|
// New nonce
|
||||||
|
config_payer.signers = authorized_signers.clone();
|
||||||
config_payer.command = CliCommand::NewNonce {
|
config_payer.command = CliCommand::NewNonce {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: read_keypair_from_option(&authority_keypair_file),
|
nonce_authority: index,
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
|
|
||||||
// Get nonce
|
// Get nonce
|
||||||
|
config_payer.signers = vec![&payer];
|
||||||
config_payer.command = CliCommand::GetNonce(nonce_account);
|
config_payer.command = CliCommand::GetNonce(nonce_account);
|
||||||
let third_nonce_string = process_command(&config_payer).unwrap();
|
let third_nonce_string = process_command(&config_payer).unwrap();
|
||||||
let third_nonce = third_nonce_string.parse::<Hash>().unwrap();
|
let third_nonce = third_nonce_string.parse::<Hash>().unwrap();
|
||||||
@ -211,14 +176,15 @@ fn full_battery_tests(
|
|||||||
|
|
||||||
// Withdraw from nonce account
|
// Withdraw from nonce account
|
||||||
let payee_pubkey = Pubkey::new_rand();
|
let payee_pubkey = Pubkey::new_rand();
|
||||||
|
config_payer.signers = authorized_signers;
|
||||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: read_keypair_from_option(&authority_keypair_file),
|
nonce_authority: index,
|
||||||
destination_account_pubkey: payee_pubkey,
|
destination_account_pubkey: payee_pubkey,
|
||||||
lamports: 100,
|
lamports: 100,
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey());
|
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
check_balance(900, &rpc_client, &nonce_account);
|
check_balance(900, &rpc_client, &nonce_account);
|
||||||
check_balance(100, &rpc_client, &payee_pubkey);
|
check_balance(100, &rpc_client, &payee_pubkey);
|
||||||
|
|
||||||
@ -231,48 +197,37 @@ fn full_battery_tests(
|
|||||||
|
|
||||||
// Set new authority
|
// Set new authority
|
||||||
let new_authority = Keypair::new();
|
let new_authority = Keypair::new();
|
||||||
let (new_authority_keypair_file, mut tmp_file) = make_tmp_file();
|
|
||||||
write_keypair(&new_authority, tmp_file.as_file_mut()).unwrap();
|
|
||||||
config_payer.command = CliCommand::AuthorizeNonceAccount {
|
config_payer.command = CliCommand::AuthorizeNonceAccount {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: read_keypair_from_option(&authority_keypair_file),
|
nonce_authority: index,
|
||||||
new_authority: read_keypair_file(&new_authority_keypair_file)
|
new_authority: new_authority.pubkey(),
|
||||||
.unwrap()
|
|
||||||
.pubkey(),
|
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
|
|
||||||
// Old authority fails now
|
// Old authority fails now
|
||||||
config_payer.command = CliCommand::NewNonce {
|
config_payer.command = CliCommand::NewNonce {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: read_keypair_from_option(&authority_keypair_file),
|
nonce_authority: index,
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap_err();
|
process_command(&config_payer).unwrap_err();
|
||||||
|
|
||||||
// New authority can advance nonce
|
// New authority can advance nonce
|
||||||
|
config_payer.signers = vec![&payer, &new_authority];
|
||||||
config_payer.command = CliCommand::NewNonce {
|
config_payer.command = CliCommand::NewNonce {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: Some(
|
nonce_authority: 1,
|
||||||
read_keypair_file(&new_authority_keypair_file)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
|
|
||||||
// New authority can withdraw from nonce account
|
// New authority can withdraw from nonce account
|
||||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: Some(
|
nonce_authority: 1,
|
||||||
read_keypair_file(&new_authority_keypair_file)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
destination_account_pubkey: payee_pubkey,
|
destination_account_pubkey: payee_pubkey,
|
||||||
lamports: 100,
|
lamports: 100,
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey());
|
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
check_balance(800, &rpc_client, &nonce_account);
|
check_balance(800, &rpc_client, &nonce_account);
|
||||||
check_balance(200, &rpc_client, &payee_pubkey);
|
check_balance(200, &rpc_client, &payee_pubkey);
|
||||||
}
|
}
|
||||||
|
143
cli/tests/pay.rs
143
cli/tests/pay.rs
@ -1,31 +1,23 @@
|
|||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use solana_cli::cli::{
|
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||||
process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand,
|
use solana_cli::{
|
||||||
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
||||||
|
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
hash::Hash,
|
fee_calculator::FeeCalculator,
|
||||||
nonce_state::NonceState,
|
nonce_state::NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{read_keypair_file, write_keypair, Keypair, KeypairUtil, Signature},
|
signature::{Keypair, Signer},
|
||||||
};
|
};
|
||||||
use std::fs::remove_dir_all;
|
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::mpsc::channel;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use solana_core::validator::new_validator_for_tests;
|
use solana_core::validator::new_validator_for_tests;
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
|
|
||||||
fn make_tmp_file() -> (String, NamedTempFile) {
|
|
||||||
let tmp_file = NamedTempFile::new().unwrap();
|
|
||||||
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
||||||
(0..5).for_each(|tries| {
|
(0..5).for_each(|tries| {
|
||||||
@ -50,32 +42,36 @@ fn test_cli_timestamp_tx() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let default_signer0 = Keypair::new();
|
||||||
|
let default_signer1 = Keypair::new();
|
||||||
|
|
||||||
let mut config_payer = CliConfig::default();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
config_payer.signers = vec![&default_signer0];
|
||||||
|
|
||||||
let mut config_witness = CliConfig::default();
|
let mut config_witness = CliConfig::default();
|
||||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||||
|
config_witness.signers = vec![&default_signer1];
|
||||||
|
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
config_payer.keypair.pubkey(),
|
config_payer.signers[0].pubkey(),
|
||||||
config_witness.keypair.pubkey()
|
config_witness.signers[0].pubkey()
|
||||||
);
|
);
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_payer.keypair.pubkey(),
|
&config_payer.signers[0].pubkey(),
|
||||||
50,
|
50,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_balance(50, &rpc_client, &config_payer.keypair.pubkey());
|
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_witness.keypair.pubkey(),
|
&config_witness.signers[0].pubkey(),
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -87,7 +83,7 @@ fn test_cli_timestamp_tx() {
|
|||||||
lamports: 10,
|
lamports: 10,
|
||||||
to: bob_pubkey,
|
to: bob_pubkey,
|
||||||
timestamp: Some(dt),
|
timestamp: Some(dt),
|
||||||
timestamp_pubkey: Some(config_witness.keypair.pubkey()),
|
timestamp_pubkey: Some(config_witness.signers[0].pubkey()),
|
||||||
..PayCommand::default()
|
..PayCommand::default()
|
||||||
});
|
});
|
||||||
let sig_response = process_command(&config_payer);
|
let sig_response = process_command(&config_payer);
|
||||||
@ -99,7 +95,7 @@ fn test_cli_timestamp_tx() {
|
|||||||
.expect("base58-encoded public key");
|
.expect("base58-encoded public key");
|
||||||
let process_id = Pubkey::new(&process_id_vec);
|
let process_id = Pubkey::new(&process_id_vec);
|
||||||
|
|
||||||
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_balance(10, &rpc_client, &process_id); // contract balance
|
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
@ -107,7 +103,7 @@ fn test_cli_timestamp_tx() {
|
|||||||
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
|
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
|
||||||
process_command(&config_witness).unwrap();
|
process_command(&config_witness).unwrap();
|
||||||
|
|
||||||
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_balance(0, &rpc_client, &process_id); // contract balance
|
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||||
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
@ -125,30 +121,34 @@ fn test_cli_witness_tx() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let default_signer0 = Keypair::new();
|
||||||
|
let default_signer1 = Keypair::new();
|
||||||
|
|
||||||
let mut config_payer = CliConfig::default();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
config_payer.signers = vec![&default_signer0];
|
||||||
|
|
||||||
let mut config_witness = CliConfig::default();
|
let mut config_witness = CliConfig::default();
|
||||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||||
|
config_witness.signers = vec![&default_signer1];
|
||||||
|
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
config_payer.keypair.pubkey(),
|
config_payer.signers[0].pubkey(),
|
||||||
config_witness.keypair.pubkey()
|
config_witness.signers[0].pubkey()
|
||||||
);
|
);
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_payer.keypair.pubkey(),
|
&config_payer.signers[0].pubkey(),
|
||||||
50,
|
50,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_witness.keypair.pubkey(),
|
&config_witness.signers[0].pubkey(),
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -157,7 +157,7 @@ fn test_cli_witness_tx() {
|
|||||||
config_payer.command = CliCommand::Pay(PayCommand {
|
config_payer.command = CliCommand::Pay(PayCommand {
|
||||||
lamports: 10,
|
lamports: 10,
|
||||||
to: bob_pubkey,
|
to: bob_pubkey,
|
||||||
witnesses: Some(vec![config_witness.keypair.pubkey()]),
|
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
|
||||||
..PayCommand::default()
|
..PayCommand::default()
|
||||||
});
|
});
|
||||||
let sig_response = process_command(&config_payer);
|
let sig_response = process_command(&config_payer);
|
||||||
@ -169,7 +169,7 @@ fn test_cli_witness_tx() {
|
|||||||
.expect("base58-encoded public key");
|
.expect("base58-encoded public key");
|
||||||
let process_id = Pubkey::new(&process_id_vec);
|
let process_id = Pubkey::new(&process_id_vec);
|
||||||
|
|
||||||
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_balance(10, &rpc_client, &process_id); // contract balance
|
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ fn test_cli_witness_tx() {
|
|||||||
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
|
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
|
||||||
process_command(&config_witness).unwrap();
|
process_command(&config_witness).unwrap();
|
||||||
|
|
||||||
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_balance(0, &rpc_client, &process_id); // contract balance
|
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||||
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
@ -195,23 +195,27 @@ fn test_cli_cancel_tx() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let default_signer0 = Keypair::new();
|
||||||
|
let default_signer1 = Keypair::new();
|
||||||
|
|
||||||
let mut config_payer = CliConfig::default();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
config_payer.signers = vec![&default_signer0];
|
||||||
|
|
||||||
let mut config_witness = CliConfig::default();
|
let mut config_witness = CliConfig::default();
|
||||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||||
|
config_witness.signers = vec![&default_signer1];
|
||||||
|
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
config_payer.keypair.pubkey(),
|
config_payer.signers[0].pubkey(),
|
||||||
config_witness.keypair.pubkey()
|
config_witness.signers[0].pubkey()
|
||||||
);
|
);
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_payer.keypair.pubkey(),
|
&config_payer.signers[0].pubkey(),
|
||||||
50,
|
50,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -220,7 +224,7 @@ fn test_cli_cancel_tx() {
|
|||||||
config_payer.command = CliCommand::Pay(PayCommand {
|
config_payer.command = CliCommand::Pay(PayCommand {
|
||||||
lamports: 10,
|
lamports: 10,
|
||||||
to: bob_pubkey,
|
to: bob_pubkey,
|
||||||
witnesses: Some(vec![config_witness.keypair.pubkey()]),
|
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
..PayCommand::default()
|
..PayCommand::default()
|
||||||
});
|
});
|
||||||
@ -233,7 +237,7 @@ fn test_cli_cancel_tx() {
|
|||||||
.expect("base58-encoded public key");
|
.expect("base58-encoded public key");
|
||||||
let process_id = Pubkey::new(&process_id_vec);
|
let process_id = Pubkey::new(&process_id_vec);
|
||||||
|
|
||||||
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_balance(10, &rpc_client, &process_id); // contract balance
|
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
@ -241,7 +245,7 @@ fn test_cli_cancel_tx() {
|
|||||||
config_payer.command = CliCommand::Cancel(process_id);
|
config_payer.command = CliCommand::Cancel(process_id);
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
|
|
||||||
check_balance(50, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
|
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_balance(0, &rpc_client, &process_id); // contract balance
|
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
@ -259,22 +263,26 @@ fn test_offline_pay_tx() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let default_signer = Keypair::new();
|
||||||
|
let default_offline_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config_offline = CliConfig::default();
|
let mut config_offline = CliConfig::default();
|
||||||
config_offline.json_rpc_url =
|
config_offline.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
config_offline.signers = vec![&default_offline_signer];
|
||||||
let mut config_online = CliConfig::default();
|
let mut config_online = CliConfig::default();
|
||||||
config_online.json_rpc_url =
|
config_online.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
config_online.signers = vec![&default_signer];
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
config_offline.keypair.pubkey(),
|
config_offline.signers[0].pubkey(),
|
||||||
config_online.keypair.pubkey()
|
config_online.signers[0].pubkey()
|
||||||
);
|
);
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_offline.keypair.pubkey(),
|
&config_offline.signers[0].pubkey(),
|
||||||
50,
|
50,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -282,49 +290,42 @@ fn test_offline_pay_tx() {
|
|||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config_online.keypair.pubkey(),
|
&config_online.signers[0].pubkey(),
|
||||||
50,
|
50,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_balance(50, &rpc_client, &config_offline.keypair.pubkey());
|
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||||
check_balance(50, &rpc_client, &config_online.keypair.pubkey());
|
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||||
|
|
||||||
|
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||||
config_offline.command = CliCommand::Pay(PayCommand {
|
config_offline.command = CliCommand::Pay(PayCommand {
|
||||||
lamports: 10,
|
lamports: 10,
|
||||||
to: bob_pubkey,
|
to: bob_pubkey,
|
||||||
|
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||||
sign_only: true,
|
sign_only: true,
|
||||||
..PayCommand::default()
|
..PayCommand::default()
|
||||||
});
|
});
|
||||||
let sig_response = process_command(&config_offline).unwrap();
|
let sig_response = process_command(&config_offline).unwrap();
|
||||||
|
|
||||||
check_balance(50, &rpc_client, &config_offline.keypair.pubkey());
|
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||||
check_balance(50, &rpc_client, &config_online.keypair.pubkey());
|
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||||
check_balance(0, &rpc_client, &bob_pubkey);
|
check_balance(0, &rpc_client, &bob_pubkey);
|
||||||
|
|
||||||
let object: Value = serde_json::from_str(&sig_response).unwrap();
|
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||||
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
|
let offline_presigner =
|
||||||
let signer_strings = object.get("signers").unwrap().as_array().unwrap();
|
presigner_from_pubkey_sigs(&config_offline.signers[0].pubkey(), &signers).unwrap();
|
||||||
let signers: Vec<_> = signer_strings
|
let online_pubkey = config_online.signers[0].pubkey();
|
||||||
.iter()
|
config_online.signers = vec![&offline_presigner];
|
||||||
.map(|signer_string| {
|
|
||||||
let mut signer = signer_string.as_str().unwrap().split('=');
|
|
||||||
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
|
|
||||||
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
|
|
||||||
(key, sig)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
config_online.command = CliCommand::Pay(PayCommand {
|
config_online.command = CliCommand::Pay(PayCommand {
|
||||||
lamports: 10,
|
lamports: 10,
|
||||||
to: bob_pubkey,
|
to: bob_pubkey,
|
||||||
signers: Some(signers),
|
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||||
blockhash: Some(blockhash_str.parse::<Hash>().unwrap()),
|
|
||||||
..PayCommand::default()
|
..PayCommand::default()
|
||||||
});
|
});
|
||||||
process_command(&config_online).unwrap();
|
process_command(&config_online).unwrap();
|
||||||
|
|
||||||
check_balance(40, &rpc_client, &config_offline.keypair.pubkey());
|
check_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
|
||||||
check_balance(50, &rpc_client, &config_online.keypair.pubkey());
|
check_balance(50, &rpc_client, &online_pubkey);
|
||||||
check_balance(10, &rpc_client, &bob_pubkey);
|
check_balance(10, &rpc_client, &bob_pubkey);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
@ -341,9 +342,11 @@ fn test_nonced_pay_tx() {
|
|||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
let default_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::default();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
@ -352,29 +355,28 @@ fn test_nonced_pay_tx() {
|
|||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&faucet_addr,
|
&faucet_addr,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
50 + minimum_nonce_balance,
|
50 + minimum_nonce_balance,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_balance(
|
check_balance(
|
||||||
50 + minimum_nonce_balance,
|
50 + minimum_nonce_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&config.keypair.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let nonce_account = Keypair::new();
|
let nonce_account = Keypair::new();
|
||||||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
|
||||||
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
|
||||||
config.command = CliCommand::CreateNonceAccount {
|
config.command = CliCommand::CreateNonceAccount {
|
||||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
nonce_account: 1,
|
||||||
seed: None,
|
seed: None,
|
||||||
nonce_authority: Some(config.keypair.pubkey()),
|
nonce_authority: Some(config.signers[0].pubkey()),
|
||||||
lamports: minimum_nonce_balance,
|
lamports: minimum_nonce_balance,
|
||||||
};
|
};
|
||||||
|
config.signers.push(&nonce_account);
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
check_balance(50, &rpc_client, &config.keypair.pubkey());
|
check_balance(50, &rpc_client, &config.signers[0].pubkey());
|
||||||
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
@ -386,16 +388,17 @@ fn test_nonced_pay_tx() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let bob_pubkey = Pubkey::new_rand();
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
|
config.signers = vec![&default_signer];
|
||||||
config.command = CliCommand::Pay(PayCommand {
|
config.command = CliCommand::Pay(PayCommand {
|
||||||
lamports: 10,
|
lamports: 10,
|
||||||
to: bob_pubkey,
|
to: bob_pubkey,
|
||||||
blockhash: Some(nonce_hash),
|
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
|
||||||
nonce_account: Some(nonce_account.pubkey()),
|
nonce_account: Some(nonce_account.pubkey()),
|
||||||
..PayCommand::default()
|
..PayCommand::default()
|
||||||
});
|
});
|
||||||
process_command(&config).expect("failed to process pay command");
|
process_command(&config).expect("failed to process pay command");
|
||||||
|
|
||||||
check_balance(40, &rpc_client, &config.keypair.pubkey());
|
check_balance(40, &rpc_client, &config.signers[0].pubkey());
|
||||||
check_balance(10, &rpc_client, &bob_pubkey);
|
check_balance(10, &rpc_client, &bob_pubkey);
|
||||||
|
|
||||||
// Verify that nonce has been used
|
// Verify that nonce has been used
|
||||||
|
@ -2,9 +2,8 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
|||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::validator::new_validator_for_tests;
|
use solana_core::validator::new_validator_for_tests;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::signature::KeypairUtil;
|
use solana_sdk::signature::Keypair;
|
||||||
use std::fs::remove_dir_all;
|
use std::{fs::remove_dir_all, sync::mpsc::channel};
|
||||||
use std::sync::mpsc::channel;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cli_request_airdrop() {
|
fn test_cli_request_airdrop() {
|
||||||
@ -18,9 +17,11 @@ fn test_cli_request_airdrop() {
|
|||||||
bob_config.command = CliCommand::Airdrop {
|
bob_config.command = CliCommand::Airdrop {
|
||||||
faucet_host: None,
|
faucet_host: None,
|
||||||
faucet_port: faucet_addr.port(),
|
faucet_port: faucet_addr.port(),
|
||||||
|
pubkey: None,
|
||||||
lamports: 50,
|
lamports: 50,
|
||||||
use_lamports_unit: true,
|
|
||||||
};
|
};
|
||||||
|
let keypair = Keypair::new();
|
||||||
|
bob_config.signers = vec![&keypair];
|
||||||
|
|
||||||
let sig_response = process_command(&bob_config);
|
let sig_response = process_command(&bob_config);
|
||||||
sig_response.unwrap();
|
sig_response.unwrap();
|
||||||
@ -28,7 +29,7 @@ fn test_cli_request_airdrop() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
let balance = rpc_client
|
let balance = rpc_client
|
||||||
.retry_get_balance(&bob_config.keypair.pubkey(), 1)
|
.retry_get_balance(&bob_config.signers[0].pubkey(), 1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(balance, 50);
|
assert_eq!(balance, 50);
|
||||||
|
1256
cli/tests/stake.rs
1256
cli/tests/stake.rs
File diff suppressed because it is too large
Load Diff
209
cli/tests/transfer.rs
Normal file
209
cli/tests/transfer.rs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||||
|
use solana_cli::{
|
||||||
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||||
|
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||||
|
};
|
||||||
|
use solana_client::rpc_client::RpcClient;
|
||||||
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
|
use solana_sdk::{
|
||||||
|
account_utils::StateMut,
|
||||||
|
fee_calculator::FeeCalculator,
|
||||||
|
nonce_state::NonceState,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
|
};
|
||||||
|
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use solana_core::validator::new_validator_for_tests_ex;
|
||||||
|
|
||||||
|
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
||||||
|
(0..5).for_each(|tries| {
|
||||||
|
let balance = client.retry_get_balance(pubkey, 1).unwrap().unwrap();
|
||||||
|
if balance == expected_balance {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if tries == 4 {
|
||||||
|
assert_eq!(balance, expected_balance);
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(500));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transfer() {
|
||||||
|
let (server, leader_data, mint_keypair, ledger_path, _) = new_validator_for_tests_ex(1, 42_000);
|
||||||
|
|
||||||
|
let (sender, receiver) = channel();
|
||||||
|
run_local_faucet(mint_keypair, sender, None);
|
||||||
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
|
let default_signer = Keypair::new();
|
||||||
|
let default_offline_signer = Keypair::new();
|
||||||
|
|
||||||
|
let mut config = CliConfig::default();
|
||||||
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
|
let sender_pubkey = config.signers[0].pubkey();
|
||||||
|
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||||
|
|
||||||
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000).unwrap();
|
||||||
|
check_balance(50_000, &rpc_client, &sender_pubkey);
|
||||||
|
check_balance(0, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
|
// Plain ole transfer
|
||||||
|
config.command = CliCommand::Transfer {
|
||||||
|
lamports: 10,
|
||||||
|
to: recipient_pubkey,
|
||||||
|
from: 0,
|
||||||
|
sign_only: false,
|
||||||
|
blockhash_query: BlockhashQuery::All,
|
||||||
|
nonce_account: None,
|
||||||
|
nonce_authority: 0,
|
||||||
|
fee_payer: 0,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(49_989, &rpc_client, &sender_pubkey);
|
||||||
|
check_balance(10, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
|
let mut offline = CliConfig::default();
|
||||||
|
offline.json_rpc_url = String::default();
|
||||||
|
offline.signers = vec![&default_offline_signer];
|
||||||
|
// Verify we cannot contact the cluster
|
||||||
|
offline.command = CliCommand::ClusterVersion;
|
||||||
|
process_command(&offline).unwrap_err();
|
||||||
|
|
||||||
|
let offline_pubkey = offline.signers[0].pubkey();
|
||||||
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50).unwrap();
|
||||||
|
check_balance(50, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
|
// Offline transfer
|
||||||
|
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||||
|
offline.command = CliCommand::Transfer {
|
||||||
|
lamports: 10,
|
||||||
|
to: recipient_pubkey,
|
||||||
|
from: 0,
|
||||||
|
sign_only: true,
|
||||||
|
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||||
|
nonce_account: None,
|
||||||
|
nonce_authority: 0,
|
||||||
|
fee_payer: 0,
|
||||||
|
};
|
||||||
|
let sign_only_reply = process_command(&offline).unwrap();
|
||||||
|
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
|
||||||
|
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||||
|
config.signers = vec![&offline_presigner];
|
||||||
|
config.command = CliCommand::Transfer {
|
||||||
|
lamports: 10,
|
||||||
|
to: recipient_pubkey,
|
||||||
|
from: 0,
|
||||||
|
sign_only: false,
|
||||||
|
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||||
|
nonce_account: None,
|
||||||
|
nonce_authority: 0,
|
||||||
|
fee_payer: 0,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(39, &rpc_client, &offline_pubkey);
|
||||||
|
check_balance(20, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
|
// Create nonce account
|
||||||
|
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||||
|
let minimum_nonce_balance = rpc_client
|
||||||
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
|
.unwrap();
|
||||||
|
config.signers = vec![&default_signer, &nonce_account];
|
||||||
|
config.command = CliCommand::CreateNonceAccount {
|
||||||
|
nonce_account: 1,
|
||||||
|
seed: None,
|
||||||
|
nonce_authority: None,
|
||||||
|
lamports: minimum_nonce_balance,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
|
|
||||||
|
// Fetch nonce hash
|
||||||
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
|
let nonce_state: NonceState = account.state().unwrap();
|
||||||
|
let nonce_hash = match nonce_state {
|
||||||
|
NonceState::Initialized(_meta, hash) => hash,
|
||||||
|
_ => panic!("Nonce is not initialized"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nonced transfer
|
||||||
|
config.signers = vec![&default_signer];
|
||||||
|
config.command = CliCommand::Transfer {
|
||||||
|
lamports: 10,
|
||||||
|
to: recipient_pubkey,
|
||||||
|
from: 0,
|
||||||
|
sign_only: false,
|
||||||
|
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
|
||||||
|
nonce_account: Some(nonce_account.pubkey()),
|
||||||
|
nonce_authority: 0,
|
||||||
|
fee_payer: 0,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
|
check_balance(30, &rpc_client, &recipient_pubkey);
|
||||||
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
|
let nonce_state: NonceState = account.state().unwrap();
|
||||||
|
let new_nonce_hash = match nonce_state {
|
||||||
|
NonceState::Initialized(_meta, hash) => hash,
|
||||||
|
_ => panic!("Nonce is not initialized"),
|
||||||
|
};
|
||||||
|
assert_ne!(nonce_hash, new_nonce_hash);
|
||||||
|
|
||||||
|
// Assign nonce authority to offline
|
||||||
|
config.signers = vec![&default_signer];
|
||||||
|
config.command = CliCommand::AuthorizeNonceAccount {
|
||||||
|
nonce_account: nonce_account.pubkey(),
|
||||||
|
nonce_authority: 0,
|
||||||
|
new_authority: offline_pubkey,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
|
|
||||||
|
// Fetch nonce hash
|
||||||
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
|
let nonce_state: NonceState = account.state().unwrap();
|
||||||
|
let nonce_hash = match nonce_state {
|
||||||
|
NonceState::Initialized(_meta, hash) => hash,
|
||||||
|
_ => panic!("Nonce is not initialized"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Offline, nonced transfer
|
||||||
|
offline.signers = vec![&default_offline_signer];
|
||||||
|
offline.command = CliCommand::Transfer {
|
||||||
|
lamports: 10,
|
||||||
|
to: recipient_pubkey,
|
||||||
|
from: 0,
|
||||||
|
sign_only: true,
|
||||||
|
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||||
|
nonce_account: Some(nonce_account.pubkey()),
|
||||||
|
nonce_authority: 0,
|
||||||
|
fee_payer: 0,
|
||||||
|
};
|
||||||
|
let sign_only_reply = process_command(&offline).unwrap();
|
||||||
|
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
|
||||||
|
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||||
|
config.signers = vec![&offline_presigner];
|
||||||
|
config.command = CliCommand::Transfer {
|
||||||
|
lamports: 10,
|
||||||
|
to: recipient_pubkey,
|
||||||
|
from: 0,
|
||||||
|
sign_only: false,
|
||||||
|
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||||
|
nonce_account: Some(nonce_account.pubkey()),
|
||||||
|
nonce_authority: 0,
|
||||||
|
fee_payer: 0,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(28, &rpc_client, &offline_pubkey);
|
||||||
|
check_balance(40, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
|
server.close().unwrap();
|
||||||
|
remove_dir_all(ledger_path).unwrap();
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-client"
|
name = "solana-client"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
description = "Solana Client"
|
description = "Solana Client"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@ -19,11 +19,12 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.44"
|
serde_json = "1.0.44"
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_matches = "1.3.0"
|
assert_matches = "1.3.0"
|
||||||
jsonrpc-core = "14.0.5"
|
jsonrpc-core = "14.0.5"
|
||||||
jsonrpc-http-server = "14.0.5"
|
jsonrpc-http-server = "14.0.5"
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use crate::rpc_request;
|
use crate::rpc_request;
|
||||||
use solana_sdk::transaction::TransactionError;
|
use solana_sdk::{signature::SignerError, transaction::TransactionError};
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ClientError {
|
pub enum ClientError {
|
||||||
Io(io::Error),
|
Io(#[from] io::Error),
|
||||||
Reqwest(reqwest::Error),
|
Reqwest(#[from] reqwest::Error),
|
||||||
RpcError(rpc_request::RpcError),
|
RpcError(#[from] rpc_request::RpcError),
|
||||||
SerdeJson(serde_json::error::Error),
|
SerdeJson(#[from] serde_json::error::Error),
|
||||||
TransactionError(TransactionError),
|
SigningError(#[from] SignerError),
|
||||||
|
TransactionError(#[from] TransactionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ClientError {
|
impl fmt::Display for ClientError {
|
||||||
@ -16,35 +18,3 @@ impl fmt::Display for ClientError {
|
|||||||
write!(f, "solana client error")
|
write!(f, "solana client error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for ClientError {}
|
|
||||||
|
|
||||||
impl From<io::Error> for ClientError {
|
|
||||||
fn from(err: io::Error) -> ClientError {
|
|
||||||
ClientError::Io(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<reqwest::Error> for ClientError {
|
|
||||||
fn from(err: reqwest::Error) -> ClientError {
|
|
||||||
ClientError::Reqwest(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<rpc_request::RpcError> for ClientError {
|
|
||||||
fn from(err: rpc_request::RpcError) -> ClientError {
|
|
||||||
ClientError::RpcError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<serde_json::error::Error> for ClientError {
|
|
||||||
fn from(err: serde_json::error::Error) -> ClientError {
|
|
||||||
ClientError::SerdeJson(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TransactionError> for ClientError {
|
|
||||||
fn from(err: TransactionError) -> ClientError {
|
|
||||||
ClientError::TransactionError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -60,13 +60,10 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
|
|||||||
Value::Null
|
Value::Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RpcRequest::GetBalance => {
|
RpcRequest::GetBalance => serde_json::to_value(Response {
|
||||||
let n = if self.url == "airdrop" { 0 } else { 50 };
|
context: RpcResponseContext { slot: 1 },
|
||||||
serde_json::to_value(Response {
|
value: Value::Number(Number::from(50)),
|
||||||
context: RpcResponseContext { slot: 1 },
|
})?,
|
||||||
value: Value::Number(Number::from(n)),
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
RpcRequest::GetRecentBlockhash => serde_json::to_value(Response {
|
RpcRequest::GetRecentBlockhash => serde_json::to_value(Response {
|
||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: (
|
value: (
|
||||||
|
@ -22,7 +22,8 @@ use solana_sdk::{
|
|||||||
hash::Hash,
|
hash::Hash,
|
||||||
inflation::Inflation,
|
inflation::Inflation,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{KeypairUtil, Signature},
|
signature::Signature,
|
||||||
|
signers::Signers,
|
||||||
transaction::{self, Transaction, TransactionError},
|
transaction::{self, Transaction, TransactionError},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@ -177,7 +178,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetVoteAccounts parse failure: {}", err),
|
format!("GetVoteAccounts parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -196,7 +197,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetClusterNodes parse failure: {}", err),
|
format!("GetClusterNodes parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -215,7 +216,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetConfirmedBlock parse failure: {}", err),
|
format!("GetConfirmedBlock parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -242,7 +243,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetConfirmedBlocks parse failure: {}", err),
|
format!("GetConfirmedBlocks parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -293,7 +294,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetEpochInfo parse failure: {}", err),
|
format!("GetEpochInfo parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -324,7 +325,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetLeaderSchedule failure: {}", err),
|
format!("GetLeaderSchedule failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -343,7 +344,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetEpochSchedule parse failure: {}", err),
|
format!("GetEpochSchedule parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -381,7 +382,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetVersion parse failure: {}", err),
|
format!("GetVersion parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -400,15 +401,15 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("MinimumLedgerSlot parse failure: {}", err),
|
format!("MinimumLedgerSlot parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_and_confirm_transaction<T: KeypairUtil>(
|
pub fn send_and_confirm_transaction<T: Signers>(
|
||||||
&self,
|
&self,
|
||||||
transaction: &mut Transaction,
|
transaction: &mut Transaction,
|
||||||
signer_keys: &[&T],
|
signer_keys: &T,
|
||||||
) -> Result<String, ClientError> {
|
) -> Result<String, ClientError> {
|
||||||
let mut send_retries = 20;
|
let mut send_retries = 20;
|
||||||
loop {
|
loop {
|
||||||
@ -456,10 +457,10 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_and_confirm_transactions<T: KeypairUtil>(
|
pub fn send_and_confirm_transactions<T: Signers>(
|
||||||
&self,
|
&self,
|
||||||
mut transactions: Vec<Transaction>,
|
mut transactions: Vec<Transaction>,
|
||||||
signer_keys: &[&T],
|
signer_keys: &T,
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
) -> Result<(), Box<dyn error::Error>> {
|
||||||
let mut send_retries = 5;
|
let mut send_retries = 5;
|
||||||
loop {
|
loop {
|
||||||
@ -516,24 +517,22 @@ impl RpcClient {
|
|||||||
// Re-sign any failed transactions with a new blockhash and retry
|
// Re-sign any failed transactions with a new blockhash and retry
|
||||||
let (blockhash, _fee_calculator) =
|
let (blockhash, _fee_calculator) =
|
||||||
self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?;
|
self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?;
|
||||||
transactions = transactions_signatures
|
transactions = vec![];
|
||||||
.into_iter()
|
for (mut transaction, _) in transactions_signatures.into_iter() {
|
||||||
.map(|(mut transaction, _)| {
|
transaction.try_sign(signer_keys, blockhash)?;
|
||||||
transaction.sign(signer_keys, blockhash);
|
transactions.push(transaction);
|
||||||
transaction
|
}
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resign_transaction<T: KeypairUtil>(
|
pub fn resign_transaction<T: Signers>(
|
||||||
&self,
|
&self,
|
||||||
tx: &mut Transaction,
|
tx: &mut Transaction,
|
||||||
signer_keys: &[&T],
|
signer_keys: &T,
|
||||||
) -> Result<(), ClientError> {
|
) -> Result<(), ClientError> {
|
||||||
let (blockhash, _fee_calculator) =
|
let (blockhash, _fee_calculator) =
|
||||||
self.get_new_blockhash(&tx.message().recent_blockhash)?;
|
self.get_new_blockhash(&tx.message().recent_blockhash)?;
|
||||||
tx.sign(signer_keys, blockhash);
|
tx.try_sign(signer_keys, blockhash)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,7 +611,7 @@ impl RpcClient {
|
|||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("AccountNotFound: pubkey={}: {}", pubkey, err),
|
format!("AccountNotFound: pubkey={}: {:?}", pubkey, err),
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
@ -698,7 +697,7 @@ impl RpcClient {
|
|||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("AccountNotFound: pubkey={}: {}", pubkey, err),
|
format!("AccountNotFound: pubkey={}: {:?}", pubkey, err),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -749,7 +748,7 @@ impl RpcClient {
|
|||||||
serde_json::from_value(response).map_err(|err| {
|
serde_json::from_value(response).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("GetTransactionCount parse failure: {}", err),
|
format!("GetTransactionCount parse failure: {:?}", err),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1129,9 +1128,7 @@ mod tests {
|
|||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
use solana_logger;
|
use solana_logger;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
instruction::InstructionError,
|
instruction::InstructionError, signature::Keypair, system_transaction,
|
||||||
signature::{Keypair, KeypairUtil},
|
|
||||||
system_transaction,
|
|
||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
};
|
};
|
||||||
use std::{sync::mpsc::channel, thread};
|
use std::{sync::mpsc::channel, thread};
|
||||||
|
@ -15,10 +15,7 @@ pub struct RpcClientRequest {
|
|||||||
|
|
||||||
impl RpcClientRequest {
|
impl RpcClientRequest {
|
||||||
pub fn new(url: String) -> Self {
|
pub fn new(url: String) -> Self {
|
||||||
Self {
|
Self::new_with_timeout(url, Duration::from_secs(20))
|
||||||
client: reqwest::blocking::Client::new(),
|
|
||||||
url,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||||
|
@ -32,6 +32,14 @@ pub struct RpcBlockCommitment<T> {
|
|||||||
pub total_stake: u64,
|
pub total_stake: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct RpcReward {
|
||||||
|
pub pubkey: String,
|
||||||
|
pub lamports: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RpcRewards = Vec<RpcReward>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RpcConfirmedBlock {
|
pub struct RpcConfirmedBlock {
|
||||||
@ -39,6 +47,7 @@ pub struct RpcConfirmedBlock {
|
|||||||
pub blockhash: String,
|
pub blockhash: String,
|
||||||
pub parent_slot: Slot,
|
pub parent_slot: Slot,
|
||||||
pub transactions: Vec<RpcTransactionWithStatusMeta>,
|
pub transactions: Vec<RpcTransactionWithStatusMeta>,
|
||||||
|
pub rewards: RpcRewards,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -17,7 +17,8 @@ use solana_sdk::{
|
|||||||
message::Message,
|
message::Message,
|
||||||
packet::PACKET_DATA_SIZE,
|
packet::PACKET_DATA_SIZE,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, KeypairUtil, Signature},
|
signature::{Keypair, Signature, Signer},
|
||||||
|
signers::Signers,
|
||||||
system_instruction,
|
system_instruction,
|
||||||
timing::duration_as_ms,
|
timing::duration_as_ms,
|
||||||
transaction::{self, Transaction},
|
transaction::{self, Transaction},
|
||||||
@ -202,9 +203,9 @@ impl ThinClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retry sending a signed Transaction to the server for processing
|
/// Retry sending a signed Transaction to the server for processing
|
||||||
pub fn send_and_confirm_transaction(
|
pub fn send_and_confirm_transaction<T: Signers>(
|
||||||
&self,
|
&self,
|
||||||
keypairs: &[&Keypair],
|
keypairs: &T,
|
||||||
transaction: &mut Transaction,
|
transaction: &mut Transaction,
|
||||||
tries: usize,
|
tries: usize,
|
||||||
pending_confirmations: usize,
|
pending_confirmations: usize,
|
||||||
@ -351,9 +352,13 @@ impl Client for ThinClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SyncClient for ThinClient {
|
impl SyncClient for ThinClient {
|
||||||
fn send_message(&self, keypairs: &[&Keypair], message: Message) -> TransportResult<Signature> {
|
fn send_message<T: Signers>(
|
||||||
|
&self,
|
||||||
|
keypairs: &T,
|
||||||
|
message: Message,
|
||||||
|
) -> TransportResult<Signature> {
|
||||||
let (blockhash, _fee_calculator) = self.get_recent_blockhash()?;
|
let (blockhash, _fee_calculator) = self.get_recent_blockhash()?;
|
||||||
let mut transaction = Transaction::new(&keypairs, message, blockhash);
|
let mut transaction = Transaction::new(keypairs, message, blockhash);
|
||||||
let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?;
|
let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?;
|
||||||
Ok(signature)
|
Ok(signature)
|
||||||
}
|
}
|
||||||
@ -561,13 +566,13 @@ impl AsyncClient for ThinClient {
|
|||||||
.send_to(&buf[..], &self.tpu_addr())?;
|
.send_to(&buf[..], &self.tpu_addr())?;
|
||||||
Ok(transaction.signatures[0])
|
Ok(transaction.signatures[0])
|
||||||
}
|
}
|
||||||
fn async_send_message(
|
fn async_send_message<T: Signers>(
|
||||||
&self,
|
&self,
|
||||||
keypairs: &[&Keypair],
|
keypairs: &T,
|
||||||
message: Message,
|
message: Message,
|
||||||
recent_blockhash: Hash,
|
recent_blockhash: Hash,
|
||||||
) -> io::Result<Signature> {
|
) -> io::Result<Signature> {
|
||||||
let transaction = Transaction::new(&keypairs, message, recent_blockhash);
|
let transaction = Transaction::new(keypairs, message, recent_blockhash);
|
||||||
self.async_send_transaction(transaction)
|
self.async_send_transaction(transaction)
|
||||||
}
|
}
|
||||||
fn async_send_instruction(
|
fn async_send_instruction(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-core"
|
name = "solana-core"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "0.23.0"
|
version = "0.23.8"
|
||||||
documentation = "https://docs.rs/solana"
|
documentation = "https://docs.rs/solana"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
@ -18,6 +18,7 @@ bincode = "1.2.1"
|
|||||||
bs58 = "0.3.0"
|
bs58 = "0.3.0"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
chrono = { version = "0.4.10", features = ["serde"] }
|
chrono = { version = "0.4.10", features = ["serde"] }
|
||||||
|
compression = "0.1.5"
|
||||||
core_affinity = "0.5.10"
|
core_affinity = "0.5.10"
|
||||||
crc = { version = "1.8.1", optional = true }
|
crc = { version = "1.8.1", optional = true }
|
||||||
crossbeam-channel = "0.3"
|
crossbeam-channel = "0.3"
|
||||||
@ -37,29 +38,30 @@ num-traits = "0.2"
|
|||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
rand_chacha = "0.1.1"
|
rand_chacha = "0.1.1"
|
||||||
rayon = "1.2.0"
|
rayon = "1.2.0"
|
||||||
|
regex = "1.3.4"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.44"
|
serde_json = "1.0.44"
|
||||||
solana-budget-program = { path = "../programs/budget", version = "0.23.0" }
|
solana-budget-program = { path = "../programs/budget", version = "0.23.8" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "0.23.8" }
|
||||||
solana-client = { path = "../client", version = "0.23.0" }
|
solana-client = { path = "../client", version = "0.23.8" }
|
||||||
solana-faucet = { path = "../faucet", version = "0.23.0" }
|
solana-faucet = { path = "../faucet", version = "0.23.8" }
|
||||||
ed25519-dalek = "=1.0.0-pre.1"
|
ed25519-dalek = "=1.0.0-pre.1"
|
||||||
solana-ledger = { path = "../ledger", version = "0.23.0" }
|
solana-ledger = { path = "../ledger", version = "0.23.8" }
|
||||||
solana-logger = { path = "../logger", version = "0.23.0" }
|
solana-logger = { path = "../logger", version = "0.23.8" }
|
||||||
solana-merkle-tree = { path = "../merkle-tree", version = "0.23.0" }
|
solana-merkle-tree = { path = "../merkle-tree", version = "0.23.8" }
|
||||||
solana-metrics = { path = "../metrics", version = "0.23.0" }
|
solana-metrics = { path = "../metrics", version = "0.23.8" }
|
||||||
solana-measure = { path = "../measure", version = "0.23.0" }
|
solana-measure = { path = "../measure", version = "0.23.8" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
|
solana-net-utils = { path = "../net-utils", version = "0.23.8" }
|
||||||
solana-chacha-cuda = { path = "../chacha-cuda", version = "0.23.0" }
|
solana-chacha-cuda = { path = "../chacha-cuda", version = "0.23.8" }
|
||||||
solana-perf = { path = "../perf", version = "0.23.0" }
|
solana-perf = { path = "../perf", version = "0.23.8" }
|
||||||
solana-runtime = { path = "../runtime", version = "0.23.0" }
|
solana-runtime = { path = "../runtime", version = "0.23.8" }
|
||||||
solana-sdk = { path = "../sdk", version = "0.23.0" }
|
solana-sdk = { path = "../sdk", version = "0.23.8" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "0.23.0" }
|
solana-stake-program = { path = "../programs/stake", version = "0.23.8" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "0.23.0" }
|
solana-storage-program = { path = "../programs/storage", version = "0.23.8" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "0.23.0" }
|
solana-vote-program = { path = "../programs/vote", version = "0.23.8" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "0.23.0" }
|
solana-vote-signer = { path = "../vote-signer", version = "0.23.8" }
|
||||||
solana-sys-tuner = { path = "../sys-tuner", version = "0.23.0" }
|
solana-sys-tuner = { path = "../sys-tuner", version = "0.23.8" }
|
||||||
symlink = "0.1.0"
|
symlink = "0.1.0"
|
||||||
sys-info = "0.5.8"
|
sys-info = "0.5.8"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
@ -69,8 +71,9 @@ tokio-codec = "0.1"
|
|||||||
tokio-fs = "0.1"
|
tokio-fs = "0.1"
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
untrusted = "0.7.0"
|
untrusted = "0.7.0"
|
||||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "0.23.0" }
|
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "0.23.8" }
|
||||||
reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0.1-3", features = ["simd-accel"] }
|
reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0.1-3", features = ["simd-accel"] }
|
||||||
|
trees = "0.2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
matches = "0.1.6"
|
matches = "0.1.6"
|
||||||
|
@ -21,8 +21,8 @@ use solana_sdk::genesis_config::GenesisConfig;
|
|||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_sdk::signature::Keypair;
|
||||||
use solana_sdk::signature::KeypairUtil;
|
|
||||||
use solana_sdk::signature::Signature;
|
use solana_sdk::signature::Signature;
|
||||||
|
use solana_sdk::signature::Signer;
|
||||||
use solana_sdk::system_instruction;
|
use solana_sdk::system_instruction;
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use solana_sdk::timing::{duration_as_us, timestamp};
|
use solana_sdk::timing::{duration_as_us, timestamp};
|
||||||
|
@ -3,7 +3,7 @@ extern crate test;
|
|||||||
|
|
||||||
use solana_ledger::entry::{next_entry_mut, Entry, EntrySlice};
|
use solana_ledger::entry::{next_entry_mut, Entry, EntrySlice};
|
||||||
use solana_sdk::hash::{hash, Hash};
|
use solana_sdk::hash::{hash, Hash};
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use solana_ledger::shred::{
|
|||||||
};
|
};
|
||||||
use solana_perf::test_tx;
|
use solana_perf::test_tx;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use solana_core::sigverify::TransactionSigVerifier;
|
|||||||
use solana_core::sigverify_stage::SigVerifyStage;
|
use solana_core::sigverify_stage::SigVerifyStage;
|
||||||
use solana_perf::test_tx::test_tx;
|
use solana_perf::test_tx::test_tx;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use solana_sdk::timing::duration_as_ms;
|
use solana_sdk::timing::duration_as_ms;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
@ -404,7 +404,7 @@ impl BankingStage {
|
|||||||
if unprocessed_packets.is_empty() {
|
if unprocessed_packets.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let num = unprocessed_packets
|
let num: usize = unprocessed_packets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, unprocessed)| unprocessed.len())
|
.map(|(_, unprocessed)| unprocessed.len())
|
||||||
.sum();
|
.sum();
|
||||||
@ -1029,7 +1029,7 @@ mod tests {
|
|||||||
use solana_runtime::bank::HashAgeKind;
|
use solana_runtime::bank::HashAgeKind;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
signature::{Keypair, KeypairUtil},
|
signature::{Keypair, Signer},
|
||||||
system_transaction,
|
system_transaction,
|
||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
};
|
};
|
||||||
|
@ -180,7 +180,7 @@ mod test {
|
|||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -58,31 +58,35 @@ impl BlockstreamService {
|
|||||||
let timeout = Duration::new(1, 0);
|
let timeout = Duration::new(1, 0);
|
||||||
let (slot, slot_leader) = slot_full_receiver.recv_timeout(timeout)?;
|
let (slot, slot_leader) = slot_full_receiver.recv_timeout(timeout)?;
|
||||||
|
|
||||||
let entries = blockstore.get_slot_entries(slot, 0, None).unwrap();
|
// Slot might not exist due to LedgerCleanupService, check first
|
||||||
let blockstore_meta = blockstore.meta(slot).unwrap().unwrap();
|
let blockstore_meta = blockstore.meta(slot).unwrap();
|
||||||
let _parent_slot = if slot == 0 {
|
if let Some(blockstore_meta) = blockstore_meta {
|
||||||
None
|
// Return error to main loop. Thread won't exit, will just log the error
|
||||||
} else {
|
let entries = blockstore.get_slot_entries(slot, 0, None)?;
|
||||||
Some(blockstore_meta.parent_slot)
|
let _parent_slot = if slot == 0 {
|
||||||
};
|
None
|
||||||
let ticks_per_slot = entries.iter().filter(|entry| entry.is_tick()).count() as u64;
|
} else {
|
||||||
let mut tick_height = ticks_per_slot * slot;
|
Some(blockstore_meta.parent_slot)
|
||||||
|
};
|
||||||
|
let ticks_per_slot = entries.iter().filter(|entry| entry.is_tick()).count() as u64;
|
||||||
|
let mut tick_height = ticks_per_slot * slot;
|
||||||
|
|
||||||
for (i, entry) in entries.iter().enumerate() {
|
for (i, entry) in entries.iter().enumerate() {
|
||||||
if entry.is_tick() {
|
if entry.is_tick() {
|
||||||
tick_height += 1;
|
tick_height += 1;
|
||||||
}
|
}
|
||||||
blockstream
|
|
||||||
.emit_entry_event(slot, tick_height, &slot_leader, &entry)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
|
|
||||||
});
|
|
||||||
if i == entries.len() - 1 {
|
|
||||||
blockstream
|
blockstream
|
||||||
.emit_block_event(slot, tick_height, &slot_leader, entry.hash)
|
.emit_entry_event(slot, tick_height, &slot_leader, &entry)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
|
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
|
||||||
});
|
});
|
||||||
|
if i == entries.len() - 1 {
|
||||||
|
blockstream
|
||||||
|
.emit_block_event(slot, tick_height, &slot_leader, entry.hash)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -103,7 +107,7 @@ mod test {
|
|||||||
use solana_ledger::create_new_tmp_ledger;
|
use solana_ledger::create_new_tmp_ledger;
|
||||||
use solana_ledger::entry::{create_ticks, Entry};
|
use solana_ledger::entry::{create_ticks, Entry};
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
@ -259,7 +259,7 @@ mod test {
|
|||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
@ -82,7 +82,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
|
|||||||
// Broadcast data
|
// Broadcast data
|
||||||
let all_shred_bufs: Vec<Vec<u8>> = shreds.to_vec().into_iter().map(|s| s.payload).collect();
|
let all_shred_bufs: Vec<Vec<u8>> = shreds.to_vec().into_iter().map(|s| s.payload).collect();
|
||||||
cluster_info
|
cluster_info
|
||||||
.read()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.broadcast_shreds(sock, all_shred_bufs, &all_seeds, stakes)?;
|
.broadcast_shreds(sock, all_shred_bufs, &all_seeds, stakes)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -264,7 +264,7 @@ impl StandardBroadcastRun {
|
|||||||
trace!("Broadcasting {:?} shreds", shred_bufs.len());
|
trace!("Broadcasting {:?} shreds", shred_bufs.len());
|
||||||
|
|
||||||
cluster_info
|
cluster_info
|
||||||
.read()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.broadcast_shreds(sock, shred_bufs, &seeds, stakes)?;
|
.broadcast_shreds(sock, shred_bufs, &seeds, stakes)?;
|
||||||
|
|
||||||
@ -362,7 +362,7 @@ mod test {
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
clock::Slot,
|
clock::Slot,
|
||||||
genesis_config::GenesisConfig,
|
genesis_config::GenesisConfig,
|
||||||
signature::{Keypair, KeypairUtil},
|
signature::{Keypair, Signer},
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -84,7 +84,7 @@ impl ClusterInfoVoteListener {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::packet;
|
use crate::packet;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
use solana_vote_program::vote_instruction;
|
use solana_vote_program::vote_instruction;
|
||||||
use solana_vote_program::vote_state::Vote;
|
use solana_vote_program::vote_state::Vote;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user