Add storage mining pool to genesis and implement automatic reward redeeming (#4683)

* Add storage mining pool to genesis and implement automatic reward collection

* Address review comments
This commit is contained in:
Sagar Dhawan 2019-06-13 22:30:51 -07:00 committed by GitHub
parent ee68b9800e
commit 119467df59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 134 additions and 20 deletions

View File

@ -6,6 +6,7 @@
use crate::rpc_client::RpcClient; use crate::rpc_client::RpcClient;
use bincode::{serialize_into, serialized_size}; use bincode::{serialize_into, serialized_size};
use log::*; use log::*;
use solana_sdk::account::Account;
use solana_sdk::client::{AsyncClient, Client, SyncClient}; use solana_sdk::client::{AsyncClient, Client, SyncClient};
use solana_sdk::fee_calculator::FeeCalculator; use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::hash::Hash; use solana_sdk::hash::Hash;
@ -306,11 +307,31 @@ impl SyncClient for ThinClient {
Ok(self.rpc_client().get_account_data(pubkey).ok()) Ok(self.rpc_client().get_account_data(pubkey).ok())
} }
fn get_account(&self, pubkey: &Pubkey) -> TransportResult<Option<Account>> {
Ok(self.rpc_client().get_account(pubkey).ok())
}
fn get_balance(&self, pubkey: &Pubkey) -> TransportResult<u64> { fn get_balance(&self, pubkey: &Pubkey) -> TransportResult<u64> {
let balance = self.rpc_client().get_balance(pubkey)?; let balance = self.rpc_client().get_balance(pubkey)?;
Ok(balance) Ok(balance)
} }
fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> {
let index = self.optimizer.experiment();
let now = Instant::now();
let recent_blockhash = self.rpc_clients[index].get_recent_blockhash();
match recent_blockhash {
Ok(recent_blockhash) => {
self.optimizer.report(index, duration_as_ms(&now.elapsed()));
Ok(recent_blockhash)
}
Err(e) => {
self.optimizer.report(index, std::u64::MAX);
Err(e)?
}
}
}
fn get_signature_status( fn get_signature_status(
&self, &self,
signature: &Signature, signature: &Signature,
@ -337,22 +358,6 @@ impl SyncClient for ThinClient {
Ok(slot) Ok(slot)
} }
fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> {
let index = self.optimizer.experiment();
let now = Instant::now();
let recent_blockhash = self.rpc_clients[index].get_recent_blockhash();
match recent_blockhash {
Ok(recent_blockhash) => {
self.optimizer.report(index, duration_as_ms(&now.elapsed()));
Ok(recent_blockhash)
}
Err(e) => {
self.optimizer.report(index, std::u64::MAX);
Err(e)?
}
}
}
fn get_transaction_count(&self) -> TransportResult<u64> { fn get_transaction_count(&self) -> TransportResult<u64> {
let index = self.optimizer.experiment(); let index = self.optimizer.experiment();
let now = Instant::now(); let now = Instant::now();

View File

@ -17,13 +17,16 @@ use solana_client::rpc_client::RpcClient;
use solana_client::rpc_request::RpcRequest; use solana_client::rpc_request::RpcRequest;
use solana_client::thin_client::ThinClient; use solana_client::thin_client::ThinClient;
use solana_ed25519_dalek as ed25519_dalek; use solana_ed25519_dalek as ed25519_dalek;
use solana_sdk::account_utils::State;
use solana_sdk::client::{AsyncClient, SyncClient}; use solana_sdk::client::{AsyncClient, SyncClient};
use solana_sdk::hash::{Hash, Hasher}; use solana_sdk::hash::{Hash, Hasher};
use solana_sdk::message::Message; use solana_sdk::message::Message;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
use solana_sdk::timing::timestamp; use solana_sdk::timing::timestamp;
use solana_sdk::transaction::Transaction; use solana_sdk::transaction::Transaction;
use solana_sdk::transport::TransportError; use solana_sdk::transport::TransportError;
use solana_storage_api::storage_contract::StorageContract;
use solana_storage_api::{get_segment_from_slot, storage_instruction, SLOTS_PER_SEGMENT}; use solana_storage_api::{get_segment_from_slot, storage_instruction, SLOTS_PER_SEGMENT};
use std::fs::File; use std::fs::File;
use std::io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom}; use std::io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom};
@ -298,7 +301,7 @@ impl Replicator {
}) })
} }
pub fn run(&mut self) { pub fn run(&mut self, mining_pool_pubkey: Option<Pubkey>) {
info!("waiting for ledger download"); info!("waiting for ledger download");
self.thread_handles.pop().unwrap().join().unwrap(); self.thread_handles.pop().unwrap().join().unwrap();
self.encrypt_ledger() self.encrypt_ledger()
@ -325,6 +328,40 @@ impl Replicator {
} }
}; };
self.blockhash = storage_blockhash; self.blockhash = storage_blockhash;
if let Some(mining_pool_pubkey) = mining_pool_pubkey {
self.redeem_rewards(&mining_pool_pubkey);
}
}
}
fn redeem_rewards(&self, mining_pool_pubkey: &Pubkey) {
let nodes = self.cluster_info.read().unwrap().tvu_peers();
let client = crate::gossip_service::get_client(&nodes);
if let Ok(Some(account)) = client.get_account(&self.storage_keypair.pubkey()) {
if let Ok(StorageContract::ReplicatorStorage {
reward_validations, ..
}) = account.state()
{
if !reward_validations.is_empty() {
let ix = storage_instruction::claim_reward(
&self.keypair.pubkey(),
&self.storage_keypair.pubkey(),
mining_pool_pubkey,
);
let message = Message::new_with_payer(vec![ix], Some(&self.keypair.pubkey()));
if let Err(e) = client.send_message(&[&self.keypair], message) {
error!("unable to redeem reward, tx failed: {:?}", e);
} else {
info!(
"collected mining rewards: Account balance {:?}",
client.get_balance(&self.keypair.pubkey())
);
}
}
}
} else {
info!("Redeem mining reward: No account data found");
} }
} }

View File

@ -151,6 +151,22 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.required(true) .required(true)
.help("Path to file containing the bootstrap leader's storage keypair"), .help("Path to file containing the bootstrap leader's storage keypair"),
) )
.arg(
Arg::with_name("storage_mining_pool_keypair_file")
.long("storage-mining-pool-keypair")
.value_name("KEYPAIR")
.takes_value(true)
.required(true)
.help("Path to file containing the storage mining pool storage keypair"),
)
.arg(
Arg::with_name("storage_mining_pool_lamports")
.long("storage-mining-pool-lamports")
.value_name("LAMPORTS")
.takes_value(true)
.required(true)
.help("Number of lamports to assign to the storage mining pool"),
)
.arg( .arg(
Arg::with_name("bootstrap_leader_lamports") Arg::with_name("bootstrap_leader_lamports")
.long("bootstrap-leader-lamports") .long("bootstrap-leader-lamports")
@ -251,17 +267,22 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let bootstrap_stake_keypair_file = matches.value_of("bootstrap_stake_keypair_file").unwrap(); let bootstrap_stake_keypair_file = matches.value_of("bootstrap_stake_keypair_file").unwrap();
let bootstrap_storage_keypair_file = let bootstrap_storage_keypair_file =
matches.value_of("bootstrap_storage_keypair_file").unwrap(); matches.value_of("bootstrap_storage_keypair_file").unwrap();
let storage_mining_pool_keypair = matches
.value_of("storage_mining_pool_keypair_file")
.unwrap();
let mint_keypair_file = matches.value_of("mint_keypair_file").unwrap(); let mint_keypair_file = matches.value_of("mint_keypair_file").unwrap();
let ledger_path = matches.value_of("ledger_path").unwrap(); let ledger_path = matches.value_of("ledger_path").unwrap();
let lamports = value_t_or_exit!(matches, "lamports", u64); let lamports = value_t_or_exit!(matches, "lamports", u64);
let bootstrap_leader_lamports = value_t_or_exit!(matches, "bootstrap_leader_lamports", u64); let bootstrap_leader_lamports = value_t_or_exit!(matches, "bootstrap_leader_lamports", u64);
let bootstrap_leader_stake_lamports = let bootstrap_leader_stake_lamports =
value_t_or_exit!(matches, "bootstrap_leader_stake_lamports", u64); value_t_or_exit!(matches, "bootstrap_leader_stake_lamports", u64);
let storage_pool_lamports = value_t_or_exit!(matches, "storage_mining_pool_lamports", u64);
let bootstrap_leader_keypair = read_keypair(bootstrap_leader_keypair_file)?; let bootstrap_leader_keypair = read_keypair(bootstrap_leader_keypair_file)?;
let bootstrap_vote_keypair = read_keypair(bootstrap_vote_keypair_file)?; let bootstrap_vote_keypair = read_keypair(bootstrap_vote_keypair_file)?;
let bootstrap_stake_keypair = read_keypair(bootstrap_stake_keypair_file)?; let bootstrap_stake_keypair = read_keypair(bootstrap_stake_keypair_file)?;
let bootstrap_storage_keypair = read_keypair(bootstrap_storage_keypair_file)?; let bootstrap_storage_keypair = read_keypair(bootstrap_storage_keypair_file)?;
let storage_mining_keypair = read_keypair(storage_mining_pool_keypair)?;
let mint_keypair = read_keypair(mint_keypair_file)?; let mint_keypair = read_keypair(mint_keypair_file)?;
let (vote_account, vote_state) = vote_state::create_bootstrap_leader_account( let (vote_account, vote_state) = vote_state::create_bootstrap_leader_account(
@ -301,6 +322,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
1, 1,
), ),
), ),
(
storage_mining_keypair.pubkey(),
storage_contract::create_mining_pool_account(storage_pool_lamports),
),
], ],
&[ &[
solana_bpf_loader_program!(), solana_bpf_loader_program!(),

View File

@ -273,6 +273,8 @@ if [[ $node_type = replicator ]]; then
storage_keypair_path="$SOLANA_CONFIG_DIR"/replicator-storage-keypair$label.json storage_keypair_path="$SOLANA_CONFIG_DIR"/replicator-storage-keypair$label.json
ledger_config_dir=$SOLANA_CONFIG_DIR/replicator-ledger$label ledger_config_dir=$SOLANA_CONFIG_DIR/replicator-ledger$label
configured_flag=$SOLANA_CONFIG_DIR/replicator$label.configured configured_flag=$SOLANA_CONFIG_DIR/replicator$label.configured
storage_mining_pool_keypair_path="$SOLANA_CONFIG_DIR"/storage-mining-pool-keypair.json
storage_mining_pool_pubkey=$($solana_keygen pubkey "$storage_mining_pool_keypair_path")
mkdir -p "$SOLANA_CONFIG_DIR" mkdir -p "$SOLANA_CONFIG_DIR"
[[ -r "$identity_keypair_path" ]] || $solana_keygen new -o "$identity_keypair_path" [[ -r "$identity_keypair_path" ]] || $solana_keygen new -o "$identity_keypair_path"
@ -293,6 +295,7 @@ EOF
default_arg --identity "$identity_keypair_path" default_arg --identity "$identity_keypair_path"
default_arg --storage-keypair "$storage_keypair_path" default_arg --storage-keypair "$storage_keypair_path"
default_arg --ledger "$ledger_config_dir" default_arg --ledger "$ledger_config_dir"
default_arg --mining-pool "$storage_mining_pool_pubkey"
rsync_entrypoint_url=$(rsync_url "$entrypoint") rsync_entrypoint_url=$(rsync_url "$entrypoint")
elif [[ $node_type = bootstrap_leader ]]; then elif [[ $node_type = bootstrap_leader ]]; then

View File

@ -13,16 +13,19 @@ $solana_keygen new -o "$SOLANA_CONFIG_DIR"/bootstrap-leader-keypair.json
$solana_keygen new -o "$SOLANA_CONFIG_DIR"/bootstrap-leader-vote-keypair.json $solana_keygen new -o "$SOLANA_CONFIG_DIR"/bootstrap-leader-vote-keypair.json
$solana_keygen new -o "$SOLANA_CONFIG_DIR"/bootstrap-leader-stake-keypair.json $solana_keygen new -o "$SOLANA_CONFIG_DIR"/bootstrap-leader-stake-keypair.json
$solana_keygen new -o "$SOLANA_CONFIG_DIR"/bootstrap-leader-storage-keypair.json $solana_keygen new -o "$SOLANA_CONFIG_DIR"/bootstrap-leader-storage-keypair.json
$solana_keygen new -o "$SOLANA_CONFIG_DIR"/storage-mining-pool-keypair.json
args=("$@") args=("$@")
default_arg --bootstrap-leader-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-keypair.json default_arg --bootstrap-leader-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-keypair.json
default_arg --bootstrap-vote-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-vote-keypair.json default_arg --bootstrap-vote-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-vote-keypair.json
default_arg --bootstrap-stake-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-stake-keypair.json default_arg --bootstrap-stake-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-stake-keypair.json
default_arg --bootstrap-storage-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-storage-keypair.json default_arg --bootstrap-storage-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader-storage-keypair.json
default_arg --storage-mining-pool-keypair "$SOLANA_CONFIG_DIR"/storage-mining-pool-keypair.json
default_arg --ledger "$SOLANA_RSYNC_CONFIG_DIR"/ledger default_arg --ledger "$SOLANA_RSYNC_CONFIG_DIR"/ledger
default_arg --mint "$SOLANA_CONFIG_DIR"/mint-keypair.json default_arg --mint "$SOLANA_CONFIG_DIR"/mint-keypair.json
default_arg --lamports 100000000000000 default_arg --lamports 100000000000000
default_arg --bootstrap-leader-lamports 424242 default_arg --bootstrap-leader-lamports 424242
default_arg --storage-mining-pool-lamports 100000000
default_arg --target-lamports-per-signature 42 default_arg --target-lamports-per-signature 42
default_arg --target-signatures-per-slot 42 default_arg --target-signatures-per-slot 42
default_arg --hashes-per-tick auto default_arg --hashes-per-tick auto

View File

@ -11,8 +11,8 @@ use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature; use solana_sdk::signature::Signature;
use std::collections::HashMap; use std::collections::HashMap;
pub const VALIDATOR_REWARD: u64 = 25; pub const VALIDATOR_REWARD: u64 = 200;
pub const REPLICATOR_REWARD: u64 = 25; pub const REPLICATOR_REWARD: u64 = 200;
// Todo Tune this for actual use cases when replicators are feature complete // Todo Tune this for actual use cases when replicators are feature complete
pub const STORAGE_ACCOUNT_SPACE: u64 = 1024 * 8; pub const STORAGE_ACCOUNT_SPACE: u64 = 1024 * 8;
pub const MAX_PROOFS_PER_SEGMENT: usize = 80; pub const MAX_PROOFS_PER_SEGMENT: usize = 80;
@ -99,6 +99,17 @@ pub fn create_validator_storage_account(owner: Pubkey, lamports: u64) -> Account
storage_account storage_account
} }
// utility function, used by genesis
pub fn create_mining_pool_account(lamports: u64) -> Account {
let mut storage_account = Account::new(lamports, STORAGE_ACCOUNT_SPACE as usize, &crate::id());
storage_account
.set_state(&StorageContract::MiningPool)
.expect("set_state");
storage_account
}
pub struct StorageAccount<'a> { pub struct StorageAccount<'a> {
pub(crate) id: Pubkey, pub(crate) id: Pubkey,
account: &'a mut Account, account: &'a mut Account,

View File

@ -2,11 +2,20 @@ use clap::{crate_description, crate_name, crate_version, App, Arg};
use solana::cluster_info::{Node, FULLNODE_PORT_RANGE}; use solana::cluster_info::{Node, FULLNODE_PORT_RANGE};
use solana::contact_info::ContactInfo; use solana::contact_info::ContactInfo;
use solana::replicator::Replicator; use solana::replicator::Replicator;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil}; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::process::exit; use std::process::exit;
use std::sync::Arc; use std::sync::Arc;
// Return an error if a pubkey cannot be parsed.
fn is_pubkey(string: String) -> Result<(), String> {
match string.parse::<Pubkey>() {
Ok(_) => Ok(()),
Err(err) => Err(format!("{:?}", err)),
}
}
fn main() { fn main() {
solana_logger::setup(); solana_logger::setup();
@ -21,6 +30,14 @@ fn main() {
.takes_value(true) .takes_value(true)
.help("File containing an identity (keypair)"), .help("File containing an identity (keypair)"),
) )
.arg(
Arg::with_name("storage_mining_pool_pubkey")
.long("mining-pool")
.value_name("PUBKEY_BASE58_STR")
.takes_value(true)
.validator(is_pubkey)
.help("The public key of the storage mining pool"),
)
.arg( .arg(
Arg::with_name("entrypoint") Arg::with_name("entrypoint")
.short("n") .short("n")
@ -69,6 +86,10 @@ fn main() {
Keypair::new() Keypair::new()
}; };
let storage_mining_pool_pubkey = matches
.value_of("storage_mining_pool_pubkey")
.map(|value| value.parse::<Pubkey>().unwrap());
let entrypoint_addr = matches let entrypoint_addr = matches
.value_of("entrypoint") .value_of("entrypoint")
.map(|entrypoint| { .map(|entrypoint| {
@ -101,6 +122,6 @@ fn main() {
) )
.unwrap(); .unwrap();
replicator.run(); replicator.run(storage_mining_pool_pubkey);
replicator.close(); replicator.close();
} }

View File

@ -1,4 +1,5 @@
use crate::bank::Bank; use crate::bank::Bank;
use solana_sdk::account::Account;
use solana_sdk::client::{AsyncClient, Client, SyncClient}; use solana_sdk::client::{AsyncClient, Client, SyncClient};
use solana_sdk::fee_calculator::FeeCalculator; use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::hash::Hash; use solana_sdk::hash::Hash;
@ -95,6 +96,10 @@ impl SyncClient for BankClient {
Ok(self.bank.get_account(pubkey).map(|account| account.data)) Ok(self.bank.get_account(pubkey).map(|account| account.data))
} }
fn get_account(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
Ok(self.bank.get_account(pubkey))
}
fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> { fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> {
Ok(self.bank.get_balance(pubkey)) Ok(self.bank.get_balance(pubkey))
} }

View File

@ -7,6 +7,7 @@
//! Asynchronous implementations are expected to create transactions, sign them, and send //! Asynchronous implementations are expected to create transactions, sign them, and send
//! them but without waiting to see if the server accepted it. //! them but without waiting to see if the server accepted it.
use crate::account::Account;
use crate::fee_calculator::FeeCalculator; use crate::fee_calculator::FeeCalculator;
use crate::hash::Hash; use crate::hash::Hash;
use crate::instruction::Instruction; use crate::instruction::Instruction;
@ -37,6 +38,9 @@ pub trait SyncClient {
/// Get an account or None if not found. /// Get an account or None if not found.
fn get_account_data(&self, pubkey: &Pubkey) -> Result<Option<Vec<u8>>>; fn get_account_data(&self, pubkey: &Pubkey) -> Result<Option<Vec<u8>>>;
/// Get an account or None if not found.
fn get_account(&self, pubkey: &Pubkey) -> Result<Option<Account>>;
/// Get account balance or 0 if not found. /// Get account balance or 0 if not found.
fn get_balance(&self, pubkey: &Pubkey) -> Result<u64>; fn get_balance(&self, pubkey: &Pubkey) -> Result<u64>;