Add storage mining pool (#4364)

* Add storage mining pool

* Set gossip port

* Add create-storage-mining-pool-account wallet command

* Add claim-storage-reward wallet command

* Create storage account upfront

* Add storage program to genesis

* Use STORAGE_ACCOUNT_SPACE

* Fix tests

* Add wallet commands to create validator/replicator storage accounts

* Add create_validator_storage_account()

* Storage stage no longer implicitly creates a storage account
This commit is contained in:
Michael Vines
2019-05-23 14:50:23 -07:00
committed by GitHub
parent 6b35e16676
commit b37d2fde3d
18 changed files with 735 additions and 177 deletions

10
Cargo.lock generated
View File

@ -80,6 +80,11 @@ dependencies = [
"predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "assert_matches"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.11" version = "0.2.11"
@ -2636,6 +2641,8 @@ dependencies = [
"solana-sdk 0.15.0", "solana-sdk 0.15.0",
"solana-stake-api 0.15.0", "solana-stake-api 0.15.0",
"solana-stake-program 0.15.0", "solana-stake-program 0.15.0",
"solana-storage-api 0.15.0",
"solana-storage-program 0.15.0",
"solana-vote-api 0.15.0", "solana-vote-api 0.15.0",
"solana-vote-program 0.15.0", "solana-vote-program 0.15.0",
] ]
@ -2692,6 +2699,7 @@ dependencies = [
name = "solana-storage-api" name = "solana-storage-api"
version = "0.15.0" version = "0.15.0"
dependencies = [ dependencies = [
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2804,6 +2812,7 @@ dependencies = [
"solana-netutil 0.15.0", "solana-netutil 0.15.0",
"solana-sdk 0.15.0", "solana-sdk 0.15.0",
"solana-stake-api 0.15.0", "solana-stake-api 0.15.0",
"solana-storage-api 0.15.0",
"solana-vote-api 0.15.0", "solana-vote-api 0.15.0",
"solana-vote-signer 0.15.0", "solana-vote-signer 0.15.0",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3424,6 +3433,7 @@ dependencies = [
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
"checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50" "checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50"
"checksum assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc477793bd82ec39799b6f6b3df64938532fdf2ab0d49ef817eac65856a5a1e" "checksum assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc477793bd82ec39799b6f6b3df64938532fdf2ab0d49ef817eac65856a5a1e"
"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb37ca32a3d9d88f18d08bac8d28368b8ee1f14f8b08eb62999c51720035b55" "checksum autocfg 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb37ca32a3d9d88f18d08bac8d28368b8ee1f14f8b08eb62999c51720035b55"
"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" "checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4"

View File

@ -142,12 +142,19 @@ pub fn kill_entry_and_spend_and_verify_rest(
assert!(cluster_nodes.len() >= nodes); assert!(cluster_nodes.len() >= nodes);
let client = create_client(entry_point_info.client_facing_addr(), FULLNODE_PORT_RANGE); let client = create_client(entry_point_info.client_facing_addr(), FULLNODE_PORT_RANGE);
let first_two_epoch_slots = MINIMUM_SLOT_LENGTH * 3; let first_two_epoch_slots = MINIMUM_SLOT_LENGTH * 3;
for ingress_node in &cluster_nodes {
client
.poll_get_balance(&ingress_node.id)
.unwrap_or_else(|err| panic!("Node {} has no balance: {}", ingress_node.id, err));
}
info!("sleeping for 2 leader fortnights"); info!("sleeping for 2 leader fortnights");
sleep(Duration::from_millis( sleep(Duration::from_millis(
slot_millis * first_two_epoch_slots as u64, slot_millis * first_two_epoch_slots as u64,
)); ));
info!("done sleeping for first 2 warmup epochs"); info!("done sleeping for first 2 warmup epochs");
info!("killing entry point"); info!("killing entry point: {}", entry_point_info.id);
assert!(client.fullnode_exit().unwrap()); assert!(client.fullnode_exit().unwrap());
info!("sleeping for some time"); info!("sleeping for some time");
sleep(Duration::from_millis( sleep(Duration::from_millis(
@ -160,10 +167,10 @@ pub fn kill_entry_and_spend_and_verify_rest(
} }
let client = create_client(ingress_node.client_facing_addr(), FULLNODE_PORT_RANGE); let client = create_client(ingress_node.client_facing_addr(), FULLNODE_PORT_RANGE);
let bal = client let balance = client
.poll_get_balance(&funding_keypair.pubkey()) .poll_get_balance(&funding_keypair.pubkey())
.expect("balance in source"); .expect("balance in source");
assert!(bal > 0); assert_ne!(balance, 0);
let mut result = Ok(()); let mut result = Ok(());
let mut retries = 0; let mut retries = 0;

View File

@ -387,6 +387,7 @@ mod tests {
#[test] #[test]
fn validator_exit() { fn validator_exit() {
solana_logger::setup();
let leader_keypair = Keypair::new(); let leader_keypair = Keypair::new();
let leader_node = Node::new_localhost_with_pubkey(&leader_keypair.pubkey()); let leader_node = Node::new_localhost_with_pubkey(&leader_keypair.pubkey());

View File

@ -11,6 +11,7 @@ use solana_client::thin_client::create_client;
use solana_client::thin_client::ThinClient; use solana_client::thin_client::ThinClient;
use solana_sdk::client::SyncClient; use solana_sdk::client::SyncClient;
use solana_sdk::genesis_block::GenesisBlock; use solana_sdk::genesis_block::GenesisBlock;
use solana_sdk::message::Message;
use solana_sdk::poh_config::PohConfig; use solana_sdk::poh_config::PohConfig;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::signature::{Keypair, KeypairUtil};
@ -19,6 +20,7 @@ use solana_sdk::timing::DEFAULT_SLOTS_PER_EPOCH;
use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT; use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT;
use solana_sdk::transaction::Transaction; use solana_sdk::transaction::Transaction;
use solana_stake_api::stake_instruction; use solana_stake_api::stake_instruction;
use solana_storage_api::storage_instruction;
use solana_vote_api::vote_instruction; use solana_vote_api::vote_instruction;
use solana_vote_api::vote_state::VoteState; use solana_vote_api::vote_state::VoteState;
use std::collections::HashMap; use std::collections::HashMap;
@ -120,6 +122,7 @@ impl LocalCluster {
mut genesis_block, mut genesis_block,
mint_keypair, mint_keypair,
voting_keypair, voting_keypair,
storage_keypair,
} = create_genesis_block_with_leader( } = create_genesis_block_with_leader(
config.cluster_lamports, config.cluster_lamports,
&leader_pubkey, &leader_pubkey,
@ -135,7 +138,7 @@ impl LocalCluster {
let (genesis_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block); let (genesis_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block);
let leader_ledger_path = tmp_copy_blocktree!(&genesis_ledger_path); let leader_ledger_path = tmp_copy_blocktree!(&genesis_ledger_path);
let leader_contact_info = leader_node.info.clone(); let leader_contact_info = leader_node.info.clone();
let leader_storage_keypair = Arc::new(Keypair::new()); let leader_storage_keypair = Arc::new(storage_keypair);
let leader_voting_keypair = Arc::new(voting_keypair); let leader_voting_keypair = Arc::new(voting_keypair);
let leader_server = Fullnode::new( let leader_server = Fullnode::new(
leader_node, leader_node,
@ -238,12 +241,12 @@ impl LocalCluster {
// setup as a listener // setup as a listener
info!("listener {} ", validator_pubkey,); info!("listener {} ", validator_pubkey,);
} else { } else {
// Send each validator some lamports to vote // Give the validator some lamports to setup vote and storage accounts
let validator_balance = Self::transfer_with_client( let validator_balance = Self::transfer_with_client(
&client, &client,
&self.funding_keypair, &self.funding_keypair,
&validator_pubkey, &validator_pubkey,
stake * 2 + 1, stake * 2 + 2,
); );
info!( info!(
"validator {} balance {}", "validator {} balance {}",
@ -257,6 +260,9 @@ impl LocalCluster {
stake, stake,
) )
.unwrap(); .unwrap();
Self::setup_storage_account(&client, &storage_keypair, &validator_keypair, false)
.unwrap();
} }
let voting_keypair = Arc::new(voting_keypair); let voting_keypair = Arc::new(voting_keypair);
@ -298,21 +304,24 @@ impl LocalCluster {
fn add_replicator(&mut self) { fn add_replicator(&mut self) {
let replicator_keypair = Arc::new(Keypair::new()); let replicator_keypair = Arc::new(Keypair::new());
let replicator_id = replicator_keypair.pubkey(); let replicator_pubkey = replicator_keypair.pubkey();
let storage_keypair = Arc::new(Keypair::new()); let storage_keypair = Arc::new(Keypair::new());
let storage_id = storage_keypair.pubkey(); let storage_pubkey = storage_keypair.pubkey();
let client = create_client( let client = create_client(
self.entry_point_info.client_facing_addr(), self.entry_point_info.client_facing_addr(),
FULLNODE_PORT_RANGE, FULLNODE_PORT_RANGE,
); );
// Give the replicator some lamports to setup its storage accounts
Self::transfer_with_client( Self::transfer_with_client(
&client, &client,
&self.funding_keypair, &self.funding_keypair,
&replicator_keypair.pubkey(), &replicator_keypair.pubkey(),
1, 42,
); );
let replicator_node = Node::new_localhost_replicator(&replicator_id); let replicator_node = Node::new_localhost_replicator(&replicator_pubkey);
Self::setup_storage_account(&client, &storage_keypair, &replicator_keypair, true).unwrap();
let (replicator_ledger_path, _blockhash) = create_new_tmp_ledger!(&self.genesis_block); let (replicator_ledger_path, _blockhash) = create_new_tmp_ledger!(&self.genesis_block);
let replicator = Replicator::new( let replicator = Replicator::new(
@ -322,12 +331,12 @@ impl LocalCluster {
replicator_keypair, replicator_keypair,
storage_keypair, storage_keypair,
) )
.unwrap(); .unwrap_or_else(|err| panic!("Replicator::new() failed: {:?}", err));
self.replicators.push(replicator); self.replicators.push(replicator);
self.replicator_infos.insert( self.replicator_infos.insert(
replicator_id, replicator_pubkey,
ReplicatorInfo::new(storage_id, replicator_ledger_path), ReplicatorInfo::new(storage_pubkey, replicator_ledger_path),
); );
} }
@ -464,6 +473,36 @@ impl LocalCluster {
"expected successful vote account registration", "expected successful vote account registration",
)) ))
} }
fn setup_storage_account(
client: &ThinClient,
storage_keypair: &Keypair,
from_keypair: &Arc<Keypair>,
replicator: bool,
) -> Result<()> {
let message = Message::new_with_payer(
if replicator {
storage_instruction::create_replicator_storage_account(
&from_keypair.pubkey(),
&storage_keypair.pubkey(),
1,
)
} else {
storage_instruction::create_validator_storage_account(
&from_keypair.pubkey(),
&storage_keypair.pubkey(),
1,
)
},
Some(&from_keypair.pubkey()),
);
let signer_keys = vec![from_keypair.as_ref()];
let blockhash = client.get_recent_blockhash().unwrap().0;
let mut transaction = Transaction::new(&signer_keys, message, blockhash);
client
.retry_transfer(&from_keypair, &mut transaction, 5)
.map(|_signature| ())
}
} }
impl Cluster for LocalCluster { impl Cluster for LocalCluster {
@ -529,7 +568,7 @@ mod test {
let num_replicators = 1; let num_replicators = 1;
let config = ClusterConfig { let config = ClusterConfig {
fullnode_config, fullnode_config,
num_replicators: 1, num_replicators,
node_stakes: vec![3; NUM_NODES], node_stakes: vec![3; NUM_NODES],
cluster_lamports: 100, cluster_lamports: 100,
ticks_per_slot: 8, ticks_per_slot: 8,

View File

@ -290,8 +290,8 @@ impl Replicator {
.expect("ledger encrypt not successful"); .expect("ledger encrypt not successful");
loop { loop {
self.create_sampling_offsets(); self.create_sampling_offsets();
if self.sample_file_to_create_mining_hash().is_err() { if let Err(err) = self.sample_file_to_create_mining_hash() {
info!("Error sampling file, exiting..."); warn!("Error sampling file, exiting: {:?}", err);
break; break;
} }
self.submit_mining_proof(); self.submit_mining_proof();
@ -365,7 +365,10 @@ impl Replicator {
self.num_chacha_blocks = num_encrypted_bytes / CHACHA_BLOCK_SIZE; self.num_chacha_blocks = num_encrypted_bytes / CHACHA_BLOCK_SIZE;
} }
info!("Done encrypting the ledger"); info!(
"Done encrypting the ledger: {:?}",
self.ledger_data_file_encrypted
);
Ok(()) Ok(())
} }
@ -406,29 +409,30 @@ impl Replicator {
if client.poll_get_balance(&keypair.pubkey())? == 0 { if client.poll_get_balance(&keypair.pubkey())? == 0 {
Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
"No account has been setup", "keypair account has no balance",
))? ))?
} }
// check if the account exists // check if the storage account exists
let bal = client.poll_get_balance(&storage_keypair.pubkey()); let balance = client.poll_get_balance(&storage_keypair.pubkey());
if bal.is_err() || bal.unwrap() == 0 { if balance.is_err() || balance.unwrap() == 0 {
let (blockhash, _fee_calculator) = client.get_recent_blockhash().expect("blockhash"); let (blockhash, _fee_calculator) = client.get_recent_blockhash().expect("blockhash");
let ix = vec![storage_instruction::create_account( let ix = storage_instruction::create_replicator_storage_account(
&keypair.pubkey(), &keypair.pubkey(),
&storage_keypair.pubkey(), &storage_keypair.pubkey(),
1, 1,
)]; );
let tx = Transaction::new_signed_instructions(&[keypair], ix, blockhash); let tx = Transaction::new_signed_instructions(&[keypair], ix, blockhash);
let signature = client.async_send_transaction(tx)?; let signature = client.async_send_transaction(tx)?;
client client
.poll_for_signature(&signature) .poll_for_signature(&signature)
.map_err(|err| match err { .map_err(|err| match err {
TransportError::IoError(e) => e, TransportError::IoError(e) => e,
TransportError::TransactionError(_) => { TransportError::TransactionError(_) => io::Error::new(
io::Error::new(ErrorKind::Other, "signature not found") ErrorKind::Other,
} "setup_mining_account: signature not found",
),
})?; })?;
} }
Ok(()) Ok(())
@ -504,10 +508,11 @@ impl Replicator {
.expect("rpc request") .expect("rpc request")
.as_u64() .as_u64()
.unwrap(); .unwrap();
info!("max slot: {}", storage_slot); info!("storage slot: {}", storage_slot);
if get_segment_from_slot(storage_slot) != 0 { if get_segment_from_slot(storage_slot) != 0 {
return Ok((storage_blockhash, storage_slot)); return Ok((storage_blockhash, storage_slot));
} }
info!("waiting for segment...");
sleep(Duration::from_secs(5)); sleep(Duration::from_secs(5));
} }
Err(Error::new( Err(Error::new(

View File

@ -192,6 +192,15 @@ impl StorageStage {
.name("solana-storage-create-accounts".to_string()) .name("solana-storage-create-accounts".to_string())
.spawn(move || { .spawn(move || {
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap(); let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
{
let working_bank = bank_forks.read().unwrap().working_bank();
let storage_account = working_bank.get_account(&storage_keypair.pubkey());
if storage_account.is_none() {
warn!("Storage account not found: {}", storage_keypair.pubkey());
}
}
loop { loop {
match instruction_receiver.recv_timeout(Duration::from_secs(1)) { match instruction_receiver.recv_timeout(Duration::from_secs(1)) {
Ok(instruction) => { Ok(instruction) => {
@ -238,22 +247,29 @@ impl StorageStage {
) -> io::Result<()> { ) -> io::Result<()> {
let working_bank = bank_forks.read().unwrap().working_bank(); let working_bank = bank_forks.read().unwrap().working_bank();
let blockhash = working_bank.confirmed_last_blockhash(); let blockhash = working_bank.confirmed_last_blockhash();
let mut instructions = vec![]; let keypair_balance = working_bank.get_balance(&keypair.pubkey());
let signer_keys = vec![keypair.as_ref(), storage_keypair.as_ref()];
if keypair_balance == 0 {
warn!("keypair account balance empty: {}", keypair.pubkey(),);
} else {
debug!(
"keypair account balance: {}: {}",
keypair.pubkey(),
keypair_balance
);
}
if working_bank if working_bank
.get_account(&storage_keypair.pubkey()) .get_account(&storage_keypair.pubkey())
.is_none() .is_none()
{ {
let create_instruction = storage_instruction::create_account( warn!(
&keypair.pubkey(), "storage account does not exist: {}",
&storage_keypair.pubkey(), storage_keypair.pubkey()
1,
); );
instructions.push(create_instruction);
info!("storage account requested");
} }
instructions.push(instruction);
let message = Message::new_with_payer(instructions, Some(&signer_keys[0].pubkey())); let signer_keys = vec![keypair.as_ref(), storage_keypair.as_ref()];
let message = Message::new_with_payer(vec![instruction], Some(&signer_keys[0].pubkey()));
let transaction = Transaction::new(&signer_keys, message, blockhash); let transaction = Transaction::new(&signer_keys, message, blockhash);
transactions_socket.send_to( transactions_socket.send_to(

View File

@ -25,7 +25,7 @@ use solana_sdk::signature::{read_keypair, KeypairUtil};
use solana_sdk::system_program; use solana_sdk::system_program;
use solana_sdk::timing; use solana_sdk::timing;
use solana_stake_api::stake_state; use solana_stake_api::stake_state;
use solana_storage_api::storage_contract::STORAGE_ACCOUNT_SPACE; use solana_storage_api::storage_contract;
use solana_vote_api::vote_state; use solana_vote_api::vote_state;
use std::error; use std::error;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -216,7 +216,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
// storage account // storage account
( (
bootstrap_storage_keypair.pubkey(), bootstrap_storage_keypair.pubkey(),
Account::new(1, STORAGE_ACCOUNT_SPACE as usize, &solana_storage_api::id()), storage_contract::create_validator_storage_account(1),
), ),
], ],
&[ &[

View File

@ -73,12 +73,13 @@ rsync_url() { # adds the 'rsync://` prefix to URLs that need it
echo "rsync://$url" echo "rsync://$url"
} }
setup_vote_and_stake_accounts() { setup_validator_accounts() {
declare entrypoint_ip=$1 declare entrypoint_ip=$1
declare node_keypair_path=$2 declare node_keypair_path=$2
declare vote_keypair_path=$3 declare vote_keypair_path=$3
declare stake_keypair_path=$4 declare stake_keypair_path=$4
declare stake=$5 declare storage_keypair_path=$5
declare stake=$6
declare node_pubkey declare node_pubkey
node_pubkey=$($solana_keygen pubkey "$node_keypair_path") node_pubkey=$($solana_keygen pubkey "$node_keypair_path")
@ -89,6 +90,9 @@ setup_vote_and_stake_accounts() {
declare stake_pubkey declare stake_pubkey
stake_pubkey=$($solana_keygen pubkey "$stake_keypair_path") stake_pubkey=$($solana_keygen pubkey "$stake_keypair_path")
declare storage_pubkey
storage_pubkey=$($solana_keygen pubkey "$storage_keypair_path")
if [[ -f "$node_keypair_path".configured ]]; then if [[ -f "$node_keypair_path".configured ]]; then
echo "Vote and stake accounts have already been configured" echo "Vote and stake accounts have already been configured"
else else
@ -108,16 +112,19 @@ setup_vote_and_stake_accounts() {
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \ $solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \
delegate-stake "$stake_keypair_path" "$vote_pubkey" || return $? delegate-stake "$stake_keypair_path" "$vote_pubkey" || return $?
# Setup validator storage account
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \
create-validator-storage-account "$storage_pubkey" || return $?
touch "$node_keypair_path".configured touch "$node_keypair_path".configured
fi fi
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \ $solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \
show-vote-account "$vote_pubkey" show-vote-account "$vote_pubkey"
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \ $solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \
show-stake-account "$stake_pubkey" show-stake-account "$stake_pubkey"
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \
show-storage-account "$storage_pubkey"
return 0 return 0
} }
@ -132,14 +139,28 @@ ledger_not_setup() {
setup_replicator_account() { setup_replicator_account() {
declare entrypoint_ip=$1 declare entrypoint_ip=$1
declare node_keypair_path=$2 declare node_keypair_path=$2
declare stake=$3 declare storage_keypair_path=$2
declare stake=$4
declare storage_pubkey
storage_pubkey=$($solana_keygen pubkey "$storage_keypair_path")
if [[ -f "$node_keypair_path".configured ]]; then if [[ -f "$node_keypair_path".configured ]]; then
echo "Replicator account has already been configured" echo "Replicator account has already been configured"
else else
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" airdrop "$stake" || return $? $solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" airdrop "$stake" || return $?
# Setup replicator storage account
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \
create-replicator-storage-account "$storage_keypair_path" || return $?
touch "$node_keypair_path".configured touch "$node_keypair_path".configured
fi fi
$solana_wallet --keypair "$node_keypair_path" --url "http://$entrypoint_ip:8899" \
show-storage-account "$storage_pubkey"
return 0
} }
args=() args=()
@ -228,7 +249,6 @@ if [[ $node_type = bootstrap_leader ]]; then
accounts_config_dir="$SOLANA_CONFIG_DIR"/bootstrap-leader-accounts accounts_config_dir="$SOLANA_CONFIG_DIR"/bootstrap-leader-accounts
fullnode_storage_keypair_path=$SOLANA_CONFIG_DIR/bootstrap-leader-storage-keypair.json fullnode_storage_keypair_path=$SOLANA_CONFIG_DIR/bootstrap-leader-storage-keypair.json
default_arg --rpc-port 8899 default_arg --rpc-port 8899
default_arg --rpc-drone-address 127.0.0.1:9900 default_arg --rpc-drone-address 127.0.0.1:9900
default_arg --gossip-port 8001 default_arg --gossip-port 8001
@ -300,11 +320,13 @@ else
fullnode_pubkey=$($solana_keygen pubkey "$fullnode_keypair_path") fullnode_pubkey=$($solana_keygen pubkey "$fullnode_keypair_path")
fullnode_vote_pubkey=$($solana_keygen pubkey "$fullnode_vote_keypair_path") fullnode_vote_pubkey=$($solana_keygen pubkey "$fullnode_vote_keypair_path")
fullnode_storage_pubkey=$($solana_keygen pubkey "$fullnode_storage_keypair_path")
cat <<EOF cat <<EOF
======================[ Fullnode configuration ]====================== ======================[ Fullnode configuration ]======================
node pubkey: $fullnode_pubkey node pubkey: $fullnode_pubkey
vote pubkey: $fullnode_vote_pubkey vote pubkey: $fullnode_vote_pubkey
storage pubkey: $fullnode_storage_pubkey
ledger: $ledger_config_dir ledger: $ledger_config_dir
accounts: $accounts_config_dir accounts: $accounts_config_dir
====================================================================== ======================================================================
@ -362,9 +384,17 @@ while true; do
trap '[[ -n $pid ]] && kill "$pid" >/dev/null 2>&1 && wait "$pid"' INT TERM ERR trap '[[ -n $pid ]] && kill "$pid" >/dev/null 2>&1 && wait "$pid"' INT TERM ERR
if [[ $node_type = validator ]] && ((stake)); then if [[ $node_type = validator ]] && ((stake)); then
setup_vote_and_stake_accounts "${entrypoint_address%:*}" "$fullnode_keypair_path" "$fullnode_vote_keypair_path" "$fullnode_stake_keypair_path" "$stake" setup_validator_accounts "${entrypoint_address%:*}" \
"$fullnode_keypair_path" \
"$fullnode_vote_keypair_path" \
"$fullnode_stake_keypair_path" \
"$fullnode_storage_keypair_path" \
"$stake"
elif [[ $node_type = replicator ]] && ((stake)); then elif [[ $node_type = replicator ]] && ((stake)); then
setup_replicator_account "${entrypoint_address%:*}" "$replicator_keypair_path" "$stake" setup_replicator_account "${entrypoint_address%:*}" \
"$replicator_keypair_path" \
"$replicator_storage_keypair_path" \
"$stake"
fi fi
echo "$PS4$program ${args[*]}" echo "$PS4$program ${args[*]}"

View File

@ -15,6 +15,7 @@ serde = "1.0.91"
serde_derive = "1.0.91" serde_derive = "1.0.91"
solana-logger = { path = "../../logger", version = "0.15.0" } solana-logger = { path = "../../logger", version = "0.15.0" }
solana-sdk = { path = "../../sdk", version = "0.15.0" } solana-sdk = { path = "../../sdk", version = "0.15.0" }
assert_matches = "1.3.0"
[dev-dependencies] [dev-dependencies]
solana-runtime = { path = "../../runtime", version = "0.15.0" } solana-runtime = { path = "../../runtime", version = "0.15.0" }

View File

@ -2,6 +2,7 @@ use crate::get_segment_from_slot;
use log::*; use log::*;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::account::Account; use solana_sdk::account::Account;
use solana_sdk::account::KeyedAccount;
use solana_sdk::account_utils::State; use solana_sdk::account_utils::State;
use solana_sdk::hash::Hash; use solana_sdk::hash::Hash;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
@ -42,8 +43,7 @@ pub struct CheckedProof {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum StorageContract { pub enum StorageContract {
//don't move this Uninitialized, // Must be first (aka, 0)
Default,
ValidatorStorage { ValidatorStorage {
slot: u64, slot: u64,
@ -58,6 +58,24 @@ pub enum StorageContract {
/// Multiple validators can validate the same set of proofs so it needs a Vec /// Multiple validators can validate the same set of proofs so it needs a Vec
reward_validations: HashMap<usize, HashMap<Hash, Vec<CheckedProof>>>, reward_validations: HashMap<usize, HashMap<Hash, Vec<CheckedProof>>>,
}, },
MiningPool,
}
// utility function, used by Bank, tests, genesis
pub fn create_validator_storage_account(lamports: u64) -> Account {
let mut storage_account = Account::new(lamports, STORAGE_ACCOUNT_SPACE as usize, &crate::id());
storage_account
.set_state(&StorageContract::ValidatorStorage {
slot: 0,
hash: Hash::default(),
lockout_validations: HashMap::new(),
reward_validations: HashMap::new(),
})
.expect("set_state");
storage_account
} }
pub struct StorageAccount<'a> { pub struct StorageAccount<'a> {
@ -69,6 +87,44 @@ impl<'a> StorageAccount<'a> {
Self { account } Self { account }
} }
pub fn initialize_mining_pool(&mut self) -> Result<(), InstructionError> {
let storage_contract = &mut self.account.state()?;
if let StorageContract::Uninitialized = storage_contract {
*storage_contract = StorageContract::MiningPool;
self.account.set_state(storage_contract)
} else {
Err(InstructionError::AccountAlreadyInitialized)?
}
}
pub fn initialize_replicator_storage(&mut self) -> Result<(), InstructionError> {
let storage_contract = &mut self.account.state()?;
if let StorageContract::Uninitialized = storage_contract {
*storage_contract = StorageContract::ReplicatorStorage {
proofs: HashMap::new(),
reward_validations: HashMap::new(),
};
self.account.set_state(storage_contract)
} else {
Err(InstructionError::AccountAlreadyInitialized)?
}
}
pub fn initialize_validator_storage(&mut self) -> Result<(), InstructionError> {
let storage_contract = &mut self.account.state()?;
if let StorageContract::Uninitialized = storage_contract {
*storage_contract = StorageContract::ValidatorStorage {
slot: 0,
hash: Hash::default(),
lockout_validations: HashMap::new(),
reward_validations: HashMap::new(),
};
self.account.set_state(storage_contract)
} else {
Err(InstructionError::AccountAlreadyInitialized)?
}
}
pub fn submit_mining_proof( pub fn submit_mining_proof(
&mut self, &mut self,
id: Pubkey, id: Pubkey,
@ -78,13 +134,6 @@ impl<'a> StorageAccount<'a> {
current_slot: u64, current_slot: u64,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut storage_contract = &mut self.account.state()?; let mut storage_contract = &mut self.account.state()?;
if let StorageContract::Default = storage_contract {
*storage_contract = StorageContract::ReplicatorStorage {
proofs: HashMap::new(),
reward_validations: HashMap::new(),
};
};
if let StorageContract::ReplicatorStorage { proofs, .. } = &mut storage_contract { if let StorageContract::ReplicatorStorage { proofs, .. } = &mut storage_contract {
let segment_index = get_segment_from_slot(slot); let segment_index = get_segment_from_slot(slot);
let current_segment = get_segment_from_slot(current_slot); let current_segment = get_segment_from_slot(current_slot);
@ -120,15 +169,6 @@ impl<'a> StorageAccount<'a> {
current_slot: u64, current_slot: u64,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut storage_contract = &mut self.account.state()?; let mut storage_contract = &mut self.account.state()?;
if let StorageContract::Default = storage_contract {
*storage_contract = StorageContract::ValidatorStorage {
slot: 0,
hash: Hash::default(),
lockout_validations: HashMap::new(),
reward_validations: HashMap::new(),
};
};
if let StorageContract::ValidatorStorage { if let StorageContract::ValidatorStorage {
slot: state_slot, slot: state_slot,
hash: state_hash, hash: state_hash,
@ -165,15 +205,6 @@ impl<'a> StorageAccount<'a> {
replicator_accounts: &mut [StorageAccount], replicator_accounts: &mut [StorageAccount],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut storage_contract = &mut self.account.state()?; let mut storage_contract = &mut self.account.state()?;
if let StorageContract::Default = storage_contract {
*storage_contract = StorageContract::ValidatorStorage {
slot: 0,
hash: Hash::default(),
lockout_validations: HashMap::new(),
reward_validations: HashMap::new(),
};
};
if let StorageContract::ValidatorStorage { if let StorageContract::ValidatorStorage {
slot: state_slot, slot: state_slot,
lockout_validations, lockout_validations,
@ -241,13 +272,11 @@ impl<'a> StorageAccount<'a> {
pub fn claim_storage_reward( pub fn claim_storage_reward(
&mut self, &mut self,
mining_pool: &mut KeyedAccount,
slot: u64, slot: u64,
current_slot: u64, current_slot: u64,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut storage_contract = &mut self.account.state()?; let mut storage_contract = &mut self.account.state()?;
if let StorageContract::Default = storage_contract {
Err(InstructionError::InvalidArgument)?
};
if let StorageContract::ValidatorStorage { if let StorageContract::ValidatorStorage {
reward_validations, reward_validations,
@ -266,14 +295,15 @@ impl<'a> StorageAccount<'a> {
); );
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let _num_validations = count_valid_proofs( let num_validations = count_valid_proofs(
&reward_validations &reward_validations
.remove(&claim_segment) .remove(&claim_segment)
.map(|mut proofs| proofs.drain().map(|(_, proof)| proof).collect::<Vec<_>>()) .map(|mut proofs| proofs.drain().map(|(_, proof)| proof).collect::<Vec<_>>())
.unwrap_or_default(), .unwrap_or_default(),
); );
// TODO can't just create lamports out of thin air let reward = TOTAL_VALIDATOR_REWARDS * num_validations;
// self.account.lamports += TOTAL_VALIDATOR_REWARDS * num_validations; mining_pool.account.lamports -= reward;
self.account.lamports += reward;
self.account.set_state(storage_contract) self.account.set_state(storage_contract)
} else if let StorageContract::ReplicatorStorage { } else if let StorageContract::ReplicatorStorage {
proofs, proofs,
@ -288,7 +318,7 @@ impl<'a> StorageAccount<'a> {
|| !reward_validations.contains_key(&claim_segment) || !reward_validations.contains_key(&claim_segment)
|| !proofs.contains_key(&claim_segment) || !proofs.contains_key(&claim_segment)
{ {
debug!( info!(
"current {:?}, claim {:?}, have rewards for {:?} segments", "current {:?}, claim {:?}, have rewards for {:?} segments",
claim_index, claim_index,
claim_segment, claim_segment,
@ -317,10 +347,15 @@ impl<'a> StorageAccount<'a> {
}) })
.unwrap_or_default(); .unwrap_or_default();
let _num_validations = count_valid_proofs(&checked_proofs); let _num_validations = count_valid_proofs(&checked_proofs);
// TODO can't just create lamports out of thin air
// self.account.lamports += num_validations // TODO enable when rewards are working
// * TOTAL_REPLICATOR_REWARDS /*
// * (num_validations / reward_validations[claim_segment].len() as u64); let reward = num_validations
* TOTAL_REPLICATOR_REWARDS
* (num_validations / reward_validations[&claim_segment].len() as u64);
mining_pool.account.lamports -= reward;
self.account.lamports += reward;
*/
self.account.set_state(storage_contract) self.account.set_state(storage_contract)
} else { } else {
Err(InstructionError::InvalidArgument)? Err(InstructionError::InvalidArgument)?
@ -399,7 +434,7 @@ mod tests {
fn test_account_data() { fn test_account_data() {
solana_logger::setup(); solana_logger::setup();
let mut account = Account::default(); let mut account = Account::default();
account.data.resize(4 * 1024, 0); account.data.resize(STORAGE_ACCOUNT_SPACE as usize, 0);
let storage_account = StorageAccount::new(&mut account); let storage_account = StorageAccount::new(&mut account);
// pretend it's a validator op code // pretend it's a validator op code
let mut contract = storage_account.account.state().unwrap(); let mut contract = storage_account.account.state().unwrap();
@ -454,9 +489,12 @@ mod tests {
// account has no space // account has no space
process_validation(&mut account, segment_index, &proof, &checked_proof).unwrap_err(); process_validation(&mut account, segment_index, &proof, &checked_proof).unwrap_err();
account.account.data.resize(4 * 1024, 0); account
.account
.data
.resize(STORAGE_ACCOUNT_SPACE as usize, 0);
let storage_contract = &mut account.account.state().unwrap(); let storage_contract = &mut account.account.state().unwrap();
if let StorageContract::Default = storage_contract { if let StorageContract::Uninitialized = storage_contract {
let mut proof_map = HashMap::new(); let mut proof_map = HashMap::new();
proof_map.insert(proof.sha_state, proof.clone()); proof_map.insert(proof.sha_state, proof.clone());
let mut proofs = HashMap::new(); let mut proofs = HashMap::new();

View File

@ -9,6 +9,14 @@ use solana_sdk::system_instruction;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StorageInstruction { pub enum StorageInstruction {
/// Initialize the account as a mining pool, validator or replicator
///
/// Expects 1 Account:
/// 0 - Account to be initialized
InitializeMiningPool,
InitializeValidatorStorage,
InitializeReplicatorStorage,
SubmitMiningProof { SubmitMiningProof {
sha_state: Hash, sha_state: Hash,
slot: u64, slot: u64,
@ -18,6 +26,11 @@ pub enum StorageInstruction {
hash: Hash, hash: Hash,
slot: u64, slot: u64,
}, },
/// Redeem storage reward credits
///
/// Expects 1 Account:
/// 0 - Storage account with credits to redeem
/// 1 - MiningPool account to redeem credits from
ClaimStorageReward { ClaimStorageReward {
slot: u64, slot: u64,
}, },
@ -27,12 +40,71 @@ pub enum StorageInstruction {
}, },
} }
pub fn create_account(from: &Pubkey, to: &Pubkey, lamports: u64) -> Instruction { pub fn create_validator_storage_account(
system_instruction::create_account(&from, to, lamports, STORAGE_ACCOUNT_SPACE, &id()) from_pubkey: &Pubkey,
storage_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account(
from_pubkey,
storage_pubkey,
lamports,
STORAGE_ACCOUNT_SPACE,
&id(),
),
Instruction::new(
id(),
&StorageInstruction::InitializeValidatorStorage,
vec![AccountMeta::new(*storage_pubkey, false)],
),
]
}
pub fn create_replicator_storage_account(
from_pubkey: &Pubkey,
storage_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account(
from_pubkey,
storage_pubkey,
lamports,
STORAGE_ACCOUNT_SPACE,
&id(),
),
Instruction::new(
id(),
&StorageInstruction::InitializeReplicatorStorage,
vec![AccountMeta::new(*storage_pubkey, false)],
),
]
}
pub fn create_mining_pool_account(
from_pubkey: &Pubkey,
storage_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account(
from_pubkey,
storage_pubkey,
lamports,
STORAGE_ACCOUNT_SPACE,
&id(),
),
Instruction::new(
id(),
&StorageInstruction::InitializeMiningPool,
vec![AccountMeta::new(*storage_pubkey, false)],
),
]
} }
pub fn mining_proof( pub fn mining_proof(
from_pubkey: &Pubkey, storage_pubkey: &Pubkey,
sha_state: Hash, sha_state: Hash,
slot: u64, slot: u64,
signature: Signature, signature: Signature,
@ -42,12 +114,12 @@ pub fn mining_proof(
slot, slot,
signature, signature,
}; };
let account_metas = vec![AccountMeta::new(*from_pubkey, true)]; let account_metas = vec![AccountMeta::new(*storage_pubkey, true)];
Instruction::new(id(), &storage_instruction, account_metas) Instruction::new(id(), &storage_instruction, account_metas)
} }
pub fn advertise_recent_blockhash( pub fn advertise_recent_blockhash(
from_pubkey: &Pubkey, storage_pubkey: &Pubkey,
storage_hash: Hash, storage_hash: Hash,
slot: u64, slot: u64,
) -> Instruction { ) -> Instruction {
@ -55,12 +127,16 @@ pub fn advertise_recent_blockhash(
hash: storage_hash, hash: storage_hash,
slot, slot,
}; };
let account_metas = vec![AccountMeta::new(*from_pubkey, true)]; let account_metas = vec![AccountMeta::new(*storage_pubkey, true)];
Instruction::new(id(), &storage_instruction, account_metas) Instruction::new(id(), &storage_instruction, account_metas)
} }
pub fn proof_validation(from_pubkey: &Pubkey, slot: u64, proofs: Vec<CheckedProof>) -> Instruction { pub fn proof_validation(
let mut account_metas = vec![AccountMeta::new(*from_pubkey, true)]; storage_pubkey: &Pubkey,
slot: u64,
proofs: Vec<CheckedProof>,
) -> Instruction {
let mut account_metas = vec![AccountMeta::new(*storage_pubkey, true)];
proofs.iter().for_each(|checked_proof| { proofs.iter().for_each(|checked_proof| {
account_metas.push(AccountMeta::new(checked_proof.proof.id, false)) account_metas.push(AccountMeta::new(checked_proof.proof.id, false))
}); });
@ -68,8 +144,15 @@ pub fn proof_validation(from_pubkey: &Pubkey, slot: u64, proofs: Vec<CheckedProo
Instruction::new(id(), &storage_instruction, account_metas) Instruction::new(id(), &storage_instruction, account_metas)
} }
pub fn reward_claim(from_pubkey: &Pubkey, slot: u64) -> Instruction { pub fn claim_reward(
storage_pubkey: &Pubkey,
mining_pool_pubkey: &Pubkey,
slot: u64,
) -> Instruction {
let storage_instruction = StorageInstruction::ClaimStorageReward { slot }; let storage_instruction = StorageInstruction::ClaimStorageReward { slot };
let account_metas = vec![AccountMeta::new(*from_pubkey, true)]; let account_metas = vec![
AccountMeta::new(*storage_pubkey, false),
AccountMeta::new(*mining_pool_pubkey, false),
];
Instruction::new(id(), &storage_instruction, account_metas) Instruction::new(id(), &storage_instruction, account_metas)
} }

View File

@ -1,10 +1,8 @@
//! storage program //! storage program
//! Receive mining proofs from miners, validate the answers //! Receive mining proofs from miners, validate the answers
//! and give reward for good proofs. //! and give reward for good proofs.
use crate::storage_contract::StorageAccount; use crate::storage_contract::StorageAccount;
use crate::storage_instruction::StorageInstruction; use crate::storage_instruction::StorageInstruction;
use log::*;
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
@ -18,30 +16,37 @@ pub fn process_instruction(
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
solana_logger::setup(); solana_logger::setup();
let num_keyed_accounts = keyed_accounts.len();
let (me, rest) = keyed_accounts.split_at_mut(1); let (me, rest) = keyed_accounts.split_at_mut(1);
let me_unsigned = me[0].signer_key().is_none();
// accounts_keys[0] must be signed let storage_account_pubkey = *me[0].unsigned_key();
let storage_account_pubkey = me[0].signer_key();
if storage_account_pubkey.is_none() {
info!("account[0] is unsigned");
Err(InstructionError::MissingRequiredSignature)?;
}
let storage_account_pubkey = *storage_account_pubkey.unwrap();
let mut storage_account = StorageAccount::new(&mut me[0].account); let mut storage_account = StorageAccount::new(&mut me[0].account);
let mut rest: Vec<_> = rest
.iter_mut()
.map(|keyed_account| StorageAccount::new(&mut keyed_account.account))
.collect();
match bincode::deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { match bincode::deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
StorageInstruction::InitializeMiningPool => {
if !rest.is_empty() {
Err(InstructionError::InvalidArgument)?;
}
storage_account.initialize_mining_pool()
}
StorageInstruction::InitializeReplicatorStorage => {
if !rest.is_empty() {
Err(InstructionError::InvalidArgument)?;
}
storage_account.initialize_replicator_storage()
}
StorageInstruction::InitializeValidatorStorage => {
if !rest.is_empty() {
Err(InstructionError::InvalidArgument)?;
}
storage_account.initialize_validator_storage()
}
StorageInstruction::SubmitMiningProof { StorageInstruction::SubmitMiningProof {
sha_state, sha_state,
slot, slot,
signature, signature,
} => { } => {
if num_keyed_accounts != 1 { if me_unsigned || !rest.is_empty() {
// This instruction must be signed by `me`
Err(InstructionError::InvalidArgument)?; Err(InstructionError::InvalidArgument)?;
} }
storage_account.submit_mining_proof( storage_account.submit_mining_proof(
@ -53,9 +58,8 @@ pub fn process_instruction(
) )
} }
StorageInstruction::AdvertiseStorageRecentBlockhash { hash, slot } => { StorageInstruction::AdvertiseStorageRecentBlockhash { hash, slot } => {
if num_keyed_accounts != 1 { if me_unsigned || !rest.is_empty() {
// keyed_accounts[0] should be the main storage key // This instruction must be signed by `me`
// to access its data
Err(InstructionError::InvalidArgument)?; Err(InstructionError::InvalidArgument)?;
} }
storage_account.advertise_storage_recent_blockhash( storage_account.advertise_storage_recent_blockhash(
@ -65,18 +69,24 @@ pub fn process_instruction(
) )
} }
StorageInstruction::ClaimStorageReward { slot } => { StorageInstruction::ClaimStorageReward { slot } => {
if num_keyed_accounts != 1 { if rest.len() != 1 {
// keyed_accounts[0] should be the main storage key
// to access its data
Err(InstructionError::InvalidArgument)?; Err(InstructionError::InvalidArgument)?;
} }
storage_account.claim_storage_reward(slot, tick_height / DEFAULT_TICKS_PER_SLOT) storage_account.claim_storage_reward(
&mut rest[0],
slot,
tick_height / DEFAULT_TICKS_PER_SLOT,
)
} }
StorageInstruction::ProofValidation { slot, proofs } => { StorageInstruction::ProofValidation { slot, proofs } => {
if num_keyed_accounts == 1 { if me_unsigned || rest.is_empty() {
// have to have at least 1 replicator to do any verification // This instruction must be signed by `me`
Err(InstructionError::InvalidArgument)?; Err(InstructionError::InvalidArgument)?;
} }
let mut rest: Vec<_> = rest
.iter_mut()
.map(|keyed_account| StorageAccount::new(&mut keyed_account.account))
.collect();
storage_account.proof_validation(slot, proofs, &mut rest) storage_account.proof_validation(slot, proofs, &mut rest)
} }
} }
@ -88,10 +98,13 @@ mod tests {
use crate::id; use crate::id;
use crate::storage_contract::{ use crate::storage_contract::{
CheckedProof, Proof, ProofStatus, StorageContract, STORAGE_ACCOUNT_SPACE, CheckedProof, Proof, ProofStatus, StorageContract, STORAGE_ACCOUNT_SPACE,
TOTAL_VALIDATOR_REWARDS,
}; };
use crate::storage_instruction; use crate::storage_instruction;
use crate::SLOTS_PER_SEGMENT; use crate::SLOTS_PER_SEGMENT;
use assert_matches::assert_matches;
use bincode::deserialize; use bincode::deserialize;
use log::*;
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::account::{create_keyed_accounts, Account}; use solana_sdk::account::{create_keyed_accounts, Account};
@ -99,6 +112,7 @@ mod tests {
use solana_sdk::genesis_block::create_genesis_block; use solana_sdk::genesis_block::create_genesis_block;
use solana_sdk::hash::{hash, Hash}; use solana_sdk::hash::{hash, Hash};
use solana_sdk::instruction::Instruction; use solana_sdk::instruction::Instruction;
use solana_sdk::message::Message;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
use std::sync::Arc; use std::sync::Arc;
@ -127,10 +141,14 @@ mod tests {
#[test] #[test]
fn test_proof_bounds() { fn test_proof_bounds() {
let pubkey = Pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let account = Account { let mut account = Account {
data: vec![0; STORAGE_ACCOUNT_SPACE as usize], data: vec![0; STORAGE_ACCOUNT_SPACE as usize],
..Account::default() ..Account::default()
}; };
{
let mut storage_account = StorageAccount::new(&mut account);
storage_account.initialize_replicator_storage().unwrap();
}
let ix = storage_instruction::mining_proof( let ix = storage_instruction::mining_proof(
&pubkey, &pubkey,
@ -212,13 +230,20 @@ mod tests {
let pubkey = Pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let mut accounts = [Account::default(), Account::default()]; let mut accounts = [Account::default(), Account::default()];
accounts[0].data.resize(STORAGE_ACCOUNT_SPACE as usize, 0); accounts[0].data.resize(STORAGE_ACCOUNT_SPACE as usize, 0);
{
let mut storage_account = StorageAccount::new(&mut accounts[0]);
storage_account.initialize_replicator_storage().unwrap();
}
let ix = let ix =
storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default()); storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default());
// move tick height into segment 1 // move tick height into segment 1
let ticks_till_next_segment = TICKS_IN_SEGMENT + 1; let ticks_till_next_segment = TICKS_IN_SEGMENT + 1;
test_instruction(&ix, &mut accounts, ticks_till_next_segment).unwrap(); assert_matches!(
test_instruction(&ix, &mut accounts, ticks_till_next_segment),
Ok(_)
);
} }
#[test] #[test]
@ -230,6 +255,8 @@ mod tests {
let replicator = replicator_keypair.pubkey(); let replicator = replicator_keypair.pubkey();
let validator_keypair = Keypair::new(); let validator_keypair = Keypair::new();
let validator = validator_keypair.pubkey(); let validator = validator_keypair.pubkey();
let mining_pool_keypair = Keypair::new();
let mining_pool = mining_pool_keypair.pubkey();
let mut bank = Bank::new(&genesis_block); let mut bank = Bank::new(&genesis_block);
bank.add_instruction_processor(id(), process_instruction); bank.add_instruction_processor(id(), process_instruction);
@ -237,11 +264,26 @@ mod tests {
let slot = 0; let slot = 0;
let bank_client = BankClient::new_shared(&bank); let bank_client = BankClient::new_shared(&bank);
let ix = storage_instruction::create_account(&mint_pubkey, &validator, 10); let message = Message::new(storage_instruction::create_validator_storage_account(
bank_client.send_instruction(&mint_keypair, ix).unwrap(); &mint_pubkey,
&validator,
10,
));
bank_client.send_message(&[&mint_keypair], message).unwrap();
let ix = storage_instruction::create_account(&mint_pubkey, &replicator, 10); let message = Message::new(storage_instruction::create_replicator_storage_account(
bank_client.send_instruction(&mint_keypair, ix).unwrap(); &mint_pubkey,
&replicator,
10,
));
bank_client.send_message(&[&mint_keypair], message).unwrap();
let message = Message::new(storage_instruction::create_mining_pool_account(
&mint_pubkey,
&mining_pool,
100,
));
bank_client.send_message(&[&mint_keypair], message).unwrap();
// tick the bank up until it's moved into storage segment 2 because the next advertise is for segment 1 // tick the bank up until it's moved into storage segment 2 because the next advertise is for segment 1
let next_storage_segment_tick_height = TICKS_IN_SEGMENT * 2; let next_storage_segment_tick_height = TICKS_IN_SEGMENT * 2;
@ -256,9 +298,7 @@ mod tests {
SLOTS_PER_SEGMENT, SLOTS_PER_SEGMENT,
); );
bank_client assert_matches!(bank_client.send_instruction(&validator_keypair, ix), Ok(_));
.send_instruction(&validator_keypair, ix)
.unwrap();
let ix = storage_instruction::mining_proof( let ix = storage_instruction::mining_proof(
&replicator, &replicator,
@ -266,9 +306,8 @@ mod tests {
slot, slot,
Signature::default(), Signature::default(),
); );
bank_client
.send_instruction(&replicator_keypair, ix) assert_matches!(bank_client.send_instruction(&replicator_keypair, ix), Ok(_));
.unwrap();
let ix = storage_instruction::advertise_recent_blockhash( let ix = storage_instruction::advertise_recent_blockhash(
&validator, &validator,
@ -281,10 +320,7 @@ mod tests {
bank.register_tick(&bank.last_blockhash()); bank.register_tick(&bank.last_blockhash());
} }
bank_client assert_matches!(bank_client.send_instruction(&validator_keypair, ix), Ok(_));
.send_instruction(&validator_keypair, ix)
.unwrap();
let ix = storage_instruction::proof_validation( let ix = storage_instruction::proof_validation(
&validator, &validator,
slot, slot,
@ -297,9 +333,8 @@ mod tests {
status: ProofStatus::Valid, status: ProofStatus::Valid,
}], }],
); );
bank_client
.send_instruction(&validator_keypair, ix) assert_matches!(bank_client.send_instruction(&validator_keypair, ix), Ok(_));
.unwrap();
let ix = storage_instruction::advertise_recent_blockhash( let ix = storage_instruction::advertise_recent_blockhash(
&validator, &validator,
@ -312,30 +347,50 @@ mod tests {
bank.register_tick(&bank.last_blockhash()); bank.register_tick(&bank.last_blockhash());
} }
bank_client assert_matches!(bank_client.send_instruction(&validator_keypair, ix), Ok(_));
.send_instruction(&validator_keypair, ix)
.unwrap();
let ix = storage_instruction::reward_claim(&validator, slot); assert_eq!(bank_client.get_balance(&validator).unwrap(), 10,);
bank_client
.send_instruction(&validator_keypair, ix)
.unwrap();
// TODO enable when rewards are working let message = Message::new_with_payer(
// assert_eq!(bank_client.get_balance(&validator).unwrap(), TOTAL_VALIDATOR_REWARDS); vec![storage_instruction::claim_reward(
&validator,
&mining_pool,
slot,
)],
Some(&validator),
);
assert_matches!(
bank_client.send_message(&[&validator_keypair], message),
Ok(_)
);
assert_eq!(
bank_client.get_balance(&validator).unwrap(),
10 + TOTAL_VALIDATOR_REWARDS
);
// tick the bank into the next storage epoch so that rewards can be claimed // tick the bank into the next storage epoch so that rewards can be claimed
for _ in 0..=TICKS_IN_SEGMENT { for _ in 0..=TICKS_IN_SEGMENT {
bank.register_tick(&bank.last_blockhash()); bank.register_tick(&bank.last_blockhash());
} }
let ix = storage_instruction::reward_claim(&replicator, slot); assert_eq!(bank_client.get_balance(&replicator).unwrap(), 10);
bank_client
.send_instruction(&replicator_keypair, ix) let message = Message::new_with_payer(
.unwrap(); vec![storage_instruction::claim_reward(
&replicator,
&mining_pool,
slot,
)],
Some(&replicator),
);
assert_matches!(
bank_client.send_message(&[&replicator_keypair], message),
Ok(_)
);
// TODO enable when rewards are working // TODO enable when rewards are working
// assert_eq!(bank_client.get_balance(&replicator).unwrap(), TOTAL_REPLICATOR_REWARDS); // assert_eq!(bank_client.get_balance(&replicator).unwrap(), 10 + TOTAL_REPLICATOR_REWARDS);
} }
fn get_storage_slot<C: SyncClient>(client: &C, account: &Pubkey) -> u64 { fn get_storage_slot<C: SyncClient>(client: &C, account: &Pubkey) -> u64 {
@ -399,13 +454,19 @@ mod tests {
.transfer(10, &mint_keypair, &replicator_pubkey) .transfer(10, &mint_keypair, &replicator_pubkey)
.unwrap(); .unwrap();
let ix = storage_instruction::create_account(&mint_pubkey, &replicator_pubkey, 1); let message = Message::new(storage_instruction::create_replicator_storage_account(
&mint_pubkey,
&replicator_pubkey,
1,
));
bank_client.send_message(&[&mint_keypair], message).unwrap();
bank_client.send_instruction(&mint_keypair, ix).unwrap(); let message = Message::new(storage_instruction::create_validator_storage_account(
&mint_pubkey,
let ix = storage_instruction::create_account(&mint_pubkey, &validator_pubkey, 1); &validator_pubkey,
1,
bank_client.send_instruction(&mint_keypair, ix).unwrap(); ));
bank_client.send_message(&[&mint_keypair], message).unwrap();
let ix = storage_instruction::advertise_recent_blockhash( let ix = storage_instruction::advertise_recent_blockhash(
&validator_pubkey, &validator_pubkey,

1
run.sh
View File

@ -73,6 +73,7 @@ args=(
--voting-keypair "$dataDir"/config/leader-vote-account-keypair.json --voting-keypair "$dataDir"/config/leader-vote-account-keypair.json
--vote-account "$leaderVoteAccountPubkey" --vote-account "$leaderVoteAccountPubkey"
--ledger "$dataDir"/ledger/ --ledger "$dataDir"/ledger/
--gossip-port 8001
--rpc-port 8899 --rpc-port 8899
--rpc-drone-address 127.0.0.1:9900 --rpc-drone-address 127.0.0.1:9900
) )

View File

@ -27,9 +27,11 @@ solana-logger = { path = "../logger", version = "0.15.0" }
solana-metrics = { path = "../metrics", version = "0.15.0" } solana-metrics = { path = "../metrics", version = "0.15.0" }
solana-sdk = { path = "../sdk", version = "0.15.0" } solana-sdk = { path = "../sdk", version = "0.15.0" }
solana-stake-api = { path = "../programs/stake_api", version = "0.15.0" } solana-stake-api = { path = "../programs/stake_api", version = "0.15.0" }
solana-storage-api = { path = "../programs/storage_api", version = "0.15.0" }
solana-vote-api = { path = "../programs/vote_api", version = "0.15.0" } solana-vote-api = { path = "../programs/vote_api", version = "0.15.0" }
solana-vote-program = { path = "../programs/vote_program", version = "0.15.0" } solana-vote-program = { path = "../programs/vote_program", version = "0.15.0" }
solana-stake-program = { path = "../programs/stake_program", version = "0.15.0" } solana-stake-program = { path = "../programs/stake_program", version = "0.15.0" }
solana-storage-program = { path = "../programs/storage_program", version = "0.15.0" }
solana-noop-program = { path = "../programs/noop_program", version = "0.15.0" } solana-noop-program = { path = "../programs/noop_program", version = "0.15.0" }
[lib] [lib]

View File

@ -4,6 +4,7 @@ use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::system_program; use solana_sdk::system_program;
use solana_stake_api::stake_state; use solana_stake_api::stake_state;
use solana_storage_api::storage_contract;
use solana_vote_api::vote_state; use solana_vote_api::vote_state;
// The default stake placed with the bootstrap leader // The default stake placed with the bootstrap leader
@ -13,6 +14,7 @@ pub struct GenesisBlockInfo {
pub genesis_block: GenesisBlock, pub genesis_block: GenesisBlock,
pub mint_keypair: Keypair, pub mint_keypair: Keypair,
pub voting_keypair: Keypair, pub voting_keypair: Keypair,
pub storage_keypair: Keypair,
} }
pub fn create_genesis_block_with_leader( pub fn create_genesis_block_with_leader(
@ -23,6 +25,7 @@ pub fn create_genesis_block_with_leader(
let mint_keypair = Keypair::new(); let mint_keypair = Keypair::new();
let voting_keypair = Keypair::new(); let voting_keypair = Keypair::new();
let staking_keypair = Keypair::new(); let staking_keypair = Keypair::new();
let storage_keypair = Keypair::new();
// TODO: de-duplicate the stake once passive staking // TODO: de-duplicate the stake once passive staking
// is fully implemented // is fully implemented
@ -41,11 +44,11 @@ pub fn create_genesis_block_with_leader(
mint_keypair.pubkey(), mint_keypair.pubkey(),
Account::new(mint_lamports, 0, &system_program::id()), Account::new(mint_lamports, 0, &system_program::id()),
), ),
// node needs an account to issue votes from, this will require // node needs an account to issue votes and storage proofs from, this will require
// airdrops at some point to cover fees... // airdrops at some point to cover fees...
( (
*bootstrap_leader_id, *bootstrap_leader_id,
Account::new(1, 0, &system_program::id()), Account::new(42, 0, &system_program::id()),
), ),
// where votes go to // where votes go to
(voting_keypair.pubkey(), vote_account), (voting_keypair.pubkey(), vote_account),
@ -58,13 +61,23 @@ pub fn create_genesis_block_with_leader(
bootstrap_leader_stake_lamports, bootstrap_leader_stake_lamports,
), ),
), ),
// storage account
(
storage_keypair.pubkey(),
storage_contract::create_validator_storage_account(1),
),
],
&[
solana_vote_program!(),
solana_stake_program!(),
solana_storage_program!(), // TODO: storage program is only needed by core/, move this line into core/src/genesis_utils.rs
], ],
&[solana_vote_program!(), solana_stake_program!()],
); );
GenesisBlockInfo { GenesisBlockInfo {
genesis_block, genesis_block,
mint_keypair, mint_keypair,
voting_keypair, voting_keypair,
storage_keypair,
} }
} }

View File

@ -25,5 +25,8 @@ extern crate solana_vote_program;
#[macro_use] #[macro_use]
extern crate solana_stake_program; extern crate solana_stake_program;
#[macro_use]
extern crate solana_storage_program;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;

View File

@ -24,6 +24,7 @@ solana-logger = { path = "../logger", version = "0.15.0" }
solana-netutil = { path = "../netutil", version = "0.15.0" } solana-netutil = { path = "../netutil", version = "0.15.0" }
solana-sdk = { path = "../sdk", version = "0.15.0" } solana-sdk = { path = "../sdk", version = "0.15.0" }
solana-stake-api = { path = "../programs/stake_api", version = "0.15.0" } solana-stake-api = { path = "../programs/stake_api", version = "0.15.0" }
solana-storage-api = { path = "../programs/storage_api", version = "0.15.0" }
solana-vote-api = { path = "../programs/vote_api", version = "0.15.0" } solana-vote-api = { path = "../programs/vote_api", version = "0.15.0" }
solana-vote-signer = { path = "../vote-signer", version = "0.15.0" } solana-vote-signer = { path = "../vote-signer", version = "0.15.0" }
url = "1.7.2" url = "1.7.2"

View File

@ -27,6 +27,7 @@ use solana_sdk::system_instruction::SystemError;
use solana_sdk::system_transaction; use solana_sdk::system_transaction;
use solana_sdk::transaction::{Transaction, TransactionError}; use solana_sdk::transaction::{Transaction, TransactionError};
use solana_stake_api::stake_instruction; use solana_stake_api::stake_instruction;
use solana_storage_api::storage_instruction;
use solana_vote_api::vote_instruction; use solana_vote_api::vote_instruction;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
@ -53,6 +54,11 @@ pub enum WalletCommand {
DelegateStake(Keypair, Pubkey), DelegateStake(Keypair, Pubkey),
RedeemVoteCredits(Pubkey, Pubkey, Pubkey), RedeemVoteCredits(Pubkey, Pubkey, Pubkey),
ShowStakeAccount(Pubkey), ShowStakeAccount(Pubkey),
CreateStorageMiningPoolAccount(Pubkey, u64),
CreateReplicatorStorageAccount(Pubkey),
CreateValidatorStorageAccount(Pubkey),
ClaimStorageReward(Pubkey, Pubkey, u64),
ShowStorageAccount(Pubkey),
Deploy(String), Deploy(String),
GetTransactionCount, GetTransactionCount,
// Pay(lamports, to, timestamp, timestamp_pubkey, witness(es), cancelable) // Pay(lamports, to, timestamp, timestamp_pubkey, witness(es), cancelable)
@ -250,6 +256,42 @@ pub fn parse_command(
let staking_account_id = pubkey_of(matches, "staking_account_id").unwrap(); let staking_account_id = pubkey_of(matches, "staking_account_id").unwrap();
Ok(WalletCommand::ShowStakeAccount(staking_account_id)) Ok(WalletCommand::ShowStakeAccount(staking_account_id))
} }
("create-storage-mining-pool-account", Some(matches)) => {
let storage_mining_pool_account_id =
pubkey_of(matches, "storage_mining_pool_account_id").unwrap();
let lamports = matches.value_of("lamports").unwrap().parse()?;
Ok(WalletCommand::CreateStorageMiningPoolAccount(
storage_mining_pool_account_id,
lamports,
))
}
("create-replicator-storage-account", Some(matches)) => {
let storage_account_id = pubkey_of(matches, "storage_account_id").unwrap();
Ok(WalletCommand::CreateReplicatorStorageAccount(
storage_account_id,
))
}
("create-validator-storage-account", Some(matches)) => {
let storage_account_id = pubkey_of(matches, "storage_account_id").unwrap();
Ok(WalletCommand::CreateValidatorStorageAccount(
storage_account_id,
))
}
("claim-storage-reward", Some(matches)) => {
let storage_mining_pool_account_id =
pubkey_of(matches, "storage_mining_pool_account_id").unwrap();
let storage_account_id = pubkey_of(matches, "storage_account_id").unwrap();
let slot = matches.value_of("slot").unwrap().parse()?;
Ok(WalletCommand::ClaimStorageReward(
storage_mining_pool_account_id,
storage_account_id,
slot,
))
}
("show-storage-account", Some(matches)) => {
let storage_account_id = pubkey_of(matches, "storage_account_id").unwrap();
Ok(WalletCommand::ShowStorageAccount(storage_account_id))
}
("deploy", Some(deploy_matches)) => Ok(WalletCommand::Deploy( ("deploy", Some(deploy_matches)) => Ok(WalletCommand::Deploy(
deploy_matches deploy_matches
.value_of("program_location") .value_of("program_location")
@ -585,6 +627,91 @@ fn process_show_stake_account(
} }
} }
fn process_create_storage_mining_pool_account(
rpc_client: &RpcClient,
config: &WalletConfig,
storage_account_id: &Pubkey,
lamports: u64,
) -> ProcessResult {
let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = storage_instruction::create_mining_pool_account(
&config.keypair.pubkey(),
storage_account_id,
lamports,
);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair])?;
Ok(signature_str.to_string())
}
fn process_create_replicator_storage_account(
rpc_client: &RpcClient,
config: &WalletConfig,
storage_account_id: &Pubkey,
) -> ProcessResult {
let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = storage_instruction::create_replicator_storage_account(
&config.keypair.pubkey(),
storage_account_id,
1,
);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair])?;
Ok(signature_str.to_string())
}
fn process_create_validator_storage_account(
rpc_client: &RpcClient,
config: &WalletConfig,
storage_account_id: &Pubkey,
) -> ProcessResult {
let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = storage_instruction::create_validator_storage_account(
&config.keypair.pubkey(),
storage_account_id,
1,
);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair])?;
Ok(signature_str.to_string())
}
fn process_claim_storage_reward(
rpc_client: &RpcClient,
config: &WalletConfig,
storage_mining_pool_account_id: &Pubkey,
storage_account_id: &Pubkey,
slot: u64,
) -> ProcessResult {
let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
let instruction =
storage_instruction::claim_reward(storage_account_id, storage_mining_pool_account_id, slot);
let signers = [&config.keypair];
let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey()));
let mut transaction = Transaction::new(&signers, message, recent_blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut transaction, &signers)?;
Ok(signature_str.to_string())
}
fn process_show_storage_account(
rpc_client: &RpcClient,
_config: &WalletConfig,
storage_account_id: &Pubkey,
) -> ProcessResult {
use solana_storage_api::storage_contract::StorageContract;
let account = rpc_client.get_account(storage_account_id)?;
let storage_contract: StorageContract = account.state().map_err(|err| {
WalletError::RpcRequestError(
format!("Unable to deserialize storage account: {:?}", err).to_string(),
)
})?;
println!("{:?}", storage_contract);
println!("account lamports: {}", account.lamports);
Ok("".to_string())
}
fn process_deploy( fn process_deploy(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &WalletConfig, config: &WalletConfig,
@ -904,11 +1031,43 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
&voting_account_id, &voting_account_id,
), ),
// Show a vote account
WalletCommand::ShowStakeAccount(staking_account_id) => { WalletCommand::ShowStakeAccount(staking_account_id) => {
process_show_stake_account(&rpc_client, config, &staking_account_id) process_show_stake_account(&rpc_client, config, &staking_account_id)
} }
WalletCommand::CreateStorageMiningPoolAccount(storage_account_id, lamports) => {
process_create_storage_mining_pool_account(
&rpc_client,
config,
&storage_account_id,
*lamports,
)
}
WalletCommand::CreateReplicatorStorageAccount(storage_account_id) => {
process_create_replicator_storage_account(&rpc_client, config, &storage_account_id)
}
WalletCommand::CreateValidatorStorageAccount(storage_account_id) => {
process_create_validator_storage_account(&rpc_client, config, &storage_account_id)
}
WalletCommand::ClaimStorageReward(
storage_mining_pool_account_id,
storage_account_id,
slot,
) => process_claim_storage_reward(
&rpc_client,
config,
&storage_mining_pool_account_id,
&storage_account_id,
*slot,
),
WalletCommand::ShowStorageAccount(storage_account_id) => {
process_show_storage_account(&rpc_client, config, &storage_account_id)
}
// Deploy a custom program to the chain // Deploy a custom program to the chain
WalletCommand::Deploy(ref program_location) => { WalletCommand::Deploy(ref program_location) => {
process_deploy(&rpc_client, config, program_location) process_deploy(&rpc_client, config, program_location)
@ -1182,7 +1341,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
) )
.subcommand( .subcommand(
SubCommand::with_name("create-mining-pool-account") SubCommand::with_name("create-mining-pool-account")
.about("Create mining pool account") .about("Create staking mining pool account")
.arg( .arg(
Arg::with_name("mining_pool_account_id") Arg::with_name("mining_pool_account_id")
.index(1) .index(1)
@ -1190,7 +1349,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.takes_value(true) .takes_value(true)
.required(true) .required(true)
.validator(is_pubkey) .validator(is_pubkey)
.help("Staking account address to fund"), .help("Staking mining pool account address to fund"),
) )
.arg( .arg(
Arg::with_name("lamports") Arg::with_name("lamports")
@ -1249,7 +1408,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.arg( .arg(
Arg::with_name("mining_pool_account_id") Arg::with_name("mining_pool_account_id")
.index(1) .index(1)
.value_name("PUBKEY") .value_name("MINING POOL PUBKEY")
.takes_value(true) .takes_value(true)
.required(true) .required(true)
.validator(is_pubkey) .validator(is_pubkey)
@ -1258,7 +1417,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.arg( .arg(
Arg::with_name("staking_account_id") Arg::with_name("staking_account_id")
.index(2) .index(2)
.value_name("PUBKEY") .value_name("STAKING ACCOUNT PUBKEY")
.takes_value(true) .takes_value(true)
.required(true) .required(true)
.validator(is_pubkey) .validator(is_pubkey)
@ -1287,6 +1446,94 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.help("Stake account pubkey"), .help("Stake account pubkey"),
) )
) )
.subcommand(
SubCommand::with_name("create-storage-mining-pool-account")
.about("Create mining pool account")
.arg(
Arg::with_name("storage_account_id")
.index(1)
.value_name("PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey)
.help("Storage mining pool account address to fund"),
)
.arg(
Arg::with_name("lamports")
.index(2)
.value_name("NUM")
.takes_value(true)
.required(true)
.help("The number of lamports to assign to the storage mining pool account"),
),
)
.subcommand(
SubCommand::with_name("create-replicator-storage-account")
.about("Create a replicator storage account")
.arg(
Arg::with_name("storage_account_id")
.index(1)
.value_name("PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey)
)
)
.subcommand(
SubCommand::with_name("create-validator-storage-account")
.about("Create a validator storage account")
.arg(
Arg::with_name("storage_account_id")
.index(1)
.value_name("PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey)
)
)
.subcommand(
SubCommand::with_name("claim-storage-reward")
.about("Redeem storage reward credits")
.arg(
Arg::with_name("storage_mining_pool_account_id")
.index(1)
.value_name("MINING POOL PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey)
.help("Mining pool account to redeem credits from"),
)
.arg(
Arg::with_name("storage_account_id")
.index(2)
.value_name("STORAGE ACCOUNT PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey)
.help("Storage account address to redeem credits for"),
)
.arg(
Arg::with_name("slot")
.index(3)
.value_name("SLOT")
.takes_value(true)
.required(true)
.help("The slot to claim rewards for"),
),)
.subcommand(
SubCommand::with_name("show-storage-account")
.about("Show the contents of a storage account")
.arg(
Arg::with_name("storage_account_id")
.index(1)
.value_name("PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey)
.help("Storage account pubkey"),
)
)
.subcommand( .subcommand(
SubCommand::with_name("deploy") SubCommand::with_name("deploy")
.about("Deploy a program") .about("Deploy a program")