Skip to content

Instantly share code, notes, and snippets.

@bennofs
Created January 17, 2021 02:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennofs/84d81d43a8ed872dd5600044ef82de06 to your computer and use it in GitHub Desktop.
Save bennofs/84d81d43a8ed872dd5600044ef82de06 to your computer and use it in GitHub Desktop.
#![allow(unused)]
use std::{collections::HashSet, path::Path};
pub use std::{process::ExitStatus, fs::File, io::Read, net::SocketAddr, net::SocketAddrV4, net::UdpSocket, path::PathBuf, time::Duration, time::Instant};
pub use std::collections::HashMap;
pub use bincode::serialize;
use itertools::izip;
pub use log::*;
use solana_bpf_loader_program::{ThisInstructionMeter, solana_bpf_loader_deprecated_program, solana_bpf_loader_program, solana_bpf_loader_upgradeable_program};
pub use solana_bpf_loader_program::{BPFError, bpf_verifier};
pub use solana_cli_output::display::new_spinner_progress_bar;
use solana_cli_output::display::println_transaction;
use solana_client::rpc_request::RpcError;
pub use solana_client::{client_error::ClientErrorKind, rpc_client::{RpcClient}, rpc_config::RpcSendTransactionConfig, rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS};
pub use solana_client::rpc_response::RpcLeaderSchedule;
pub use solana_core::poh_recorder::Slot;
pub use solana_faucet::faucet::request_airdrop_transaction;
pub use solana_perf::packet::{Packet, Packets};
use solana_program::bpf_loader_upgradeable;
use solana_rbpf::vm::EbpfVm;
pub use solana_rbpf::vm::{Config, Executable};
use solana_runtime::bank::{Bank, Builtin, Builtins, ExecuteTimings, NonceRollbackInfo, TransactionBalancesSet, TransactionResults};
pub use solana_sdk::{account::Account, commitment_config::CommitmentConfig, genesis_config::GenesisConfig, signature::Keypair, signature::Signer, signature::keypair_from_seed, signature::write_keypair, signature::{Signature, write_keypair_file}, signers::Signers, transaction::Transaction};
pub use solana_cli::send_tpu::{get_leader_tpu, send_transaction_tpu};
pub use solana_sdk::system_program;
pub use solana_sdk::system_instruction;
pub use solana_program::{bpf_loader, clock::MAX_HASH_AGE_IN_SECONDS, hash::{Hash, Hasher}, instruction::CompiledInstruction, instruction::{AccountMeta, Instruction}, native_token::lamports_to_sol, loader_instruction, message::Message, message::MessageHeader, native_token::Sol, native_token::sol_to_lamports, pubkey::Pubkey};
use solana_transaction_status::{ConfirmedTransaction, InnerInstructions, TransactionStatusMeta, TransactionWithStatusMeta, token_balances::{TransactionTokenBalancesSet, collect_token_balance_from_account, collect_token_balances}};
pub use solana_transaction_status::UiTransactionEncoding;
pub use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
pub const DATA_CHUNK_SIZE: usize = 229;
pub fn send_and_confirm_transactions_with_spinner<T: Signers>(
rpc_client: &RpcClient,
mut transactions: Vec<Transaction>,
signer_keys: &T
) -> Result<(), Box<dyn error::Error>> {
let progress_bar = new_spinner_progress_bar();
let mut send_retries = 5;
let mut leader_schedule: Option<RpcLeaderSchedule> = None;
let mut leader_schedule_epoch = 0;
let send_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let cluster_nodes = rpc_client.get_cluster_nodes().ok();
loop {
let mut status_retries = 15;
progress_bar.set_message("Finding leader node...");
let epoch_info = rpc_client.get_epoch_info().unwrap();
if epoch_info.epoch > leader_schedule_epoch || leader_schedule.is_none() {
leader_schedule = rpc_client.get_leader_schedule(Some(epoch_info.absolute_slot))?;
leader_schedule_epoch = epoch_info.epoch;
}
let tpu_address = get_leader_tpu(
std::cmp::min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
leader_schedule.as_ref(),
cluster_nodes.as_ref(),
);
// Send all transactions
let mut pending_transactions = HashMap::new();
let num_transactions = transactions.len();
for transaction in transactions {
if let Some(tpu_address) = tpu_address {
let wire_transaction =
serialize(&transaction).expect("serialization should succeed");
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
} else {
let _result = rpc_client
.send_transaction(
&transaction
)
.ok();
}
pending_transactions.insert(transaction.signatures[0], transaction);
progress_bar.set_message(&format!(
"[{}/{}] Total Transactions sent",
pending_transactions.len(),
num_transactions
));
}
// Collect statuses for all the transactions, drop those that are confirmed
while status_retries > 0 {
status_retries -= 1;
progress_bar.set_message(&format!(
"[{}/{}] Transactions confirmed",
num_transactions - pending_transactions.len(),
num_transactions
));
let mut statuses = vec![];
let pending_signatures = pending_transactions.keys().cloned().collect::<Vec<_>>();
for pending_signatures_chunk in
pending_signatures.chunks(MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS - 1)
{
statuses.extend(
rpc_client
.get_signature_statuses_with_history(pending_signatures_chunk)?
.value
.into_iter(),
);
}
assert_eq!(statuses.len(), pending_signatures.len());
for (signature, status) in pending_signatures.into_iter().zip(statuses.into_iter()) {
if let Some(status) = status {
if status.confirmations.is_none() || status.confirmations.unwrap() > 1 {
let _ = pending_transactions.remove(&signature);
}
}
progress_bar.set_message(&format!(
"[{}/{}] Transactions confirmed",
num_transactions - pending_transactions.len(),
num_transactions
));
}
if pending_transactions.is_empty() {
return Ok(());
}
std::thread::sleep(Duration::from_millis(500));
}
if send_retries == 0 {
panic!("transaction failed");
}
send_retries -= 1;
// Re-sign any failed transactions with a new blockhash and retry
let (blockhash, _fee_calculator) = rpc_client
.get_recent_blockhash()
.unwrap();
transactions = vec![];
for (_, mut transaction) in pending_transactions.into_iter() {
transaction.try_sign(signer_keys, blockhash)?;
transactions.push(transaction);
}
}
}
pub fn build_contract(name: &str) {
let mut proc = std::process::Command::new("./cargo-build-bpf")
.args(&["--manifest-path", &format!("hacks/{}/Cargo.toml", name)])
.spawn()
.unwrap();
let status = proc.wait().unwrap();
if !status.success() {
panic!("compile failed: {:?}", status);
}
}
pub fn write_contract_data(rpc_client: &RpcClient, signer: &Keypair, program_id: &Keypair, program_data: Vec<u8>) -> bool {
let loader_id = bpf_loader::id();
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(program_data.len()).unwrap();
let signers = [signer, &program_id];
// Check program account to see if partial initialization has occurred
let (initial_instructions, _balance_needed) = if let Ok(account) = rpc_client
.get_account(&program_id.pubkey())
{
let mut instructions: Vec<Instruction> = vec![];
let mut balance_needed = 0;
if account.executable {
return true;
}
if account.owner != loader_id && !system_program::check_id(&account.owner) {
panic!("program account is already owned by another account");
}
if account.data.is_empty() && system_program::check_id(&account.owner) {
instructions.push(system_instruction::allocate(
&program_id.pubkey(),
program_data.len() as u64,
));
if account.owner != loader_id {
instructions.push(system_instruction::assign(&program_id.pubkey(), &loader_id));
}
}
if account.lamports < minimum_balance {
let balance = minimum_balance - account.lamports;
instructions.push(system_instruction::transfer(
&signer.pubkey(),
&program_id.pubkey(),
balance,
));
balance_needed = balance;
}
(instructions, balance_needed)
} else {
(
vec![system_instruction::create_account(
&signer.pubkey(),
&program_id.pubkey(),
minimum_balance,
program_data.len() as u64,
&loader_id,
)],
minimum_balance,
)
};
let initial_message = if !initial_instructions.is_empty() {
Some(Message::new(
&initial_instructions,
Some(&signer.pubkey()),
))
} else {
None
};
let mut write_messages = vec![];
for (chunk, i) in program_data.chunks(DATA_CHUNK_SIZE).zip(0..) {
let instruction = loader_instruction::write(
&program_id.pubkey(),
&loader_id,
(i * DATA_CHUNK_SIZE) as u32,
chunk.to_vec(),
);
let message = Message::new(&[instruction], Some(&signers[0].pubkey()));
write_messages.push(message);
}
let (blockhash, _fee_calculator) = rpc_client
.get_recent_blockhash()
.unwrap();
if let Some(message) = initial_message {
trace!("Creating or modifying program account");
let num_required_signatures = message.header.num_required_signatures;
let mut initial_transaction = Transaction::new_unsigned(message);
// Most of the initial_transaction combinations require both the fee-payer and new program
// account to sign the transaction. One (transfer) only requires the fee-payer signature.
// This check is to ensure signing does not fail on a KeypairPubkeyMismatch error from an
// extraneous signature.
if num_required_signatures == 2 {
initial_transaction.sign(&signers, blockhash);
} else {
initial_transaction.sign(&[signers[0]], blockhash);
}
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_commitment(
&initial_transaction,
CommitmentConfig::single(),
);
if let Err(e) = result {
panic!("program account allocation failed: {:?}", e);
}
}
let (blockhash, _) = rpc_client
.get_recent_blockhash().unwrap();
let mut write_transactions = vec![];
for message in write_messages.into_iter() {
let mut tx = Transaction::new_unsigned(message);
tx.sign(&signers, blockhash);
write_transactions.push(tx);
}
trace!("Writing program data");
send_and_confirm_transactions_with_spinner(
&rpc_client,
write_transactions,
&signers,
).expect("data writes to program account");
false
}
pub fn write_buffer_data(rpc_client: &RpcClient, signer: &Keypair, buffer: &Keypair, program_data: Vec<u8>, buf_size: usize) -> bool {
let loader_id = bpf_loader_upgradeable::id();
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(buf_size).unwrap();
let signers = [signer, &buffer];
;
// check if we need to initialize the buffer
let initial_instructions = if rpc_client.get_account(&buffer.pubkey()).is_err() {
bpf_loader_upgradeable::create_buffer(
&signer.pubkey(),
&buffer.pubkey(),
Some(&signer.pubkey()),
minimum_balance,
buf_size,
).expect("create buffer instr")
} else {
Vec::new()
};
let initial_message = if !initial_instructions.is_empty() {
Some(Message::new(
&initial_instructions,
Some(&signer.pubkey()),
))
} else {
None
};
let mut write_messages = vec![];
for (chunk, i) in program_data.chunks(DATA_CHUNK_SIZE).zip(0..) {
let instruction = bpf_loader_upgradeable::write(
&buffer.pubkey(),
Some(&signer.pubkey()),
(i * DATA_CHUNK_SIZE) as u32,
chunk.to_vec(),
);
let message = Message::new(&[instruction], Some(&signers[0].pubkey()));
write_messages.push(message);
}
let (blockhash, _fee_calculator) = rpc_client
.get_recent_blockhash()
.unwrap();
if let Some(message) = initial_message {
trace!("Creating or modifying program account");
let num_required_signatures = message.header.num_required_signatures;
let mut initial_transaction = Transaction::new_unsigned(message);
// Most of the initial_transaction combinations require both the fee-payer and new program
// account to sign the transaction. One (transfer) only requires the fee-payer signature.
// This check is to ensure signing does not fail on a KeypairPubkeyMismatch error from an
// extraneous signature.
if num_required_signatures == 2 {
initial_transaction.sign(&signers, blockhash);
} else {
initial_transaction.sign(&[signers[0]], blockhash);
}
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_commitment(
&initial_transaction,
CommitmentConfig::single(),
);
if let Err(e) = result {
panic!("program account allocation failed: {:?}", e);
}
}
let (blockhash, _) = rpc_client
.get_recent_blockhash().unwrap();
let mut write_transactions = vec![];
for message in write_messages.into_iter() {
let mut tx = Transaction::new_unsigned(message);
tx.sign(&signers, blockhash);
write_transactions.push(tx);
}
trace!("Writing buffer data");
send_and_confirm_transactions_with_spinner(
&rpc_client,
write_transactions,
&signers,
).expect("data writes to buffer account");
false
}
pub fn ensure_deployed<F: FnOnce(&mut [u8])>(
rpc_client: &RpcClient,
signer: &Keypair,
program_location: &str,
patch: F
) -> Keypair {
let mut file = File::open(program_location).unwrap();
let mut program_data = Vec::new();
file.read_to_end(&mut program_data).unwrap();
patch(&mut program_data);
let mut hasher = Hasher::default();
hasher.hash(&program_data);
let program_id = solana_sdk::signature::keypair_from_seed(&hasher.result().to_bytes()).unwrap();
Executable::<BPFError, ThisInstructionMeter>::from_elf(&program_data, Some(|x| bpf_verifier::check(x, false)), Config::default())
.expect("load elf");
let executable = write_contract_data(&rpc_client, &signer, &program_id, program_data);
if executable {
return program_id;
}
let loader_id = bpf_loader::id();
let signers = [signer, &program_id];
let instruction = loader_instruction::finalize(&program_id.pubkey(), &loader_id);
let finalize_message = Message::new(&[instruction], Some(&signers[0].pubkey()));
let (blockhash, _) = rpc_client
.get_recent_blockhash()
.unwrap();
let mut finalize_tx = Transaction::new_unsigned(finalize_message);
finalize_tx.sign(&signers, blockhash);
trace!("Finalizing program account");
println!("{:?}", &finalize_tx);
rpc_client
.send_and_confirm_transaction_with_spinner_and_config(
&finalize_tx,
rpc_client.commitment(),
RpcSendTransactionConfig {
skip_preflight: true,
..RpcSendTransactionConfig::default()
},
)
.expect("finalize program account");
program_id
}
pub fn ensure_nonce_account(client: &RpcClient, signer: &Keypair) -> (Keypair, Hash) {
let id = keypair_from_seed(&[14; 32]).expect("create keypair from seed");
println!("nonce pubkey {:?}", id.pubkey());
if let Ok(account) = client.get_account(&id.pubkey()) {
let state = solana_client::nonce_utils::data_from_account(&account).expect("is nonce account");
println!("found existing nonce account: {:?} lamports, owner {:?}, state {:?}", account.lamports, account.owner, state);
return (id, state.blockhash);
}
let instructions = system_instruction::create_nonce_account(
&signer.pubkey(),
&id.pubkey(),
&signer.pubkey(),
client.get_minimum_balance_for_rent_exemption(solana_program::nonce::State::size()).expect("get minimum balance")
);
let message = Message::new(&instructions, Some(&signer.pubkey()));
let recent_blockhash = client.get_recent_blockhash().unwrap().0;
let transaction = Transaction::new(&[signer, &id], message, recent_blockhash);
client.send_and_confirm_transaction_with_spinner(&transaction).expect("create nonce account");
let account = client.get_account(&id.pubkey()).expect("none account created ok");
(id, solana_client::nonce_utils::data_from_account(&account).expect("is nonce account").blockhash)
}
pub fn wait_until_confirmed(client: &RpcClient, signature: &Signature) {
let now = Instant::now();
let progress_bar = new_spinner_progress_bar();
let mut confirmations = 0;
let desired_confirmations = MAX_LOCKOUT_HISTORY + 1;
loop {
progress_bar.set_message(&format!(
"[{}/{}] Waiting for transaction {}",
confirmations + 1,
desired_confirmations,
signature,
));
if client.get_signature_status(signature).expect("get signature status").is_some() {
progress_bar.set_message("Transaction confirmed");
progress_bar.finish_and_clear();
break;
}
std::thread::sleep(Duration::from_millis(500));
confirmations = client
.get_num_blocks_since_signature_confirmation(signature)
.unwrap_or(confirmations);
if now.elapsed().as_secs() >= MAX_HASH_AGE_IN_SECONDS as u64 {
panic!("transaction not finalized. \
This can happen when a transaction lands in an abandoned fork. \
Please retry.".to_string());
}
}
}
pub fn wait_and_print_transaction(client: &RpcClient, signature: &Signature) {
wait_until_confirmed(client, signature);
let confirmed_transaction = client.get_confirmed_transaction(
signature,
UiTransactionEncoding::Base64
).expect("get confirmed transaction");
println!(
"\nTransaction executed in slot {}:",
confirmed_transaction.slot
);
solana_cli_output::display::println_transaction(
&confirmed_transaction
.transaction
.transaction
.decode()
.expect("Successful decode"),
&confirmed_transaction.transaction.meta,
" ",
);
}
pub fn verify_sigs(transaction: &Transaction) -> u8 {
let mut packet = Packet::default();
let payload = serialize(transaction).unwrap();
packet.meta.size = payload.len();
packet.data[..payload.len()].copy_from_slice(&payload);
solana_perf::sigverify::ed25519_verify_cpu(&[Packets::new(vec![
packet
])])[0][0]
}
pub fn ensure_balance(client: &RpcClient, target: &Keypair, signer: &Keypair, wanted: u64) {
let current = client.get_balance(&target.pubkey()).expect("get balance for ensure balance");
let (recent_blockhash, _) = client.get_recent_blockhash().expect("get recent blockhash");
if current > wanted {
client.send_and_confirm_transaction_with_spinner(&Transaction::new(
&[signer, target],
Message::new(&[
system_instruction::transfer(&target.pubkey(), &signer.pubkey(), current - wanted)
], Some(&signer.pubkey())),
recent_blockhash,
)).expect("transfer to ensure balance");
} else if current < wanted {
client.send_and_confirm_transaction_with_spinner(&Transaction::new(
&[signer, target],
Message::new(&[
system_instruction::transfer(&signer.pubkey(), &target.pubkey(), wanted - current)
], Some(&signer.pubkey())),
recent_blockhash,
)).expect("transfer to ensure balance");
}
}
pub fn setup_client() -> (RpcClient, Keypair, Pubkey) {
let url = std::env::args().nth(1).unwrap_or("http://127.0.0.1:8899".to_string());
let client = RpcClient::new(url.to_string());
let signer = keypair_from_seed(&[13; 32]).unwrap();
let (blockhash, _) = client.get_recent_blockhash().unwrap();
let host = solana_net_utils::parse_host(&url::Url::parse(&url).unwrap().host().unwrap().to_string()).unwrap();
let addr = SocketAddr::new(host, 9900);
let transaction = request_airdrop_transaction(&addr, &signer.pubkey(), sol_to_lamports(10.0), blockhash).unwrap();
if client.get_balance(&signer.pubkey()).expect("get balance") < 10_000_000 {
client.send_and_confirm_transaction_with_spinner(&transaction).unwrap();
}
let faucet_pubkey = transaction.signer_key(0, 0).expect("faucet pubkey");
println!("network version: {:?}", client.get_version().expect("get version"));
println!("signer: {} balance {}", &signer.pubkey(), client.get_balance(&signer.pubkey()).expect("get balance"));
println!("faucet: {} balance {}", &faucet_pubkey, client.get_balance(&faucet_pubkey).expect("get balance"));
(client, signer, faucet_pubkey.clone())
}
pub fn tx_with_instructions(signers: Vec<&Keypair>, blockhash: Hash, instructions: &[Instruction]) -> Transaction {
let message = Message::new(instructions, Some(&signers[0].pubkey()));
let num_sigs: usize = message.header.num_required_signatures.into();
let required_sigs = message.account_keys[..num_sigs].into_iter().copied().collect::<HashSet<_>>();
let provided_sigs = signers.iter().map(|x| x.pubkey()).collect::<HashSet<_>>();
for key in required_sigs.difference(&provided_sigs) {
println!("missing signature from {}", key.to_string());
}
for key in provided_sigs.difference(&required_sigs) {
println!("unnecessary signature from {}", key.to_string());
}
Transaction::new(&signers, message, blockhash)
}
pub fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec<ConfirmedTransaction> {
let batch = bank.prepare_batch(txs, None);
let mut mint_decimals = HashMap::new();
let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
let slot = bank.slot();
let mut timings = ExecuteTimings::default();
let (TransactionResults { execution_results, .. },
TransactionBalancesSet { pre_balances, post_balances, .. },
inner_instructions,
transaction_logs)
= bank.load_execute_and_commit_transactions(&batch, std::usize::MAX, true, true, true, &mut timings);
let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
izip!(
txs.iter(),
execution_results.into_iter(),
inner_instructions.into_iter(),
pre_balances.into_iter(),
post_balances.into_iter(),
tx_pre_token_balances.into_iter(),
tx_post_token_balances.into_iter(),
transaction_logs.into_iter(),
).map(|(
tx,
(execute_result, nonce_rollback),
inner_instructions,
pre_balances,
post_balances,
pre_token_balances,
post_token_balances,
log_messages,
)| {
let fee_calculator = nonce_rollback
.map(|nonce_rollback| nonce_rollback.fee_calculator())
.unwrap_or_else(|| {
bank.get_fee_calculator(&tx.message().recent_blockhash)
})
.expect("FeeCalculator must exist");
let fee = fee_calculator.calculate_fee(tx.message());
let inner_instructions = inner_instructions.map(|inner_instructions| {
inner_instructions
.into_iter()
.enumerate()
.map(|(index, instructions)| InnerInstructions {
index: index as u8,
instructions
})
.filter(|i| !i.instructions.is_empty())
.collect()
});
let tx_status_meta = TransactionStatusMeta {
status: execute_result,
fee,
pre_balances,
post_balances,
pre_token_balances: Some(pre_token_balances),
post_token_balances: Some(post_token_balances),
inner_instructions,
log_messages: Some(log_messages),
};
ConfirmedTransaction {
slot,
transaction: TransactionWithStatusMeta {
transaction: tx.clone(),
meta: Some(tx_status_meta)
}
}
}).collect()
}
pub fn execute_as_transaction(bank: &Bank, signers: Vec<&Keypair>, instructions: &[Instruction]) -> ConfirmedTransaction {
let tx = tx_with_instructions(signers, bank.last_blockhash(), instructions);
return execute_transactions(bank, &[tx]).into_iter().next().expect("must have tx result");
}
pub fn print_confirmed_tx(name: &str, confirmed_tx: ConfirmedTransaction) {
let tx = confirmed_tx.transaction.transaction.clone();
let encoded = confirmed_tx.encode(UiTransactionEncoding::JsonParsed);
println!("EXECUTE {} (slot {})", name, encoded.slot);
println_transaction(&tx, &encoded.transaction.meta, " ");
}
pub fn make_genesis(faucet: &Pubkey, extra_programs: &[(Pubkey, &str)]) -> GenesisConfig {
let mut config = GenesisConfig::new(
&[
(faucet.clone(), Account::new(1u64 << 48, 0, &system_program::id()),)
],
&[],
);
// add spl programs
let mut programs = vec![
("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".parse().unwrap(), "BPFLoader2111111111111111111111111111111111".parse().unwrap(), "spl_token-2.0.6.so".to_string()),
("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo".parse().unwrap(), "BPFLoader1111111111111111111111111111111111".parse().unwrap(), "spl_memo-1.0.0.so".to_string()),
("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL".parse().unwrap(), "BPFLoader2111111111111111111111111111111111".parse().unwrap(), "spl_associated-token-account-1.0.1.so".to_string()),
("Feat1YXHhH6t1juaWF74WLcfv4XoNocjXA6sPWHNgAse".parse().unwrap(), "BPFLoader2111111111111111111111111111111111".parse().unwrap(), "spl_feature-proposal-1.0.0.so".to_string()),
];
for (pk, program) in extra_programs {
build_contract(*program);
programs.push((*pk, "BPFLoader2111111111111111111111111111111111".parse().unwrap(), format!("target/deploy/{}.so", program).to_string()));
}
for (id, loader, file_name) in programs.iter() {
let mut program_data = Vec::new();
File::open(file_name)
.and_then(|mut f| f.read_to_end(&mut program_data))
.unwrap_or_else(|e| {
panic!("cannot read genesis program code file {}: {}", file_name, e);
});
config.add_account(*id, Account {
lamports: config.rent.minimum_balance(program_data.len()),
data: program_data,
executable: true,
owner: *loader,
rent_epoch: 0,
});
}
config
}
pub fn setup_bank(genesis: &GenesisConfig, dir: &Path) -> Bank {
Bank::new_with_paths(
genesis,
vec![dir.to_path_buf()],
&[],
None,
Some(&Builtins {
genesis_builtins: [solana_bpf_loader_upgradeable_program!(), solana_bpf_loader_program!(), solana_bpf_loader_deprecated_program!()].iter().map(|p| {
Builtin::new(&p.0, p.1, p.2)
}).collect(),
feature_builtins: vec![],
}),
HashSet::new(),
false,
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment