forked from solana-labs/solana
-
Notifications
You must be signed in to change notification settings - Fork 295
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SVM: add a sample stand-alone application based on SVM (#2217)
Add an extended example to the SVM crate - json-rpc-server is a server application that implements several Solana Json RPC commands, in particular simulateTransaction command to run a transaction in minimal Solana run-time environment required to use SVM. - json-rpc-client is a sample client application that submits simulateTransaction requests to json-rpc server. - json-rpc-program is source code of an on-chain program executed in the context of the simultateTransaction command submitted by the client program.
- Loading branch information
Showing
16 changed files
with
1,792 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
This is an example application using SVM to implement a tiny subset of | ||
Solana RPC protocol for the purpose of simulating transaction | ||
execution without having to use the entire Solana Runtime. | ||
|
||
The exmample consists of two host applications | ||
- json-rpc-server -- the RPC server that accepts incoming RPC requests | ||
and performs transaction simulation sending back the results, | ||
- json-rpc-client -- the RPC client program that sends transactions to | ||
json-rpc-server for simulation, | ||
|
||
and | ||
|
||
- json-rpc-program is the source code of on-chain program that is | ||
executed in a transaction sent by json-rpc-client. | ||
|
||
To run the example, compile the json-rpc-program with `cargo | ||
build-sbf` command. Using solana-test-validator create a ledger, or | ||
use an existing one, and deploy the compiled program to store it in | ||
the ledger. Using agave-ledger-tool dump ledger accounts to a file, | ||
e.g. `accounts.out`. Now start the json-rpc-server, e.g. | ||
``` | ||
cargo run --manifest-path json-rpc-server/Cargo.toml -- -l test-ledger -a accounts.json | ||
``` | ||
|
||
Finally, run the client program. | ||
``` | ||
cargo run --manifest-path json-rpc-client/Cargo.toml -- -C config.yml -k json-rpc-program/target/deploy/helloworld-keypair.json -u localhost | ||
``` | ||
|
||
The client will communicate with the server and print the responses it | ||
recieves from the server. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
use { | ||
crate::utils, | ||
solana_client::rpc_client::RpcClient, | ||
solana_sdk::{ | ||
commitment_config::CommitmentConfig, | ||
instruction::{AccountMeta, Instruction}, | ||
message::Message, | ||
signature::Signer, | ||
signer::keypair::{read_keypair_file, Keypair}, | ||
transaction::Transaction, | ||
}, | ||
}; | ||
|
||
/// Establishes a RPC connection with the Simulation server. | ||
/// Information about the server is gleened from the config file `config.yml`. | ||
pub fn establish_connection(url: &Option<&str>, config: &Option<&str>) -> utils::Result<RpcClient> { | ||
let rpc_url = match url { | ||
Some(x) => { | ||
if *x == "localhost" { | ||
"http://localhost:8899".to_string() | ||
} else { | ||
String::from(*x) | ||
} | ||
} | ||
None => utils::get_rpc_url(config)?, | ||
}; | ||
Ok(RpcClient::new_with_commitment( | ||
rpc_url, | ||
CommitmentConfig::confirmed(), | ||
)) | ||
} | ||
|
||
/// Loads keypair information from the file located at KEYPAIR_PATH | ||
/// and then verifies that the loaded keypair information corresponds | ||
/// to an executable account via CONNECTION. Failure to read the | ||
/// keypair or the loaded keypair corresponding to an executable | ||
/// account will result in an error being returned. | ||
pub fn get_program(keypair_path: &str, connection: &RpcClient) -> utils::Result<Keypair> { | ||
let program_keypair = read_keypair_file(keypair_path).map_err(|e| { | ||
utils::Error::InvalidConfig(format!( | ||
"failed to read program keypair file ({}): ({})", | ||
keypair_path, e | ||
)) | ||
})?; | ||
|
||
let program_info = connection.get_account(&program_keypair.pubkey())?; | ||
if !program_info.executable { | ||
return Err(utils::Error::InvalidConfig(format!( | ||
"program with keypair ({}) is not executable", | ||
keypair_path | ||
))); | ||
} | ||
|
||
Ok(program_keypair) | ||
} | ||
|
||
pub fn say_hello(player: &Keypair, program: &Keypair, connection: &RpcClient) -> utils::Result<()> { | ||
let greeting_pubkey = utils::get_greeting_public_key(&player.pubkey(), &program.pubkey())?; | ||
println!("greeting pubkey {greeting_pubkey:?}"); | ||
|
||
// Submit an instruction to the chain which tells the program to | ||
// run. We pass the account that we want the results to be stored | ||
// in as one of the account arguments which the program will | ||
// handle. | ||
|
||
let data = [1u8]; | ||
let instruction = Instruction::new_with_bytes( | ||
program.pubkey(), | ||
&data, | ||
vec![AccountMeta::new(greeting_pubkey, false)], | ||
); | ||
let message = Message::new(&[instruction], Some(&player.pubkey())); | ||
let transaction = Transaction::new(&[player], message, connection.get_latest_blockhash()?); | ||
|
||
let response = connection.simulate_transaction(&transaction)?; | ||
println!("{:?}", response); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use clap::{crate_description, crate_name, crate_version, App, Arg}; | ||
|
||
mod client; | ||
mod utils; | ||
|
||
fn main() { | ||
let version = crate_version!().to_string(); | ||
let args = std::env::args().collect::<Vec<_>>(); | ||
let matches = App::new(crate_name!()) | ||
.about(crate_description!()) | ||
.version(version.as_str()) | ||
.arg( | ||
Arg::with_name("config") | ||
.long("config") | ||
.short("C") | ||
.takes_value(true) | ||
.value_name("CONFIG") | ||
.help("Config filepath"), | ||
) | ||
.arg( | ||
Arg::with_name("keypair") | ||
.long("keypair") | ||
.short("k") | ||
.takes_value(true) | ||
.value_name("KEYPAIR") | ||
.help("Filepath or URL to a keypair"), | ||
) | ||
.arg( | ||
Arg::with_name("url") | ||
.long("url") | ||
.short("u") | ||
.takes_value(true) | ||
.value_name("URL_OR_MONIKER") | ||
.help("URL for JSON RPC Server"), | ||
) | ||
.get_matches_from(args); | ||
let config = matches.value_of("config"); | ||
let keypair = matches.value_of("keypair").unwrap(); | ||
let url = matches.value_of("url"); | ||
let connection = client::establish_connection(&url, &config).unwrap(); | ||
println!( | ||
"Connected to Simulation server running version ({}).", | ||
connection.get_version().unwrap() | ||
); | ||
let player = utils::get_player(&config).unwrap(); | ||
let program = client::get_program(keypair, &connection).unwrap(); | ||
client::say_hello(&player, &program, &connection).unwrap(); | ||
} |
Oops, something went wrong.