Add deploy method to solana-wallet
This commit is contained in:
parent
ead7f4287a
commit
21eae981f9
|
@ -150,6 +150,18 @@ fn main() -> Result<(), Box<error::Error>> {
|
|||
.required(true)
|
||||
.help("The transaction signature to confirm"),
|
||||
),
|
||||
).subcommand(
|
||||
SubCommand::with_name("deploy")
|
||||
.about("Deploy a program")
|
||||
.arg(
|
||||
Arg::with_name("program-location")
|
||||
.index(1)
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("/path/to/program.o"),
|
||||
)
|
||||
// TODO: Add "loader" argument; current default is bpf_loader
|
||||
).subcommand(
|
||||
SubCommand::with_name("pay")
|
||||
.about("Send a payment")
|
||||
|
|
|
@ -90,6 +90,7 @@ extern crate bytes;
|
|||
extern crate chrono;
|
||||
extern crate clap;
|
||||
extern crate dirs;
|
||||
extern crate elf;
|
||||
extern crate generic_array;
|
||||
#[cfg(test)]
|
||||
#[cfg(any(feature = "chacha", feature = "cuda"))]
|
||||
|
|
155
src/wallet.rs
155
src/wallet.rs
|
@ -1,4 +1,5 @@
|
|||
use bincode::{deserialize, serialize};
|
||||
// use bpf_loader;
|
||||
use bs58;
|
||||
use budget_program::BudgetState;
|
||||
use budget_transaction::BudgetTransaction;
|
||||
|
@ -6,8 +7,11 @@ use chrono::prelude::*;
|
|||
use clap::ArgMatches;
|
||||
use cluster_info::NodeInfo;
|
||||
use drone::DroneRequest;
|
||||
use elf;
|
||||
use fullnode::Config;
|
||||
use hash::Hash;
|
||||
use loader_transaction::LoaderTransaction;
|
||||
use native_loader;
|
||||
use ring::rand::SystemRandom;
|
||||
use ring::signature::Ed25519KeyPair;
|
||||
use rpc_request::RpcRequest;
|
||||
|
@ -26,6 +30,10 @@ use std::{error, fmt, mem};
|
|||
use system_transaction::SystemTransaction;
|
||||
use transaction::Transaction;
|
||||
|
||||
// TODO: put these somewhere more logical
|
||||
const PLATFORM_SECTION_C: &str = ".text.entrypoint";
|
||||
const USERDATA_CHUNK_SIZE: usize = 256;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum WalletCommand {
|
||||
Address,
|
||||
|
@ -33,6 +41,7 @@ pub enum WalletCommand {
|
|||
Balance,
|
||||
Cancel(Pubkey),
|
||||
Confirm(Signature),
|
||||
Deploy(String),
|
||||
// Pay(tokens, to, timestamp, timestamp_pubkey, witness(es), cancelable)
|
||||
Pay(
|
||||
i64,
|
||||
|
@ -52,6 +61,7 @@ pub enum WalletCommand {
|
|||
pub enum WalletError {
|
||||
CommandNotRecognized(String),
|
||||
BadParameter(String),
|
||||
DynamicProgramError(String),
|
||||
RpcRequestError(String),
|
||||
}
|
||||
|
||||
|
@ -129,6 +139,12 @@ pub fn parse_command(
|
|||
Err(WalletError::BadParameter("Invalid signature".to_string()))
|
||||
}
|
||||
}
|
||||
("deploy", Some(deploy_matches)) => Ok(WalletCommand::Deploy(
|
||||
deploy_matches
|
||||
.value_of("program-location")
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
)),
|
||||
("pay", Some(pay_matches)) => {
|
||||
let tokens = pay_matches.value_of("tokens").unwrap().parse()?;
|
||||
let to = if pay_matches.is_present("to") {
|
||||
|
@ -364,6 +380,133 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
|
|||
))?,
|
||||
}
|
||||
}
|
||||
// Deploy a custom program to the chain
|
||||
WalletCommand::Deploy(ref program_location) => {
|
||||
let params = json!(format!("{}", config.id.pubkey()));
|
||||
let balance = RpcRequest::GetBalance
|
||||
.make_rpc_request(&config.rpc_addr, 1, Some(params))?
|
||||
.as_i64();
|
||||
if let Some(tokens) = balance {
|
||||
if tokens < 2 {
|
||||
Err(WalletError::DynamicProgramError(
|
||||
"Insufficient funds".to_string(),
|
||||
))?
|
||||
}
|
||||
}
|
||||
|
||||
let last_id = get_last_id(&config)?;
|
||||
let program = Keypair::new();
|
||||
let program_userdata = elf::File::open_path(program_location)
|
||||
.map_err(|_| {
|
||||
WalletError::DynamicProgramError("Could not parse program file".to_string())
|
||||
})?.get_section(PLATFORM_SECTION_C)
|
||||
.ok_or_else(|| {
|
||||
WalletError::DynamicProgramError(
|
||||
"Could not find entrypoint in program file".to_string(),
|
||||
)
|
||||
})?.data
|
||||
.clone();
|
||||
|
||||
// Loader instance for testing; REMOVE
|
||||
let loader = Keypair::new();
|
||||
let tx = Transaction::system_create(
|
||||
&config.id,
|
||||
loader.pubkey(),
|
||||
last_id,
|
||||
1,
|
||||
56,
|
||||
native_loader::id(),
|
||||
0,
|
||||
);
|
||||
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||
|
||||
let name = String::from("bpf_loader");
|
||||
let tx = Transaction::write(
|
||||
&loader,
|
||||
native_loader::id(),
|
||||
0,
|
||||
name.as_bytes().to_vec(),
|
||||
last_id,
|
||||
0,
|
||||
);
|
||||
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||
|
||||
let tx = Transaction::finalize(&loader, native_loader::id(), last_id, 0);
|
||||
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||
|
||||
let tx = Transaction::system_spawn(&loader, last_id, 0);
|
||||
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||
// end loader instance
|
||||
|
||||
let tx = Transaction::system_create(
|
||||
&config.id,
|
||||
program.pubkey(),
|
||||
last_id,
|
||||
1,
|
||||
program_userdata.len() as u64,
|
||||
// bpf_loader::id(),
|
||||
loader.pubkey(),
|
||||
0,
|
||||
);
|
||||
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||
if !confirm_tx(&config, &signature_str)? {
|
||||
Err(WalletError::DynamicProgramError(
|
||||
"Program allocate space failed".to_string(),
|
||||
))?
|
||||
}
|
||||
|
||||
let mut offset = 0;
|
||||
let write_result = program_userdata
|
||||
.chunks(USERDATA_CHUNK_SIZE)
|
||||
.map(|chunk| {
|
||||
let tx = Transaction::write(
|
||||
&program,
|
||||
// bpf_loader::id(),
|
||||
loader.pubkey(),
|
||||
offset,
|
||||
chunk.to_vec(),
|
||||
last_id,
|
||||
0,
|
||||
);
|
||||
let signature_str = serialize_and_send_tx(&config, &tx).unwrap();
|
||||
offset += USERDATA_CHUNK_SIZE as u32;
|
||||
signature_str
|
||||
}).map(|signature| confirm_tx(&config, &signature).unwrap())
|
||||
.all(|status| status);
|
||||
|
||||
if write_result {
|
||||
let last_id = get_last_id(&config)?;
|
||||
let tx = Transaction::finalize(
|
||||
&program,
|
||||
// bpf_loader::id(),
|
||||
loader.pubkey(),
|
||||
last_id,
|
||||
0,
|
||||
);
|
||||
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||
if !confirm_tx(&config, &signature_str)? {
|
||||
Err(WalletError::DynamicProgramError(
|
||||
"Program finalize transaction failed".to_string(),
|
||||
))?
|
||||
}
|
||||
|
||||
let tx = Transaction::system_spawn(&program, last_id, 0);
|
||||
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||
if !confirm_tx(&config, &signature_str)? {
|
||||
Err(WalletError::DynamicProgramError(
|
||||
"Program spawn failed".to_string(),
|
||||
))?
|
||||
}
|
||||
|
||||
Ok(json!({
|
||||
"programId": format!("{}", program.pubkey()),
|
||||
}).to_string())
|
||||
} else {
|
||||
Err(WalletError::DynamicProgramError(
|
||||
"Program chunks write error".to_string(),
|
||||
))?
|
||||
}
|
||||
}
|
||||
// If client has positive balance, pay tokens to another address
|
||||
WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => {
|
||||
let last_id = get_last_id(&config)?;
|
||||
|
@ -609,6 +752,18 @@ fn serialize_and_send_tx(
|
|||
Ok(signature.as_str().unwrap().to_string())
|
||||
}
|
||||
|
||||
fn confirm_tx(config: &WalletConfig, signature: &str) -> Result<bool, Box<error::Error>> {
|
||||
let params = json!(signature.to_string());
|
||||
let status =
|
||||
RpcRequest::ConfirmTransaction.make_rpc_request(&config.rpc_addr, 1, Some(params))?;
|
||||
if status.as_bool().is_none() {
|
||||
Err(WalletError::RpcRequestError(
|
||||
"Received result of an unexpected type".to_string(),
|
||||
))?
|
||||
}
|
||||
Ok(status.as_bool().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Reference in New Issue