@@ -152,6 +152,7 @@ startNodes() {
|
|||||||
addLogs=true
|
addLogs=true
|
||||||
fi
|
fi
|
||||||
initCompleteFiles=()
|
initCompleteFiles=()
|
||||||
|
maybeExpectedGenesisBlockhash=
|
||||||
for i in $(seq 0 $((${#nodes[@]} - 1))); do
|
for i in $(seq 0 $((${#nodes[@]} - 1))); do
|
||||||
declare cmd=${nodes[$i]}
|
declare cmd=${nodes[$i]}
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ startNodes() {
|
|||||||
rm -f "$initCompleteFile"
|
rm -f "$initCompleteFile"
|
||||||
initCompleteFiles+=("$initCompleteFile")
|
initCompleteFiles+=("$initCompleteFile")
|
||||||
fi
|
fi
|
||||||
startNode "$i" "$cmd"
|
startNode "$i" "$cmd $maybeExpectedGenesisBlockhash"
|
||||||
if $addLogs; then
|
if $addLogs; then
|
||||||
logs+=("$(getNodeLogFile "$i" "$cmd")")
|
logs+=("$(getNodeLogFile "$i" "$cmd")")
|
||||||
fi
|
fi
|
||||||
@@ -170,6 +171,14 @@ startNodes() {
|
|||||||
if [[ "$i" -eq 1 ]]; then
|
if [[ "$i" -eq 1 ]]; then
|
||||||
SECONDS=
|
SECONDS=
|
||||||
waitForNodeToInit "$initCompleteFile"
|
waitForNodeToInit "$initCompleteFile"
|
||||||
|
|
||||||
|
(
|
||||||
|
source multinode-demo/common.sh
|
||||||
|
set -x
|
||||||
|
$solana_cli --keypair config/bootstrap-leader/identity-keypair.json \
|
||||||
|
--url http://127.0.0.1:8899 get-genesis-blockhash
|
||||||
|
) | tee genesis-blockhash.log
|
||||||
|
maybeExpectedGenesisBlockhash="--expected-genesis-blockhash $(tail -n1 genesis-blockhash.log)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@@ -96,6 +96,7 @@ pub enum WalletCommand {
|
|||||||
ClaimStorageReward(Pubkey, Pubkey),
|
ClaimStorageReward(Pubkey, Pubkey),
|
||||||
ShowStorageAccount(Pubkey),
|
ShowStorageAccount(Pubkey),
|
||||||
Deploy(String),
|
Deploy(String),
|
||||||
|
GetGenesisBlockhash,
|
||||||
GetSlot,
|
GetSlot,
|
||||||
GetEpochInfo,
|
GetEpochInfo,
|
||||||
GetTransactionCount,
|
GetTransactionCount,
|
||||||
@@ -343,6 +344,7 @@ pub fn parse_command(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)),
|
)),
|
||||||
|
("get-genesis-blockhash", Some(_matches)) => Ok(WalletCommand::GetGenesisBlockhash),
|
||||||
("get-slot", Some(_matches)) => Ok(WalletCommand::GetSlot),
|
("get-slot", Some(_matches)) => Ok(WalletCommand::GetSlot),
|
||||||
("get-epoch-info", Some(_matches)) => Ok(WalletCommand::GetEpochInfo),
|
("get-epoch-info", Some(_matches)) => Ok(WalletCommand::GetEpochInfo),
|
||||||
("get-transaction-count", Some(_matches)) => Ok(WalletCommand::GetTransactionCount),
|
("get-transaction-count", Some(_matches)) => Ok(WalletCommand::GetTransactionCount),
|
||||||
@@ -1086,6 +1088,11 @@ fn process_cancel(rpc_client: &RpcClient, config: &WalletConfig, pubkey: &Pubkey
|
|||||||
log_instruction_custom_error::<BudgetError>(result)
|
log_instruction_custom_error::<BudgetError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_get_genesis_blockhash(rpc_client: &RpcClient) -> ProcessResult {
|
||||||
|
let genesis_blockhash = rpc_client.get_genesis_blockhash()?;
|
||||||
|
Ok(genesis_blockhash.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn process_get_slot(rpc_client: &RpcClient) -> ProcessResult {
|
fn process_get_slot(rpc_client: &RpcClient) -> ProcessResult {
|
||||||
let slot = rpc_client.get_slot()?;
|
let slot = rpc_client.get_slot()?;
|
||||||
Ok(slot.to_string())
|
Ok(slot.to_string())
|
||||||
@@ -1501,6 +1508,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
|||||||
process_deploy(&rpc_client, config, program_location)
|
process_deploy(&rpc_client, config, program_location)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WalletCommand::GetGenesisBlockhash => process_get_genesis_blockhash(&rpc_client),
|
||||||
WalletCommand::GetSlot => process_get_slot(&rpc_client),
|
WalletCommand::GetSlot => process_get_slot(&rpc_client),
|
||||||
WalletCommand::GetEpochInfo => process_get_epoch_info(&rpc_client),
|
WalletCommand::GetEpochInfo => process_get_epoch_info(&rpc_client),
|
||||||
WalletCommand::GetTransactionCount => process_get_transaction_count(&rpc_client),
|
WalletCommand::GetTransactionCount => process_get_transaction_count(&rpc_client),
|
||||||
@@ -2196,6 +2204,10 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.help("/path/to/program.o"),
|
.help("/path/to/program.o"),
|
||||||
), // TODO: Add "loader" argument; current default is bpf_loader
|
), // TODO: Add "loader" argument; current default is bpf_loader
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("get-genesis-blockhash")
|
||||||
|
.about("Get the genesis blockhash"),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("get-slot")
|
SubCommand::with_name("get-slot")
|
||||||
.about("Get current slot"),
|
.about("Get current slot"),
|
||||||
|
@@ -497,12 +497,13 @@ pub fn new_banks_from_blocktree(
|
|||||||
) {
|
) {
|
||||||
let genesis_block = GenesisBlock::load(blocktree_path).expect("Failed to load genesis block");
|
let genesis_block = GenesisBlock::load(blocktree_path).expect("Failed to load genesis block");
|
||||||
let genesis_blockhash = genesis_block.hash();
|
let genesis_blockhash = genesis_block.hash();
|
||||||
|
info!("genesis blockhash: {}", genesis_blockhash);
|
||||||
|
|
||||||
if let Some(expected_genesis_blockhash) = expected_genesis_blockhash {
|
if let Some(expected_genesis_blockhash) = expected_genesis_blockhash {
|
||||||
if genesis_blockhash != expected_genesis_blockhash {
|
if genesis_blockhash != expected_genesis_blockhash {
|
||||||
error!(
|
error!(
|
||||||
"Genesis blockhash mismatch: expected {} but local genesis blockhash is {}",
|
"genesis blockhash mismatch: expected {}",
|
||||||
expected_genesis_blockhash, genesis_blockhash,
|
expected_genesis_blockhash
|
||||||
);
|
);
|
||||||
error!(
|
error!(
|
||||||
"Delete the ledger directory to continue: {:?}",
|
"Delete the ledger directory to continue: {:?}",
|
||||||
|
@@ -67,6 +67,9 @@ while [[ -n $1 ]]; do
|
|||||||
elif [[ $1 = --blockstream ]]; then
|
elif [[ $1 = --blockstream ]]; then
|
||||||
args+=("$1" "$2")
|
args+=("$1" "$2")
|
||||||
shift 2
|
shift 2
|
||||||
|
elif [[ $1 = --expected-genesis-blockhash ]]; then
|
||||||
|
args+=("$1" "$2")
|
||||||
|
shift 2
|
||||||
elif [[ $1 = --identity ]]; then
|
elif [[ $1 = --identity ]]; then
|
||||||
identity_keypair_path=$2
|
identity_keypair_path=$2
|
||||||
args+=("$1" "$2")
|
args+=("$1" "$2")
|
||||||
|
@@ -20,6 +20,7 @@ use std::io::{self, Read};
|
|||||||
use std::net::{SocketAddr, TcpListener};
|
use std::net::{SocketAddr, TcpListener};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@@ -31,6 +32,12 @@ fn port_range_validator(port_range: String) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_validator(hash: String) -> Result<(), String> {
|
||||||
|
Hash::from_str(&hash)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|e| format!("{:?}", e))
|
||||||
|
}
|
||||||
|
|
||||||
static TRUCK: Emoji = Emoji("🚚 ", "");
|
static TRUCK: Emoji = Emoji("🚚 ", "");
|
||||||
static SPARKLE: Emoji = Emoji("✨ ", "");
|
static SPARKLE: Emoji = Emoji("✨ ", "");
|
||||||
|
|
||||||
@@ -171,7 +178,7 @@ fn initialize_ledger_path(
|
|||||||
.map(|addrs| addrs.0)
|
.map(|addrs| addrs.0)
|
||||||
.find(|rpc_addr| rpc_addr.ip() == entrypoint.gossip.ip())
|
.find(|rpc_addr| rpc_addr.ip() == entrypoint.gossip.ip())
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
eprintln!(
|
error!(
|
||||||
"Entrypoint ({:?}) is not running the RPC service",
|
"Entrypoint ({:?}) is not running the RPC service",
|
||||||
entrypoint.gossip.ip()
|
entrypoint.gossip.ip()
|
||||||
);
|
);
|
||||||
@@ -199,7 +206,7 @@ fn initialize_ledger_path(
|
|||||||
snapshot_package.parent().unwrap(),
|
snapshot_package.parent().unwrap(),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|err| eprintln!("Warning: Unable to fetch snapshot: {:?}", err));
|
.unwrap_or_else(|err| warn!("Unable to fetch snapshot: {:?}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
match client.get_slot() {
|
match client.get_slot() {
|
||||||
@@ -396,6 +403,14 @@ pub fn main() {
|
|||||||
.takes_value(false)
|
.takes_value(false)
|
||||||
.help("Use CUDA"),
|
.help("Use CUDA"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("expected_genesis_blockhash")
|
||||||
|
.long("expected-genesis-blockhash")
|
||||||
|
.value_name("HASH")
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(hash_validator)
|
||||||
|
.help("Require the genesis block have this blockhash"),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
if matches.is_present("cuda") {
|
if matches.is_present("cuda") {
|
||||||
@@ -405,7 +420,7 @@ pub fn main() {
|
|||||||
let mut validator_config = ValidatorConfig::default();
|
let mut validator_config = ValidatorConfig::default();
|
||||||
let keypair = if let Some(identity) = matches.value_of("identity") {
|
let keypair = if let Some(identity) = matches.value_of("identity") {
|
||||||
read_keypair(identity).unwrap_or_else(|err| {
|
read_keypair(identity).unwrap_or_else(|err| {
|
||||||
eprintln!("{}: Unable to open keypair file: {}", err, identity);
|
error!("{}: Unable to open keypair file: {}", err, identity);
|
||||||
exit(1);
|
exit(1);
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -414,7 +429,7 @@ pub fn main() {
|
|||||||
|
|
||||||
let voting_keypair = if let Some(identity) = matches.value_of("voting_keypair") {
|
let voting_keypair = if let Some(identity) = matches.value_of("voting_keypair") {
|
||||||
read_keypair(identity).unwrap_or_else(|err| {
|
read_keypair(identity).unwrap_or_else(|err| {
|
||||||
eprintln!("{}: Unable to open keypair file: {}", err, identity);
|
error!("{}: Unable to open keypair file: {}", err, identity);
|
||||||
exit(1);
|
exit(1);
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -422,7 +437,7 @@ pub fn main() {
|
|||||||
};
|
};
|
||||||
let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") {
|
let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") {
|
||||||
read_keypair(storage_keypair).unwrap_or_else(|err| {
|
read_keypair(storage_keypair).unwrap_or_else(|err| {
|
||||||
eprintln!("{}: Unable to open keypair file: {}", err, storage_keypair);
|
error!("{}: Unable to open keypair file: {}", err, storage_keypair);
|
||||||
exit(1);
|
exit(1);
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -471,7 +486,7 @@ pub fn main() {
|
|||||||
let snapshot_interval_slots = value_t_or_exit!(matches, "snapshot_interval_slots", usize);
|
let snapshot_interval_slots = value_t_or_exit!(matches, "snapshot_interval_slots", usize);
|
||||||
let snapshot_path = ledger_path.clone().join("snapshot");
|
let snapshot_path = ledger_path.clone().join("snapshot");
|
||||||
fs::create_dir_all(&snapshot_path).unwrap_or_else(|err| {
|
fs::create_dir_all(&snapshot_path).unwrap_or_else(|err| {
|
||||||
eprintln!(
|
error!(
|
||||||
"Failed to create snapshots directory {:?}: {}",
|
"Failed to create snapshots directory {:?}: {}",
|
||||||
snapshot_path, err
|
snapshot_path, err
|
||||||
);
|
);
|
||||||
@@ -495,7 +510,7 @@ pub fn main() {
|
|||||||
let entrypoint_addr = solana_netutil::parse_host_port(entrypoint)
|
let entrypoint_addr = solana_netutil::parse_host_port(entrypoint)
|
||||||
.expect("failed to parse entrypoint address");
|
.expect("failed to parse entrypoint address");
|
||||||
let ip_addr = solana_netutil::get_public_ip_addr(&entrypoint_addr).unwrap_or_else(|err| {
|
let ip_addr = solana_netutil::get_public_ip_addr(&entrypoint_addr).unwrap_or_else(|err| {
|
||||||
eprintln!(
|
error!(
|
||||||
"Failed to contact cluster entrypoint {} ({}): {}",
|
"Failed to contact cluster entrypoint {} ({}): {}",
|
||||||
entrypoint, entrypoint_addr, err
|
entrypoint, entrypoint_addr, err
|
||||||
);
|
);
|
||||||
@@ -543,7 +558,7 @@ pub fn main() {
|
|||||||
if let Some(port) = matches.value_of("rpc_port") {
|
if let Some(port) = matches.value_of("rpc_port") {
|
||||||
let port_number = port.to_string().parse().expect("integer");
|
let port_number = port.to_string().parse().expect("integer");
|
||||||
if port_number == 0 {
|
if port_number == 0 {
|
||||||
eprintln!("Invalid RPC port requested: {:?}", port);
|
error!("Invalid RPC port requested: {:?}", port);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
node.info.rpc = SocketAddr::new(node.info.gossip.ip(), port_number);
|
node.info.rpc = SocketAddr::new(node.info.gossip.ip(), port_number);
|
||||||
@@ -551,6 +566,10 @@ pub fn main() {
|
|||||||
tcp_ports = vec![port_number, port_number + 1];
|
tcp_ports = vec![port_number, port_number + 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
validator_config.expected_genesis_blockhash = matches
|
||||||
|
.value_of("expected_genesis_blockhash")
|
||||||
|
.map(|s| Hash::from_str(&s).unwrap());
|
||||||
|
|
||||||
if let Some(ref cluster_entrypoint) = cluster_entrypoint {
|
if let Some(ref cluster_entrypoint) = cluster_entrypoint {
|
||||||
let udp_sockets = [
|
let udp_sockets = [
|
||||||
&node.sockets.gossip,
|
&node.sockets.gossip,
|
||||||
@@ -583,21 +602,31 @@ pub fn main() {
|
|||||||
&udp_sockets,
|
&udp_sockets,
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected_genesis_blockhash = initialize_ledger_path(
|
let genesis_blockhash = initialize_ledger_path(
|
||||||
cluster_entrypoint,
|
cluster_entrypoint,
|
||||||
&ledger_path,
|
&ledger_path,
|
||||||
matches.is_present("no_snapshot_fetch"),
|
matches.is_present("no_snapshot_fetch"),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
eprintln!("Failed to download ledger: {}", err);
|
error!("Failed to download ledger: {}", err);
|
||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
validator_config.expected_genesis_blockhash = Some(expected_genesis_blockhash);
|
|
||||||
|
if let Some(expected_genesis_blockhash) = validator_config.expected_genesis_blockhash {
|
||||||
|
if expected_genesis_blockhash != genesis_blockhash {
|
||||||
|
error!(
|
||||||
|
"Genesis blockhash mismatch: expected {} but local genesis blockhash is {}",
|
||||||
|
expected_genesis_blockhash, genesis_blockhash,
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validator_config.expected_genesis_blockhash = Some(genesis_blockhash);
|
||||||
} else {
|
} else {
|
||||||
// Without a cluster entrypoint, ledger_path must already be present
|
// Without a cluster entrypoint, ledger_path must already be present
|
||||||
if !ledger_path.is_dir() {
|
if !ledger_path.is_dir() {
|
||||||
eprintln!(
|
error!(
|
||||||
"Error: ledger directory does not exist or is not accessible: {:?}",
|
"ledger directory does not exist or is not accessible: {:?}",
|
||||||
ledger_path
|
ledger_path
|
||||||
);
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -618,7 +647,7 @@ pub fn main() {
|
|||||||
|
|
||||||
if let Some(filename) = init_complete_file {
|
if let Some(filename) = init_complete_file {
|
||||||
File::create(filename).unwrap_or_else(|_| {
|
File::create(filename).unwrap_or_else(|_| {
|
||||||
eprintln!("Unable to create: {}", filename);
|
error!("Unable to create: {}", filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user