Compare commits

...

27 Commits

Author SHA1 Message Date
28d24497a3 Wait for the leader to initialize before starting the validators 2018-07-13 12:32:24 -07:00
05cea4c1da dedup 2018-07-13 11:48:17 -07:00
260f5edfd6 Use correct leader.json 2018-07-13 11:48:17 -07:00
7105136595 Enable CUDA for the leader node 2018-07-13 11:36:12 -07:00
54db379bf2 Refresh in parallel 2018-07-13 11:19:31 -07:00
effbf0b978 Add script to refresh testnet nodes 2018-07-13 11:19:31 -07:00
8e7a2a9587 Validators now request an airdrop of 1 token before starting up 2018-07-13 10:02:19 -07:00
18e6ff4167 Fail gracefully when keypair file is unreadable 2018-07-13 10:00:55 -07:00
fa1cdaa91a Add home plugs to enable Snap access to ~/.config/solana/id.json 2018-07-13 09:32:37 -07:00
b538b67524 Bump timeout for stable build
When the CI build machine caches are empty stable occasionally needs more than 20m
2018-07-13 09:17:45 -07:00
2b0f6355af Tagged snap builds now correctly publish to the beta channel 2018-07-12 23:43:59 -07:00
11b9a0323d fixups 2018-07-12 22:51:55 -07:00
710fa822a0 fixups 2018-07-12 22:51:55 -07:00
aaf6ce5aea fixups 2018-07-12 22:51:55 -07:00
34ea483736 step two: supply a ledger file argument to fullnode in the demo
(also whack unused "myip.sh", even though it was pretty)
2018-07-12 22:51:55 -07:00
a3ff40476e Banish stdin/stdout for ledger
step one: accept a "ledger file" argument instead of "outfile"
2018-07-12 22:51:55 -07:00
4cca3ff454 Fix keypair option in scripts
Thanks @CriesofCarrots!
2018-07-12 21:50:28 -06:00
3d9acdd970 Fix nightly 2018-07-12 21:50:28 -06:00
428f220b88 Battle shellcheck 2018-07-12 21:50:28 -06:00
10add6a8ac Cleanup setup.sh 2018-07-12 21:50:28 -06:00
f06a8dceda Fix keygen docs
Thanks @rob-solana
2018-07-12 21:50:28 -06:00
545f4f1c87 Pass the owner's keypair to fullnode-config 2018-07-12 21:50:28 -06:00
77543d83ff Fix default keypair paths 2018-07-12 21:50:28 -06:00
eb6a30cb7c In Wallet, make --tokens required and --to optional 2018-07-12 21:50:28 -06:00
97372b8e63 Add --outfile option to solana-keygen 2018-07-12 21:50:28 -06:00
cea29ed772 More keygen 2018-07-12 21:50:28 -06:00
b5006b8f2b Migrate to solana-keygen
Most of #593
2018-07-12 21:50:28 -06:00
25 changed files with 362 additions and 302 deletions

View File

@ -37,10 +37,6 @@ path = "src/bin/fullnode-config.rs"
name = "solana-genesis"
path = "src/bin/genesis.rs"
[[bin]]
name = "solana-mint"
path = "src/bin/mint.rs"
[[bin]]
name = "solana-drone"
path = "src/bin/drone.rs"
@ -85,6 +81,7 @@ futures = "0.1.21"
clap = "2.31"
reqwest = "0.8.6"
influx_db_client = "0.3.4"
dirs = "1.0.2"
[dev-dependencies]
criterion = "0.2"

View File

@ -3,7 +3,7 @@ steps:
name: "stable [public]"
env:
CARGO_TARGET_CACHE_NAME: "stable"
timeout_in_minutes: 20
timeout_in_minutes: 30
- command: "ci/shellcheck.sh"
name: "shellcheck [public]"
timeout_in_minutes: 20
@ -36,3 +36,9 @@ steps:
- trigger: "solana-snap"
branches: "!pull/*"
async: true
build:
message: "${BUILDKITE_MESSAGE}"
commit: "${BUILDKITE_COMMIT}"
branch: "${BUILDKITE_BRANCH}"
env:
TRIGGERED_BUILDKITE_TAG: "${BUILDKITE_TAG}"

80
ci/refresh-testnet.sh Executable file
View File

@ -0,0 +1,80 @@
#!/bin/bash
#
# Refreshes the Solana software running on the Testnet full nodes
#
# This script must be run by a user/machine that has successfully authenticated
# with GCP and has sufficient permission.
#
if [[ -z $SOLANA_METRICS_CONFIG ]]; then
echo Error: SOLANA_METRICS_CONFIG environment variable is unset
exit 1
fi
# Default to --edge channel. To select the beta channel:
# export SOLANA_METRICS_CONFIG=--beta
if [[ -z $SOLANA_SNAP_CHANNEL ]]; then
SOLANA_SNAP_CHANNEL=--edge
fi
vmlist=(testnet-solana-com:us-west1-b) # Leader is hard coded as the first entry
echo "--- Available validators"
gcloud compute instances list --filter="labels.testnet-mode=validator"
while read -r vmName vmZone status; do
if [[ $status != RUNNING ]]; then
echo "Warning: $vmName is not RUNNING, ignoring it."
continue
fi
vmlist+=("$vmName:$vmZone")
done < <(gcloud compute instances list --filter="labels.testnet-mode=validator" --format 'value(name,zone,status)')
echo "--- Refreshing"
leader=true
for info in "${vmlist[@]}"; do
vmName=${info%:*}
vmZone=${info#*:}
echo "Starting refresh for $vmName"
(
echo "--- Processing $vmName in zone $vmZone"
if $leader; then
nodeConfig="mode=leader+drone enable-cuda=1 metrics-config=$SOLANA_METRICS_CONFIG"
else
nodeConfig="mode=validator metrics-config=$SOLANA_METRICS_CONFIG"
fi
cat > "autogen-refresh-$vmName.sh" <<EOF
set -x
sudo snap remove solana
sudo snap install solana $SOLANA_SNAP_CHANNEL --devmode
sudo snap set solana $nodeConfig
snap info solana
sudo snap logs solana -n200
EOF
set -x
gcloud compute scp --zone "$vmZone" "autogen-refresh-$vmName.sh" "$vmName":
gcloud compute ssh "$vmName" --zone "$vmZone" \
--ssh-flag="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t" \
--command="bash ./autogen-refresh-$vmName.sh"
) > "log-$vmName.txt" 2>&1 &
if $leader; then
echo Waiting for leader...
# Wait for the leader to initialize before starting the validators
# TODO: Remove this limitation eventually.
wait
fi
leader=false
done
echo Waiting for validators...
wait
for info in "${vmlist[@]}"; do
vmName=${info%:*}
cat "log-$vmName.txt"
done
echo "--- done"
exit 0

View File

@ -7,7 +7,11 @@ if [[ -z $BUILDKITE_BRANCH ]] || ./ci/is-pr.sh; then
DRYRUN="echo"
fi
if [[ -z "$BUILDKITE_TAG" ]]; then
# BUILDKITE_TAG is the normal environment variable set by Buildkite. However
# when this script is run from a triggered pipeline, TRIGGERED_BUILDKITE_TAG is
# used instead of BUILDKITE_TAG (due to Buildkite limitations that prevents
# BUILDKITE_TAG from propagating through to triggered pipelines)
if [[ -z "$BUILDKITE_TAG" && -z "$TRIGGERED_BUILDKITE_TAG" ]]; then
SNAP_CHANNEL=edge
else
SNAP_CHANNEL=beta

View File

@ -23,9 +23,9 @@ fi
client_json="$SOLANA_CONFIG_CLIENT_DIR"/client.json
if [[ ! -r $client_json ]]; then
$solana_mint <<<0 > "$client_json"
$solana_keygen -o "$client_json"
fi
# shellcheck disable=SC2086 # $solana_client_demo should not be quoted
exec $solana_client_demo \
-n "$count" -l "$SOLANA_CONFIG_CLIENT_DIR"/leader.json -m "$SOLANA_CONFIG_CLIENT_DIR"/client.json
-n "$count" -l "$SOLANA_CONFIG_CLIENT_DIR"/leader.json -k "$SOLANA_CONFIG_CLIENT_DIR"/client.json

View File

@ -53,7 +53,7 @@ solana_fullnode=$(solana_program fullnode)
solana_fullnode_config=$(solana_program fullnode-config)
solana_fullnode_cuda=$(solana_program fullnode-cuda)
solana_genesis=$(solana_program genesis)
solana_mint=$(solana_program mint)
solana_keygen=$(solana_program keygen)
export RUST_LOG=${RUST_LOG:-solana=info} # if RUST_LOG is unset, default to info
export RUST_BACKTRACE=1
@ -107,15 +107,19 @@ tune_networking() {
# Reference: https://medium.com/@CameronSparr/increase-os-udp-buffers-to-improve-performance-51d167bb1360
[[ $(uname) = Linux ]] && (
set -x
# TODO: Check values and warn instead, it's a little rude to set them here.
sudo sysctl -w net.core.rmem_max=26214400 1>/dev/null 2>/dev/null
sudo sysctl -w net.core.rmem_default=26214400 1>/dev/null 2>/dev/null
# test the existence of the sysctls before trying to set them
sysctl net.core.rmem_max 2>/dev/null 1>/dev/null &&
sudo sysctl -w net.core.rmem_max=26214400 1>/dev/null 2>/dev/null
sysctl net.core.rmem_default 2>/dev/null 1>/dev/null &&
sudo sysctl -w net.core.rmem_default=26214400 1>/dev/null 2>/dev/null
)
return 0
}
SOLANA_CONFIG_DIR=${SNAP_DATA:-$PWD}/config
SOLANA_CONFIG_PRIVATE_DIR=${SNAP_DATA:-$PWD}/config-private
SOLANA_CONFIG_CLIENT_DIR=${SNAP_USER_DATA:-$PWD}/config-client-client
SOLANA_CONFIG_CLIENT_DIR=${SNAP_USER_DATA:-$PWD}/config-client
rsync_url() { # adds the 'rsync://` prefix to URLs that need it
declare url="$1"

View File

@ -38,4 +38,4 @@ $rsync -vPz "$rsync_leader_url"/config/leader.json "$SOLANA_CONFIG_DIR"/
# shellcheck disable=SC2086 # $solana_drone should not be quoted
exec $solana_drone \
-l "$SOLANA_CONFIG_DIR"/leader.json -m "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json
-l "$SOLANA_CONFIG_DIR"/leader.json -k "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json

View File

@ -25,9 +25,15 @@ fi
tune_networking
# migrate from old ledger format? why not...
if [[ ! -f "$SOLANA_CONFIG_DIR"/ledger.log &&
-f "$SOLANA_CONFIG_DIR"/genesis.log ]]; then
(shopt -s nullglob &&
cat "$SOLANA_CONFIG_DIR"/genesis.log \
"$SOLANA_CONFIG_DIR"/tx-*.log) > "$SOLANA_CONFIG_DIR"/ledger.log
fi
# shellcheck disable=SC2086 # $program should not be quoted
exec $program \
-l "$SOLANA_CONFIG_DIR"/leader.json \
< <(shopt -s nullglob && cat "$SOLANA_CONFIG_DIR"/genesis.log \
"$SOLANA_CONFIG_DIR"/tx-*.log) \
> "$SOLANA_CONFIG_DIR"/tx-"$(date -u +%Y%m%d%H%M%S%N)".log
--identity "$SOLANA_CONFIG_DIR"/leader.json \
--ledger "$SOLANA_CONFIG_DIR"/ledger.log

View File

@ -1,59 +0,0 @@
#!/bin/bash
function myip()
{
# shellcheck disable=SC2207
declare ipaddrs=(
# query interwebs
$(curl -s ifconfig.co)
# machine's interfaces
$(ifconfig |
awk '/inet addr:/ {gsub("addr:","",$2); print $2; next}
/inet6 addr:/ {gsub("/.*", "", $3); print $3; next}
/inet(6)? / {print $2}'
)
)
if (( ! ${#ipaddrs[*]} ))
then
echo "
myip: error: I'm having trouble determining what our IP address is...
Are we connected to a network?
"
return 1
fi
declare prompt="
Please choose the IP address you want to advertise to the network:
0) ${ipaddrs[0]} <====== this one was returned by the interwebs...
"
for ((i=1; i < ${#ipaddrs[*]}; i++))
do
prompt+=" $i) ${ipaddrs[i]}
"
done
while read -r -p "${prompt}
please enter a number [0 for default]: " which
do
[[ -z ${which} ]] && break;
[[ ${which} =~ [0-9]+ ]] && (( which < ${#ipaddrs[*]} )) && break;
echo "Ug. invalid entry \"${which}\"...
"
sleep 1
done
which=${which:-0}
echo "${ipaddrs[which]}"
}
if [[ ${0} == "${BASH_SOURCE[0]}" ]]
then
myip "$@"
fi

View File

@ -71,6 +71,8 @@ done
leader_address_args=("$ip_address_arg")
validator_address_args=("$ip_address_arg" -b 9000)
id_path="$SOLANA_CONFIG_PRIVATE_DIR"/id.json
mint_path="$SOLANA_CONFIG_PRIVATE_DIR"/mint.json
set -e
@ -78,25 +80,26 @@ echo "Cleaning $SOLANA_CONFIG_DIR"
rm -rvf "$SOLANA_CONFIG_DIR"
mkdir -p "$SOLANA_CONFIG_DIR"
rm -rvf "$SOLANA_CONFIG_PRIVATE_DIR"
mkdir -p "$SOLANA_CONFIG_PRIVATE_DIR"
$solana_keygen -o "$id_path"
if $node_type_leader; then
rm -rvf "$SOLANA_CONFIG_PRIVATE_DIR"
mkdir -p "$SOLANA_CONFIG_PRIVATE_DIR"
echo "Creating $SOLANA_CONFIG_DIR/mint.json with $num_tokens tokens"
$solana_mint <<<"$num_tokens" > "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json
$solana_keygen -o "$mint_path"
echo "Creating $SOLANA_CONFIG_DIR/genesis.log"
$solana_genesis < "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json > "$SOLANA_CONFIG_DIR"/genesis.log
echo "Creating $SOLANA_CONFIG_DIR/ledger.log"
$solana_genesis --tokens="$num_tokens" < "$mint_path" > "$SOLANA_CONFIG_DIR"/ledger.log
echo "Creating $SOLANA_CONFIG_DIR/leader.json"
$solana_fullnode_config "${leader_address_args[@]}" > "$SOLANA_CONFIG_DIR"/leader.json
$solana_fullnode_config --keypair="$id_path" "${leader_address_args[@]}" > "$SOLANA_CONFIG_DIR"/leader.json
fi
if $node_type_validator; then
echo "Creating $SOLANA_CONFIG_DIR/validator.json"
$solana_fullnode_config "${validator_address_args[@]}" > "$SOLANA_CONFIG_DIR"/validator.json
$solana_fullnode_config --keypair="$id_path" "${validator_address_args[@]}" > "$SOLANA_CONFIG_DIR"/validator.json
fi
ls -lh "$SOLANA_CONFIG_DIR"/

View File

@ -73,8 +73,24 @@ ls -lh "$SOLANA_LEADER_CONFIG_DIR"
tune_networking
# migrate from old ledger format? why not...
if [[ ! -f "$SOLANA_LEADER_CONFIG_DIR"/ledger.log &&
-f "$SOLANA_LEADER_CONFIG_DIR"/genesis.log ]]; then
(shopt -s nullglob &&
cat "$SOLANA_LEADER_CONFIG_DIR"/genesis.log \
"$SOLANA_LEADER_CONFIG_DIR"/tx-*.log) > "$SOLANA_LEADER_CONFIG_DIR"/ledger.log
fi
# Ensure the validator has at least 1 token before connecting to the network
# TODO: Remove this workaround
while ! $solana_wallet \
-l "$SOLANA_LEADER_CONFIG_DIR"/leader.json \
-k "$SOLANA_CONFIG_PRIVATE_DIR"/id.json airdrop --tokens 1; do
sleep 1
done
# shellcheck disable=SC2086 # $program should not be quoted
exec $program \
-l "$SOLANA_CONFIG_DIR"/validator.json -t "$leader_address:$leader_port" \
< <(shopt -s nullglob && cat "$SOLANA_LEADER_CONFIG_DIR"/genesis.log \
"$SOLANA_LEADER_CONFIG_DIR"/tx-*.log)
--identity "$SOLANA_CONFIG_DIR"/validator.json \
--testnet "$leader_address:$leader_port" \
--ledger "$SOLANA_LEADER_CONFIG_DIR"/ledger.log

View File

@ -36,12 +36,12 @@ if [[ ! -r "$SOLANA_CONFIG_CLIENT_DIR"/leader.json ]]; then
)
fi
client_json="$SOLANA_CONFIG_CLIENT_DIR"/client.json
if [[ ! -r $client_json ]]; then
$solana_mint <<<0 > "$client_json"
client_id_path="$SOLANA_CONFIG_CLIENT_DIR"/id.json
if [[ ! -r $client_id_path ]]; then
$solana_keygen -o "$client_id_path"
fi
set -x
# shellcheck disable=SC2086 # $solana_wallet should not be quoted
exec $solana_wallet \
-l "$SOLANA_CONFIG_CLIENT_DIR"/leader.json -m "$client_json" "$@"
-l "$SOLANA_CONFIG_CLIENT_DIR"/leader.json -k "$client_id_path" "$@"

View File

@ -37,16 +37,26 @@ apps:
plugs:
- network
- network-bind
- home
genesis:
command: solana-genesis
mint:
command: solana-mint
keygen:
command: solana-keygen
plugs:
- home
client-demo:
command: solana-client-demo
plugs:
- network
- network-bind
- home
wallet:
# TODO: Merge wallet.sh functionality into solana-wallet proper
command: wallet.sh
#command: solana-wallet
plugs:
- network
- home
daemon-validator:
daemon: simple

View File

@ -12,11 +12,10 @@ use solana::crdt::{Crdt, NodeInfo};
use solana::drone::DroneRequest;
use solana::fullnode::Config;
use solana::hash::Hash;
use solana::mint::Mint;
use solana::nat::{udp_public_bind, udp_random_bind};
use solana::ncp::Ncp;
use solana::service::Service;
use solana::signature::{GenKeys, KeyPair, KeyPairUtil};
use solana::signature::{read_keypair, GenKeys, KeyPair, KeyPairUtil};
use solana::streamer::default_window;
use solana::thin_client::ThinClient;
use solana::timing::{duration_as_ms, duration_as_s};
@ -77,7 +76,7 @@ fn sample_tx_count(
fn generate_and_send_txs(
client: &mut ThinClient,
tx_clients: &[ThinClient],
id: &Mint,
id: &KeyPair,
keypairs: &[KeyPair],
leader: &NodeInfo,
txs: i64,
@ -91,7 +90,7 @@ fn generate_and_send_txs(
let transactions: Vec<_> = if !reclaim {
keypairs
.par_iter()
.map(|keypair| Transaction::new(&id.keypair(), keypair.pubkey(), 1, *last_id))
.map(|keypair| Transaction::new(&id, keypair.pubkey(), 1, *last_id))
.collect()
} else {
keypairs
@ -164,12 +163,13 @@ fn main() {
.help("/path/to/leader.json"),
)
.arg(
Arg::with_name("mint")
.short("m")
.long("mint")
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/mint.json"),
.default_value("~/.config/solana/id.json")
.help("/path/to/id.json"),
)
.arg(
Arg::with_name("num_nodes")
@ -205,13 +205,7 @@ fn main() {
leader = NodeInfo::new_leader(&server_addr);
};
let id: Mint;
if let Some(m) = matches.value_of("mint") {
id = read_mint(m).expect("client mint");
} else {
eprintln!("No mint found!");
exit(1);
};
let id = read_keypair(matches.value_of("keypair").unwrap()).expect("client keypair");
if let Some(t) = matches.value_of("threads") {
threads = t.to_string().parse().expect("integer");
@ -259,7 +253,7 @@ fn main() {
println!("Got last ID {:?}", last_id);
let mut seed = [0u8; 32];
seed.copy_from_slice(&id.keypair().public_key_bytes()[..32]);
seed.copy_from_slice(&id.public_key_bytes()[..32]);
let rnd = GenKeys::new(seed);
println!("Creating keypairs...");
@ -433,15 +427,9 @@ fn read_leader(path: &str) -> Config {
serde_json::from_reader(file).unwrap_or_else(|_| panic!("failed to parse {}", path))
}
fn read_mint(path: &str) -> Result<Mint, Box<error::Error>> {
let file = File::open(path.to_string())?;
let mint = serde_json::from_reader(file)?;
Ok(mint)
}
fn request_airdrop(
drone_addr: &SocketAddr,
id: &Mint,
id: &KeyPair,
tokens: u64,
) -> Result<(), Box<error::Error>> {
let mut stream = TcpStream::connect(drone_addr)?;

View File

@ -12,11 +12,9 @@ use clap::{App, Arg};
use solana::crdt::NodeInfo;
use solana::drone::{Drone, DroneRequest};
use solana::fullnode::Config;
use solana::mint::Mint;
use std::error;
use solana::signature::read_keypair;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::process::exit;
use std::sync::{Arc, Mutex};
use std::thread;
use tokio::net::TcpListener;
@ -35,11 +33,12 @@ fn main() {
.help("/path/to/leader.json"),
)
.arg(
Arg::with_name("mint")
.short("m")
.long("mint")
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.required(true)
.help("/path/to/mint.json"),
)
.arg(
@ -68,13 +67,9 @@ fn main() {
leader = NodeInfo::new_leader(&server_addr);
};
let mint: Mint;
if let Some(m) = matches.value_of("mint") {
mint = read_mint(m).expect("client mint");
} else {
eprintln!("No mint found!");
exit(1);
};
let mint_keypair =
read_keypair(matches.value_of("keypair").expect("keypair")).expect("client keypair");
let time_slice: Option<u64>;
if let Some(t) = matches.value_of("time") {
time_slice = Some(t.to_string().parse().expect("integer"));
@ -88,8 +83,6 @@ fn main() {
request_cap = None;
}
let mint_keypair = mint.keypair();
let drone_addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
let drone = Arc::new(Mutex::new(Drone::new(
@ -152,9 +145,3 @@ fn read_leader(path: &str) -> Config {
let file = File::open(path).unwrap_or_else(|_| panic!("file not found: {}", path));
serde_json::from_reader(file).unwrap_or_else(|_| panic!("failed to parse {}", path))
}
fn read_mint(path: &str) -> Result<Mint, Box<error::Error>> {
let file = File::open(path.to_string())?;
let mint = serde_json::from_reader(file)?;
Ok(mint)
}

View File

@ -1,4 +1,5 @@
extern crate clap;
extern crate dirs;
extern crate serde_json;
extern crate solana;
@ -6,6 +7,7 @@ use clap::{App, Arg};
use solana::crdt::{get_ip_addr, parse_port_or_addr};
use solana::fullnode::Config;
use solana::nat::get_public_ip_addr;
use solana::signature::read_pkcs8;
use std::io;
use std::net::SocketAddr;
@ -18,6 +20,14 @@ fn main() {
.takes_value(false)
.help("detect network address from local machine configuration"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/id.json"),
)
.arg(
Arg::with_name("public")
.short("p")
@ -54,9 +64,18 @@ fn main() {
bind_addr
};
let mut path = dirs::home_dir().expect("home directory");
let id_path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap()
} else {
path.extend(&[".config", "solana", "id.json"]);
path.to_str().unwrap()
};
let pkcs8 = read_pkcs8(id_path).expect("client keypair");
// we need all the receiving sockets to be bound within the expected
// port range that we open on aws
let config = Config::new(&bind_addr);
let config = Config::new(&bind_addr, pkcs8);
let stdout = io::stdout();
serde_json::to_writer(stdout, &config).expect("serialize");
}

View File

@ -1,4 +1,3 @@
extern crate atty;
extern crate clap;
extern crate env_logger;
extern crate getopts;
@ -6,10 +5,9 @@ extern crate log;
extern crate serde_json;
extern crate solana;
use atty::{is, Stream};
use clap::{App, Arg};
use solana::crdt::{NodeInfo, TestNode};
use solana::fullnode::{Config, FullNode, InFile, OutFile};
use solana::fullnode::{Config, FullNode, LedgerFile};
use solana::service::Service;
use solana::signature::{KeyPair, KeyPairUtil};
use std::fs::File;
@ -22,8 +20,8 @@ fn main() -> () {
let matches = App::new("fullnode")
.arg(
Arg::with_name("identity")
.short("l")
.long("local")
.short("i")
.long("identity")
.value_name("FILE")
.takes_value(true)
.help("run with the identity found in FILE"),
@ -34,27 +32,23 @@ fn main() -> () {
.long("testnet")
.value_name("HOST:PORT")
.takes_value(true)
.help("testnet; connect to the network at this gossip entry point"),
.help("connect to the network at this gossip entry point"),
)
.arg(
Arg::with_name("output")
.short("o")
.long("output")
Arg::with_name("ledger")
.short("L")
.long("ledger")
.value_name("FILE")
.takes_value(true)
.help("output log to FILE, defaults to stdout (ignored by validators)"),
.help("use FILE as persistent ledger (defaults to stdin/stdout)"),
)
.get_matches();
if is(Stream::Stdin) {
eprintln!("nothing found on stdin, expected a log file");
exit(1);
}
let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
let mut keypair = KeyPair::new();
let mut repl_data = NodeInfo::new_leader_with_pubkey(keypair.pubkey(), &bind_addr);
if let Some(l) = matches.value_of("identity") {
let path = l.to_string();
if let Some(i) = matches.value_of("identity") {
let path = i.to_string();
if let Ok(file) = File::open(path.clone()) {
let parse: serde_json::Result<Config> = serde_json::from_reader(file);
if let Ok(data) = parse {
@ -69,27 +63,22 @@ fn main() -> () {
exit(1);
}
}
let ledger = if let Some(l) = matches.value_of("ledger") {
LedgerFile::Path(l.to_string())
} else {
LedgerFile::StdInOut
};
let mut node = TestNode::new_with_bind_addr(repl_data, bind_addr);
let fullnode = if let Some(t) = matches.value_of("testnet") {
let testnet_address_string = t.to_string();
let testnet_addr = testnet_address_string.parse().unwrap();
FullNode::new(
node,
false,
InFile::StdIn,
Some(keypair),
Some(testnet_addr),
None,
)
FullNode::new(node, false, ledger, Some(keypair), Some(testnet_addr))
} else {
node.data.leader_id = node.data.id;
let outfile = if let Some(o) = matches.value_of("output") {
OutFile::Path(o.to_string())
} else {
OutFile::StdOut
};
FullNode::new(node, true, InFile::StdIn, None, None, Some(outfile))
FullNode::new(node, true, ledger, None, None)
};
fullnode.join().expect("join");
}

View File

@ -1,10 +1,13 @@
//! A command-line executable for generating the chain's genesis block.
extern crate atty;
#[macro_use]
extern crate clap;
extern crate serde_json;
extern crate solana;
use atty::{is, Stream};
use clap::{App, Arg};
use solana::entry_writer::EntryWriter;
use solana::mint::Mint;
use std::error;
@ -12,6 +15,20 @@ use std::io::{stdin, stdout, Read};
use std::process::exit;
fn main() -> Result<(), Box<error::Error>> {
let matches = App::new("solana-genesis")
.arg(
Arg::with_name("tokens")
.short("t")
.long("tokens")
.value_name("NUMBER")
.takes_value(true)
.required(true)
.help("Number of tokens with which to initialize mint"),
)
.get_matches();
let tokens = value_t_or_exit!(matches, "tokens", i64);
if is(Stream::Stdin) {
eprintln!("nothing found on stdin, expected a json file");
exit(1);
@ -24,7 +41,9 @@ fn main() -> Result<(), Box<error::Error>> {
exit(1);
}
let mint: Mint = serde_json::from_str(&buffer)?;
let pkcs8: Vec<u8> = serde_json::from_str(&buffer)?;
let mint = Mint::new_with_pkcs8(tokens, pkcs8);
let mut writer = stdout();
EntryWriter::write_entries(&mut writer, mint.create_entries())?;
Ok(())

View File

@ -1,14 +1,49 @@
extern crate clap;
extern crate dirs;
extern crate ring;
extern crate serde_json;
use clap::{App, Arg};
use ring::rand::SystemRandom;
use ring::signature::Ed25519KeyPair;
use std::error;
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
fn main() -> Result<(), Box<error::Error>> {
let matches = App::new("solana-keygen")
.arg(
Arg::with_name("outfile")
.short("o")
.long("outfile")
.value_name("PATH")
.takes_value(true)
.help("path to generated file"),
)
.get_matches();
let rnd = SystemRandom::new();
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rnd)?;
let serialized = serde_json::to_string(&pkcs8_bytes.to_vec())?;
println!("{}", serialized);
let mut path = dirs::home_dir().expect("home directory");
let outfile = if matches.is_present("outfile") {
matches.value_of("outfile").unwrap()
} else {
path.extend(&[".config", "solana", "id.json"]);
path.to_str().unwrap()
};
if outfile == "-" {
println!("{}", serialized);
} else {
if let Some(outdir) = Path::new(outfile).parent() {
fs::create_dir_all(outdir)?;
}
let mut f = File::create(outfile)?;
f.write_all(&serialized.into_bytes())?;
}
Ok(())
}

View File

@ -1,29 +0,0 @@
extern crate atty;
extern crate serde_json;
extern crate solana;
use atty::{is, Stream};
use solana::mint::Mint;
use std::io;
use std::process::exit;
fn main() {
let mut input_text = String::new();
if is(Stream::Stdin) {
eprintln!("nothing found on stdin, expected a token number");
exit(1);
}
io::stdin().read_line(&mut input_text).unwrap();
let trimmed = input_text.trim();
let tokens = trimmed.parse::<i64>().unwrap_or_else(|e| {
eprintln!("{}", e);
exit(1);
});
let mint = Mint::new(tokens);
let serialized = serde_json::to_string(&mint).unwrap_or_else(|e| {
eprintln!("failed to serialize: {}", e);
exit(1);
});
println!("{}", serialized);
}

View File

@ -2,6 +2,7 @@ extern crate atty;
extern crate bincode;
extern crate bs58;
extern crate clap;
extern crate dirs;
extern crate env_logger;
extern crate serde_json;
extern crate solana;
@ -11,8 +12,7 @@ use clap::{App, Arg, SubCommand};
use solana::crdt::NodeInfo;
use solana::drone::DroneRequest;
use solana::fullnode::Config;
use solana::mint::Mint;
use solana::signature::{PublicKey, Signature};
use solana::signature::{read_keypair, KeyPair, KeyPairUtil, PublicKey, Signature};
use solana::thin_client::ThinClient;
use std::error;
use std::fmt;
@ -20,7 +20,6 @@ use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream, UdpSocket};
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;
@ -57,7 +56,7 @@ impl error::Error for WalletError {
struct WalletConfig {
leader: NodeInfo,
id: Mint,
id: KeyPair,
drone_addr: SocketAddr,
command: WalletCommand,
}
@ -67,7 +66,7 @@ impl Default for WalletConfig {
let default_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
WalletConfig {
leader: NodeInfo::new_leader(&default_addr),
id: Mint::new(0),
id: KeyPair::new(),
drone_addr: default_addr,
command: WalletCommand::Balance,
}
@ -85,12 +84,12 @@ fn parse_args() -> Result<WalletConfig, Box<error::Error>> {
.help("/path/to/leader.json"),
)
.arg(
Arg::with_name("mint")
.short("m")
.long("mint")
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/mint.json"),
.help("/path/to/id.json"),
)
.subcommand(
SubCommand::with_name("airdrop")
@ -101,6 +100,7 @@ fn parse_args() -> Result<WalletConfig, Box<error::Error>> {
.long("tokens")
.value_name("NUMBER")
.takes_value(true)
.required(true)
.help("The number of tokens to request"),
),
)
@ -122,7 +122,6 @@ fn parse_args() -> Result<WalletConfig, Box<error::Error>> {
.long("to")
.value_name("PUBKEY")
.takes_value(true)
.required(true)
.help("The pubkey of recipient"),
),
)
@ -149,24 +148,27 @@ fn parse_args() -> Result<WalletConfig, Box<error::Error>> {
leader = NodeInfo::new_leader(&server_addr);
};
let id: Mint;
if let Some(m) = matches.value_of("mint") {
id = read_mint(m)?;
let mut path = dirs::home_dir().expect("home directory");
let id_path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap()
} else {
eprintln!("No mint found!");
exit(1);
path.extend(&[".config", "solana", "id.json"]);
path.to_str().unwrap()
};
let id = read_keypair(id_path).or_else(|err| {
display_actions();
Err(WalletError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, id_path
)))
})?;
let mut drone_addr = leader.contact_info.tpu;
drone_addr.set_port(9900);
let command = match matches.subcommand() {
("airdrop", Some(airdrop_matches)) => {
let tokens = if airdrop_matches.is_present("tokens") {
airdrop_matches.value_of("tokens").unwrap().parse()?
} else {
id.tokens
};
let tokens = airdrop_matches.value_of("tokens").unwrap().parse()?;
Ok(WalletCommand::AirDrop(tokens))
}
("pay", Some(pay_matches)) => {
@ -184,11 +186,7 @@ fn parse_args() -> Result<WalletConfig, Box<error::Error>> {
id.pubkey()
};
let tokens = if pay_matches.is_present("tokens") {
pay_matches.value_of("tokens").unwrap().parse()?
} else {
id.tokens
};
let tokens = pay_matches.value_of("tokens").unwrap().parse()?;
Ok(WalletCommand::Pay(tokens, to))
}
@ -264,7 +262,7 @@ fn process_command(
// If client has positive balance, spend tokens in {balance} number of transactions
WalletCommand::Pay(tokens, to) => {
let last_id = client.get_last_id();
let sig = client.transfer(tokens, &config.id.keypair(), to, &last_id)?;
let sig = client.transfer(tokens, &config.id, to, &last_id)?;
println!("{}", bs58::encode(sig).into_string());
}
// Confirm the last client transaction by signature
@ -295,12 +293,6 @@ fn read_leader(path: &str) -> Config {
serde_json::from_reader(file).unwrap_or_else(|_| panic!("failed to parse {}", path))
}
fn read_mint(path: &str) -> Result<Mint, Box<error::Error>> {
let file = File::open(path.to_string())?;
let mint = serde_json::from_reader(file)?;
Ok(mint)
}
fn mk_client(r: &NodeInfo) -> io::Result<ThinClient> {
let requests_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
@ -318,7 +310,7 @@ fn mk_client(r: &NodeInfo) -> io::Result<ThinClient> {
fn request_airdrop(
drone_addr: &SocketAddr,
id: &Mint,
id: &KeyPair,
tokens: u64,
) -> Result<(), Box<error::Error>> {
let mut stream = TcpStream::connect(drone_addr)?;

View File

@ -7,13 +7,12 @@ use entry_writer;
use ledger::Block;
use ncp::Ncp;
use packet::BlobRecycler;
use ring::rand::SystemRandom;
use rpu::Rpu;
use service::Service;
use signature::{KeyPair, KeyPairUtil};
use std::collections::VecDeque;
use std::fs::{File, OpenOptions};
use std::io::{sink, stdin, stdout, BufReader};
use std::io::{stdin, stdout, BufReader};
use std::io::{Read, Write};
use std::net::SocketAddr;
use std::sync::atomic::{AtomicBool, Ordering};
@ -31,13 +30,8 @@ pub struct FullNode {
thread_hdls: Vec<JoinHandle<()>>,
}
pub enum InFile {
StdIn,
Path(String),
}
pub enum OutFile {
StdOut,
pub enum LedgerFile {
StdInOut,
Path(String),
}
@ -50,11 +44,7 @@ pub struct Config {
/// Structure to be replicated by the network
impl Config {
pub fn new(bind_addr: &SocketAddr) -> Self {
let rnd = SystemRandom::new();
let pkcs8 = KeyPair::generate_pkcs8(&rnd)
.expect("generate_pkcs8 in mint pub fn new")
.to_vec();
pub fn new(bind_addr: &SocketAddr, pkcs8: Vec<u8>) -> Self {
let keypair =
KeyPair::from_pkcs8(Input::from(&pkcs8)).expect("from_pkcs8 in fullnode::Config new");
let pubkey = keypair.pubkey();
@ -71,16 +61,24 @@ impl FullNode {
pub fn new(
mut node: TestNode,
leader: bool,
infile: InFile,
ledger: LedgerFile,
keypair_for_validator: Option<KeyPair>,
network_entry_for_validator: Option<SocketAddr>,
outfile_for_leader: Option<OutFile>,
) -> FullNode {
info!("creating bank...");
let bank = Bank::default();
let infile: Box<Read> = match infile {
InFile::Path(path) => Box::new(File::open(path).unwrap()),
InFile::StdIn => Box::new(stdin()),
let (infile, outfile): (Box<Read>, Box<Write + Send>) = match ledger {
LedgerFile::Path(path) => (
Box::new(File::open(path.clone()).expect("opening ledger file")),
Box::new(
OpenOptions::new()
.create(true)
.append(true)
.open(path)
.expect("opening ledger file"),
),
),
LedgerFile::StdInOut => (Box::new(stdin()), Box::new(stdout())),
};
let reader = BufReader::new(infile);
let entries = entry_writer::read_entries(reader).map(|e| e.expect("failed to parse entry"));
@ -122,17 +120,6 @@ impl FullNode {
server
} else {
node.data.leader_id = node.data.id;
let outfile_for_leader: Box<Write + Send> = match outfile_for_leader {
Some(OutFile::Path(file)) => Box::new(
OpenOptions::new()
.create(true)
.append(true)
.open(file)
.expect("opening ledger file"),
),
Some(OutFile::StdOut) => Box::new(stdout()),
None => Box::new(sink()),
};
let server = FullNode::new_leader(
bank,
@ -142,7 +129,7 @@ impl FullNode {
None,
node,
exit.clone(),
outfile_for_leader,
outfile,
);
info!(
"leader ready... local request address: {} (advertising {})",

View File

@ -15,11 +15,7 @@ pub struct Mint {
}
impl Mint {
pub fn new(tokens: i64) -> Self {
let rnd = SystemRandom::new();
let pkcs8 = KeyPair::generate_pkcs8(&rnd)
.expect("generate_pkcs8 in mint pub fn new")
.to_vec();
pub fn new_with_pkcs8(tokens: i64, pkcs8: Vec<u8>) -> Self {
let keypair =
KeyPair::from_pkcs8(Input::from(&pkcs8)).expect("from_pkcs8 in mint pub fn new");
let pubkey = keypair.pubkey();
@ -29,6 +25,15 @@ impl Mint {
tokens,
}
}
pub fn new(tokens: i64) -> Self {
let rnd = SystemRandom::new();
let pkcs8 = KeyPair::generate_pkcs8(&rnd)
.expect("generate_pkcs8 in mint pub fn new")
.to_vec();
Self::new_with_pkcs8(tokens, pkcs8)
}
pub fn seed(&self) -> Hash {
hash(&self.pkcs8)
}

View File

@ -8,8 +8,11 @@ use ring::error::Unspecified;
use ring::rand::SecureRandom;
use ring::signature::Ed25519KeyPair;
use ring::{rand, signature};
use serde_json;
use std::cell::RefCell;
use untrusted;
use std::error;
use std::fs::File;
use untrusted::Input;
pub type KeyPair = Ed25519KeyPair;
pub type PublicKey = GenericArray<u8, U32>;
@ -24,10 +27,8 @@ impl KeyPairUtil for Ed25519KeyPair {
/// Return a new ED25519 keypair
fn new() -> Self {
let rng = rand::SystemRandom::new();
let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng)
.expect("generate_pkcs8 in signature pb fn new");
signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes))
.expect("from_pcks8 in signature pb fn new")
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng).expect("generate_pkcs8");
Ed25519KeyPair::from_pkcs8(Input::from(&pkcs8_bytes)).expect("from_pcks8")
}
/// Return the public key for the given keypair
@ -42,9 +43,9 @@ pub trait SignatureUtil {
impl SignatureUtil for GenericArray<u8, U64> {
fn verify(&self, peer_public_key_bytes: &[u8], msg_bytes: &[u8]) -> bool {
let peer_public_key = untrusted::Input::from(peer_public_key_bytes);
let msg = untrusted::Input::from(msg_bytes);
let sig = untrusted::Input::from(self);
let peer_public_key = Input::from(peer_public_key_bytes);
let msg = Input::from(msg_bytes);
let sig = Input::from(self);
signature::verify(&signature::ED25519, peer_public_key, msg, sig).is_ok()
}
}
@ -77,7 +78,7 @@ impl GenKeys {
.into_par_iter()
.map(|seed| {
let pkcs8 = GenKeys::new(seed).new_key();
KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8)).unwrap()
KeyPair::from_pkcs8(Input::from(&pkcs8)).unwrap()
})
.collect()
}
@ -91,6 +92,18 @@ impl SecureRandom for GenKeys {
}
}
pub fn read_pkcs8(path: &str) -> Result<Vec<u8>, Box<error::Error>> {
let file = File::open(path.to_string())?;
let pkcs8: Vec<u8> = serde_json::from_reader(file)?;
Ok(pkcs8)
}
pub fn read_keypair(path: &str) -> Result<KeyPair, Box<error::Error>> {
let pkcs8 = read_pkcs8(path)?;
let keypair = Ed25519KeyPair::from_pkcs8(Input::from(&pkcs8))?;
Ok(keypair)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -7,7 +7,7 @@ extern crate solana;
use solana::crdt::TestNode;
use solana::crdt::{Crdt, NodeInfo};
use solana::entry_writer::EntryWriter;
use solana::fullnode::{FullNode, InFile, OutFile};
use solana::fullnode::{FullNode, LedgerFile};
use solana::logger;
use solana::mint::Mint;
use solana::ncp::Ncp;
@ -94,8 +94,7 @@ fn test_multi_node_validator_catchup_from_zero() {
let server = FullNode::new(
leader,
true,
InFile::Path(ledger_path.clone()),
None,
LedgerFile::Path(ledger_path.clone()),
None,
None,
);
@ -106,10 +105,9 @@ fn test_multi_node_validator_catchup_from_zero() {
let mut val = FullNode::new(
validator,
false,
InFile::Path(ledger_path.clone()),
LedgerFile::Path(ledger_path.clone()),
Some(keypair),
Some(leader_data.contact_info.ncp),
None,
);
nodes.push(val);
}
@ -141,10 +139,9 @@ fn test_multi_node_validator_catchup_from_zero() {
let val = FullNode::new(
validator,
false,
InFile::Path(ledger_path.clone()),
LedgerFile::Path(ledger_path.clone()),
Some(keypair),
Some(leader_data.contact_info.ncp),
None,
);
nodes.push(val);
//contains the leader and new node
@ -196,8 +193,7 @@ fn test_multi_node_basic() {
let server = FullNode::new(
leader,
true,
InFile::Path(ledger_path.clone()),
None,
LedgerFile::Path(ledger_path.clone()),
None,
None,
);
@ -208,10 +204,9 @@ fn test_multi_node_basic() {
let val = FullNode::new(
validator,
false,
InFile::Path(ledger_path.clone()),
LedgerFile::Path(ledger_path.clone()),
Some(keypair),
Some(leader_data.contact_info.ncp),
None,
);
nodes.push(val);
}
@ -251,10 +246,9 @@ fn test_boot_validator_from_file() {
let leader_fullnode = FullNode::new(
leader,
true,
InFile::Path(ledger_path.clone()),
LedgerFile::Path(ledger_path.clone()),
None,
None,
Some(OutFile::Path(ledger_path.clone())),
);
let leader_balance =
send_tx_and_retry_get_balance(&leader_data, &alice, &bob_pubkey, Some(500)).unwrap();
@ -269,12 +263,10 @@ fn test_boot_validator_from_file() {
let val_fullnode = FullNode::new(
validator,
false,
InFile::Path(ledger_path.clone()),
LedgerFile::Path(ledger_path.clone()),
Some(keypair),
Some(leader_data.contact_info.ncp),
None,
);
let mut client = mk_client(&validator_data);
let getbal = retry_get_balance(&mut client, &bob_pubkey, Some(leader_balance));
assert!(getbal == Some(leader_balance));
@ -290,10 +282,9 @@ fn create_leader(ledger_path: &str) -> (NodeInfo, FullNode) {
let leader_fullnode = FullNode::new(
leader,
true,
InFile::Path(ledger_path.to_string()),
LedgerFile::Path(ledger_path.to_string()),
None,
None,
Some(OutFile::Path(ledger_path.to_string())),
);
(leader_data, leader_fullnode)
}
@ -342,10 +333,9 @@ fn test_leader_restart_validator_start_from_old_ledger() {
let val_fullnode = FullNode::new(
validator,
false,
InFile::Path(stale_ledger_path.clone()),
LedgerFile::Path(stale_ledger_path.clone()),
Some(keypair),
Some(leader_data.contact_info.ncp),
None,
);
// trigger broadcast, validator should catch up from leader, whose window contains
@ -386,10 +376,9 @@ fn test_multi_node_dynamic_network() {
let server = FullNode::new(
leader,
true,
InFile::Path(ledger_path.clone()),
LedgerFile::Path(ledger_path.clone()),
None,
None,
Some(OutFile::Path(ledger_path.clone())),
);
info!("{:x} LEADER", leader_data.debug_id());
let leader_balance =
@ -412,10 +401,9 @@ fn test_multi_node_dynamic_network() {
let val = FullNode::new(
validator,
false,
InFile::Path(ledger_path.clone()),
LedgerFile::Path(ledger_path.clone()),
Some(keypair),
Some(leader_data.contact_info.ncp),
Some(OutFile::Path(ledger_path.clone())),
);
info!("started[{}/{}] {:x}", n, N, rd.debug_id());
(rd, val)