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)
|
.required(true)
|
||||||
.help("The transaction signature to confirm"),
|
.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(
|
||||||
SubCommand::with_name("pay")
|
SubCommand::with_name("pay")
|
||||||
.about("Send a payment")
|
.about("Send a payment")
|
||||||
|
|
|
@ -90,6 +90,7 @@ extern crate bytes;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate dirs;
|
extern crate dirs;
|
||||||
|
extern crate elf;
|
||||||
extern crate generic_array;
|
extern crate generic_array;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(any(feature = "chacha", feature = "cuda"))]
|
#[cfg(any(feature = "chacha", feature = "cuda"))]
|
||||||
|
|
155
src/wallet.rs
155
src/wallet.rs
|
@ -1,4 +1,5 @@
|
||||||
use bincode::{deserialize, serialize};
|
use bincode::{deserialize, serialize};
|
||||||
|
// use bpf_loader;
|
||||||
use bs58;
|
use bs58;
|
||||||
use budget_program::BudgetState;
|
use budget_program::BudgetState;
|
||||||
use budget_transaction::BudgetTransaction;
|
use budget_transaction::BudgetTransaction;
|
||||||
|
@ -6,8 +7,11 @@ use chrono::prelude::*;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use cluster_info::NodeInfo;
|
use cluster_info::NodeInfo;
|
||||||
use drone::DroneRequest;
|
use drone::DroneRequest;
|
||||||
|
use elf;
|
||||||
use fullnode::Config;
|
use fullnode::Config;
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
|
use loader_transaction::LoaderTransaction;
|
||||||
|
use native_loader;
|
||||||
use ring::rand::SystemRandom;
|
use ring::rand::SystemRandom;
|
||||||
use ring::signature::Ed25519KeyPair;
|
use ring::signature::Ed25519KeyPair;
|
||||||
use rpc_request::RpcRequest;
|
use rpc_request::RpcRequest;
|
||||||
|
@ -26,6 +30,10 @@ use std::{error, fmt, mem};
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use transaction::Transaction;
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum WalletCommand {
|
pub enum WalletCommand {
|
||||||
Address,
|
Address,
|
||||||
|
@ -33,6 +41,7 @@ pub enum WalletCommand {
|
||||||
Balance,
|
Balance,
|
||||||
Cancel(Pubkey),
|
Cancel(Pubkey),
|
||||||
Confirm(Signature),
|
Confirm(Signature),
|
||||||
|
Deploy(String),
|
||||||
// Pay(tokens, to, timestamp, timestamp_pubkey, witness(es), cancelable)
|
// Pay(tokens, to, timestamp, timestamp_pubkey, witness(es), cancelable)
|
||||||
Pay(
|
Pay(
|
||||||
i64,
|
i64,
|
||||||
|
@ -52,6 +61,7 @@ pub enum WalletCommand {
|
||||||
pub enum WalletError {
|
pub enum WalletError {
|
||||||
CommandNotRecognized(String),
|
CommandNotRecognized(String),
|
||||||
BadParameter(String),
|
BadParameter(String),
|
||||||
|
DynamicProgramError(String),
|
||||||
RpcRequestError(String),
|
RpcRequestError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +139,12 @@ pub fn parse_command(
|
||||||
Err(WalletError::BadParameter("Invalid signature".to_string()))
|
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)) => {
|
("pay", Some(pay_matches)) => {
|
||||||
let tokens = pay_matches.value_of("tokens").unwrap().parse()?;
|
let tokens = pay_matches.value_of("tokens").unwrap().parse()?;
|
||||||
let to = if pay_matches.is_present("to") {
|
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
|
// If client has positive balance, pay tokens to another address
|
||||||
WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => {
|
WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => {
|
||||||
let last_id = get_last_id(&config)?;
|
let last_id = get_last_id(&config)?;
|
||||||
|
@ -609,6 +752,18 @@ fn serialize_and_send_tx(
|
||||||
Ok(signature.as_str().unwrap().to_string())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in New Issue