Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
2798271da0 | |||
dc258cebab | |||
4b8c5194c7 | |||
3c7c6dacfb | |||
e36337a764 | |||
a49856b898 | |||
8ca2f52041 | |||
2f7f243022 | |||
7e443770d7 | |||
8ec09884b8 | |||
88c7e636d6 | |||
add3fd479d | |||
70410536b9 | |||
6f3f9b485c | |||
bd9ce3590d |
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -574,9 +574,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono-humanize"
|
||||
version = "0.1.1"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0a4c32145b4db85fe1c4f2b125a4f9493769df424f5f84baf6b04ea8eaf33c9"
|
||||
checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
]
|
||||
@ -655,6 +655,21 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"regex",
|
||||
"terminal_size",
|
||||
"unicode-width",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.5"
|
||||
@ -979,7 +994,7 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aa86af7b19b40ef9cbef761ed411a49f0afa06b7b6dcd3dfe2f96a3c546138"
|
||||
dependencies = [
|
||||
"console",
|
||||
"console 0.11.3",
|
||||
"lazy_static",
|
||||
"tempfile",
|
||||
]
|
||||
@ -1924,7 +1939,7 @@ version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4"
|
||||
dependencies = [
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"lazy_static",
|
||||
"number_prefix",
|
||||
"regex",
|
||||
@ -3651,9 +3666,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.4"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf487fbf5c6239d7ea2ff8b10cb6b811cd4b5080d1c2aeed1dec18753c06e10"
|
||||
checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -4161,7 +4176,7 @@ dependencies = [
|
||||
"bs58",
|
||||
"chrono",
|
||||
"clap",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"criterion-stats",
|
||||
"ctrlc",
|
||||
"dirs-next",
|
||||
@ -4218,7 +4233,7 @@ dependencies = [
|
||||
"Inflector",
|
||||
"base64 0.13.0",
|
||||
"chrono",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"humantime 2.0.1",
|
||||
"indicatif",
|
||||
"serde",
|
||||
@ -4415,7 +4430,7 @@ name = "solana-download-utils"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"bzip2",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"indicatif",
|
||||
"log 0.4.11",
|
||||
"reqwest",
|
||||
@ -4615,7 +4630,7 @@ dependencies = [
|
||||
"bzip2",
|
||||
"chrono",
|
||||
"clap",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"ctrlc",
|
||||
"dirs-next",
|
||||
"indicatif",
|
||||
@ -5061,7 +5076,7 @@ name = "solana-remote-wallet"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"base32",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"dialoguer",
|
||||
"hidapi",
|
||||
"log 0.4.11",
|
||||
@ -5268,7 +5283,7 @@ name = "solana-stake-monitor"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"log 0.4.11",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
@ -5400,7 +5415,7 @@ dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
"clap",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"csv",
|
||||
"ctrlc",
|
||||
"dirs-next",
|
||||
@ -5466,7 +5481,7 @@ dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
"clap",
|
||||
"console",
|
||||
"console 0.14.1",
|
||||
"core_affinity",
|
||||
"fd-lock",
|
||||
"indicatif",
|
||||
@ -5836,9 +5851,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.12"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8038f95fc7a6f351163f4b964af631bd26c9e828f7db085f2a84aca56f70d13b"
|
||||
checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.8",
|
||||
|
@ -24,9 +24,11 @@ use {
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
convert::TryFrom,
|
||||
error,
|
||||
io::{stdin, stdout, Write},
|
||||
ops::Deref,
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
@ -89,33 +91,49 @@ impl CliSignerInfo {
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DefaultSigner {
|
||||
pub arg_name: String,
|
||||
pub path: String,
|
||||
is_path_checked: RefCell<bool>,
|
||||
}
|
||||
|
||||
impl DefaultSigner {
|
||||
pub fn new(path: String) -> Self {
|
||||
pub fn new<AN: AsRef<str>, P: AsRef<str>>(arg_name: AN, path: P) -> Self {
|
||||
let arg_name = arg_name.as_ref().to_string();
|
||||
let path = path.as_ref().to_string();
|
||||
Self {
|
||||
arg_name: "keypair".to_string(),
|
||||
arg_name,
|
||||
path,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
pub fn from_path(path: String) -> Result<Self, Box<dyn error::Error>> {
|
||||
std::fs::metadata(&path)
|
||||
.map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
|
||||
path
|
||||
),
|
||||
)
|
||||
.into()
|
||||
})
|
||||
.map(|_| Self::new(path))
|
||||
|
||||
fn path(&self) -> Result<&str, Box<dyn std::error::Error>> {
|
||||
if !self.is_path_checked.borrow().deref() {
|
||||
parse_signer_source(&self.path)
|
||||
.and_then(|s| {
|
||||
if let SignerSourceKind::Filepath(path) = &s.kind {
|
||||
std::fs::metadata(path).map(|_| ()).map_err(|e| e.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
|
||||
self.path
|
||||
),
|
||||
)
|
||||
})?;
|
||||
*self.is_path_checked.borrow_mut() = true;
|
||||
}
|
||||
Ok(&self.path)
|
||||
}
|
||||
|
||||
pub fn generate_unique_signers(
|
||||
&self,
|
||||
bulk_signers: Vec<Option<Box<dyn Signer>>>,
|
||||
@ -145,7 +163,7 @@ impl DefaultSigner {
|
||||
matches: &ArgMatches,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
|
||||
signer_from_path(matches, self.path()?, &self.arg_name, wallet_manager)
|
||||
}
|
||||
|
||||
pub fn signer_from_path_with_config(
|
||||
@ -154,7 +172,13 @@ impl DefaultSigner {
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
config: &SignerFromPathConfig,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path_with_config(matches, &self.path, &self.arg_name, wallet_manager, config)
|
||||
signer_from_path_with_config(
|
||||
matches,
|
||||
self.path()?,
|
||||
&self.arg_name,
|
||||
wallet_manager,
|
||||
config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +301,9 @@ pub(crate) fn parse_signer_source<S: AsRef<str>>(
|
||||
ASK_KEYWORD => Ok(SignerSource::new_legacy(SignerSourceKind::Prompt)),
|
||||
_ => match Pubkey::from_str(source.as_str()) {
|
||||
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
|
||||
Err(_) => Ok(SignerSource::new(SignerSourceKind::Filepath(source))),
|
||||
Err(_) => std::fs::metadata(source.as_str())
|
||||
.map(|_| SignerSource::new(SignerSourceKind::Filepath(source)))
|
||||
.map_err(|err| err.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -751,6 +777,10 @@ mod tests {
|
||||
// Catchall into SignerSource::Filepath fails
|
||||
let junk = "sometextthatisnotapubkeyorfile".to_string();
|
||||
assert!(Pubkey::from_str(&junk).is_err());
|
||||
assert!(matches!(
|
||||
parse_signer_source(&junk),
|
||||
Err(SignerSourceError::IoError(_))
|
||||
));
|
||||
|
||||
let prompt = "prompt:".to_string();
|
||||
assert!(matches!(
|
||||
|
@ -12,7 +12,7 @@ documentation = "https://docs.rs/solana-cli-output"
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
humantime = "2.0.1"
|
||||
Inflector = "0.11.4"
|
||||
indicatif = "0.15.0"
|
||||
|
@ -16,7 +16,7 @@ chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = "2.33.1"
|
||||
criterion-stats = "0.3.0"
|
||||
ctrlc = { version = "3.1.5", features = ["termination"] }
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
dirs-next = "2.0.0"
|
||||
log = "0.4.11"
|
||||
Inflector = "0.11.4"
|
||||
|
@ -2299,7 +2299,7 @@ mod tests {
|
||||
let default_keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
|
||||
|
||||
let default_signer = DefaultSigner::new(default_keypair_file);
|
||||
let default_signer = DefaultSigner::new("keypair", &default_keypair_file);
|
||||
|
||||
let signer_info = default_signer
|
||||
.generate_unique_signers(vec![], &matches, &mut None)
|
||||
@ -2377,7 +2377,7 @@ mod tests {
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let keypair = read_keypair_file(&keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
// Test Airdrop Subcommand
|
||||
let test_airdrop =
|
||||
test_commands
|
||||
@ -2905,7 +2905,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let default_keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(default_keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
|
||||
//Test Transfer Subcommand, SOL
|
||||
let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
|
@ -2098,7 +2098,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new(default_keypair_file);
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
|
||||
let test_cluster_version = test_commands
|
||||
.clone()
|
||||
|
@ -173,12 +173,13 @@ pub fn parse_args<'a>(
|
||||
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||
&config.json_rpc_url,
|
||||
);
|
||||
let default_signer_arg_name = "keypair".to_string();
|
||||
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting(
|
||||
matches.value_of("keypair").unwrap_or(""),
|
||||
matches.value_of(&default_signer_arg_name).unwrap_or(""),
|
||||
&config.keypair_path,
|
||||
);
|
||||
|
||||
let default_signer = DefaultSigner::from_path(default_signer_path.clone())?;
|
||||
let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path);
|
||||
|
||||
let CliCommandInfo {
|
||||
command,
|
||||
|
@ -596,7 +596,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new(default_keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let nonce_account_keypair = Keypair::new();
|
||||
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
@ -2131,7 +2131,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@ -2339,7 +2339,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
|
||||
// defaults
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
@ -2487,7 +2487,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
|
||||
let program_pubkey = Pubkey::new_unique();
|
||||
let new_authority_pubkey = Pubkey::new_unique();
|
||||
@ -2595,7 +2595,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
let new_authority_pubkey = Pubkey::new_unique();
|
||||
@ -2652,7 +2652,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(keypair_file);
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
|
||||
// defaults
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
@ -2751,7 +2751,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new(keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
|
||||
// defaults
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
|
@ -2117,7 +2117,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new(default_keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let stake_account_keypair = Keypair::new();
|
||||
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
@ -826,7 +826,7 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new(default_keypair_file.clone());
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
|
||||
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
|
@ -60,7 +60,7 @@ the latest recommended settings are applied.
|
||||
To run it:
|
||||
|
||||
```bash
|
||||
sudo solana-sys-tuner --user $(whoami) > sys-tuner.log 2>&1 &
|
||||
sudo $(command -v solana-sys-tuner) --user $(whoami) > sys-tuner.log 2>&1 &
|
||||
```
|
||||
|
||||
#### Manual
|
||||
|
@ -11,7 +11,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bzip2 = "0.3.3"
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
indicatif = "0.15.0"
|
||||
log = "0.4.11"
|
||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
|
@ -15,7 +15,7 @@ bincode = "1.3.1"
|
||||
bzip2 = "0.3.3"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = { version = "2.33.1" }
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
ctrlc = { version = "3.1.5", features = ["termination"] }
|
||||
dirs-next = "2.0.0"
|
||||
indicatif = "0.15.0"
|
||||
|
@ -13,7 +13,7 @@ edition = "2018"
|
||||
bincode = "1.3.1"
|
||||
byteorder = "1.3.4"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
chrono-humanize = "0.1.1"
|
||||
chrono-humanize = "0.2.1"
|
||||
crossbeam-channel = "0.4"
|
||||
dlopen_derive = "0.1.4"
|
||||
dlopen = "0.1.8"
|
||||
@ -32,7 +32,7 @@ rand_chacha = "0.2.2"
|
||||
rayon = "1.5.0"
|
||||
reed-solomon-erasure = { version = "4.0.2", features = ["simd-accel"] }
|
||||
serde = "1.0.122"
|
||||
serde_bytes = "0.11.4"
|
||||
serde_bytes = "0.11.5"
|
||||
sha2 = "0.9.2"
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.0" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.0" }
|
||||
|
@ -165,6 +165,10 @@ impl Blockstore {
|
||||
& self
|
||||
.db
|
||||
.delete_range_cf::<cf::PerfSamples>(&mut write_batch, from_slot, to_slot)
|
||||
.is_ok()
|
||||
& self
|
||||
.db
|
||||
.delete_range_cf::<cf::BlockHeight>(&mut write_batch, from_slot, to_slot)
|
||||
.is_ok();
|
||||
let mut w_active_transaction_status_index =
|
||||
self.active_transaction_status_index.write().unwrap();
|
||||
@ -263,6 +267,10 @@ impl Blockstore {
|
||||
&& self
|
||||
.perf_samples_cf
|
||||
.compact_range(from_slot, to_slot)
|
||||
.unwrap_or(false)
|
||||
&& self
|
||||
.block_height_cf
|
||||
.compact_range(from_slot, to_slot)
|
||||
.unwrap_or(false);
|
||||
compact_timer.stop();
|
||||
if !result {
|
||||
|
@ -267,6 +267,8 @@ impl Rocks {
|
||||
ColumnFamilyDescriptor::new(PerfSamples::NAME, get_cf_options(&access_type));
|
||||
let block_height_cf_descriptor =
|
||||
ColumnFamilyDescriptor::new(BlockHeight::NAME, get_cf_options(&access_type));
|
||||
// Don't forget to add to both run_purge_with_stats() and
|
||||
// compact_storage() in ledger/src/blockstore/blockstore_purge.rs!!
|
||||
|
||||
let cfs = vec![
|
||||
(SlotMeta::NAME, meta_cf_descriptor),
|
||||
@ -363,7 +365,7 @@ impl Rocks {
|
||||
}
|
||||
|
||||
fn get_cf(&self, cf: &ColumnFamily, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||
let opt = self.0.get_cf(cf, key)?.map(|db_vec| db_vec.to_vec());
|
||||
let opt = self.0.get_cf(cf, key)?;
|
||||
Ok(opt)
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,10 @@ Operate a configured testnet
|
||||
--cluster-type development|devnet|testnet|mainnet-beta
|
||||
- Specify whether or not to launch the cluster in "development" mode with all features enabled at epoch 0,
|
||||
or various other live clusters' feature set (default: development)
|
||||
--warp-slot WARP_SLOT - Boot from a snapshot that has warped ahead to WARP_SLOT rather than a slot 0 genesis.
|
||||
--slots-per-epoch SLOTS
|
||||
- Override the number of slots in an epoch
|
||||
--warp-slot WARP_SLOT
|
||||
- Boot from a snapshot that has warped ahead to WARP_SLOT rather than a slot 0 genesis.
|
||||
sanity/start-specific options:
|
||||
-F - Discard validator nodes that didn't bootup successfully
|
||||
-o noInstallCheck - Skip solana-install sanity
|
||||
@ -822,6 +825,9 @@ while [[ -n $1 ]]; do
|
||||
esac
|
||||
genesisOptions="$genesisOptions $1 $2"
|
||||
shift 2
|
||||
elif [[ $1 = --slots-per-epoch ]]; then
|
||||
genesisOptions="$genesisOptions $1 $2"
|
||||
shift 2
|
||||
elif [[ $1 = --no-snapshot-fetch ]]; then
|
||||
maybeNoSnapshot="$1"
|
||||
shift 1
|
||||
|
@ -12,7 +12,7 @@ async-trait = "0.1.42"
|
||||
base64 = "0.12.3"
|
||||
bincode = "1.3.1"
|
||||
chrono = "0.4.19"
|
||||
chrono-humanize = "0.1.1"
|
||||
chrono-humanize = "0.2.1"
|
||||
log = "0.4.11"
|
||||
mio = "0.7.6"
|
||||
serde = "1.0.112"
|
||||
|
62
programs/bpf/Cargo.lock
generated
62
programs/bpf/Cargo.lock
generated
@ -381,9 +381,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono-humanize"
|
||||
version = "0.1.2"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8164ae3089baf04ff71f32aeb70213283dcd236dce8bc976d00b17a458f5f71c"
|
||||
checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
]
|
||||
@ -458,6 +458,21 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"regex",
|
||||
"terminal_size",
|
||||
"unicode-width",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.5"
|
||||
@ -2282,21 +2297,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.9"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
|
||||
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.18"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
@ -2536,9 +2550,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.4"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf487fbf5c6239d7ea2ff8b10cb6b811cd4b5080d1c2aeed1dec18753c06e10"
|
||||
checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -2953,6 +2967,16 @@ name = "solana-bpf-rust-mem"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"solana-program 1.7.0",
|
||||
"solana-program-test",
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-membuiltins"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"solana-bpf-rust-mem",
|
||||
"solana-program 1.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2993,6 +3017,13 @@ dependencies = [
|
||||
"solana-program 1.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-ro-account_modify"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"solana-program 1.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-ro-modify"
|
||||
version = "1.7.0"
|
||||
@ -3085,7 +3116,7 @@ dependencies = [
|
||||
"Inflector",
|
||||
"base64 0.13.0",
|
||||
"chrono",
|
||||
"console 0.11.3",
|
||||
"console 0.14.1",
|
||||
"humantime",
|
||||
"indicatif",
|
||||
"serde",
|
||||
@ -3412,7 +3443,7 @@ name = "solana-remote-wallet"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"base32",
|
||||
"console 0.11.3",
|
||||
"console 0.14.1",
|
||||
"dialoguer",
|
||||
"hidapi",
|
||||
"log",
|
||||
@ -3878,15 +3909,6 @@ dependencies = [
|
||||
"syn 1.0.67",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.43"
|
||||
|
@ -64,12 +64,14 @@ members = [
|
||||
"rust/many_args",
|
||||
"rust/many_args_dep",
|
||||
"rust/mem",
|
||||
"rust/membuiltins",
|
||||
"rust/noop",
|
||||
"rust/panic",
|
||||
"rust/param_passing",
|
||||
"rust/param_passing_dep",
|
||||
"rust/rand",
|
||||
"rust/ro_modify",
|
||||
"rust/ro_account_modify",
|
||||
"rust/sanity",
|
||||
"rust/sha",
|
||||
"rust/spoof1",
|
||||
|
@ -78,11 +78,13 @@ fn main() {
|
||||
"iter",
|
||||
"many_args",
|
||||
"mem",
|
||||
"membuiltins",
|
||||
"noop",
|
||||
"panic",
|
||||
"param_passing",
|
||||
"rand",
|
||||
"ro_modify",
|
||||
"ro_account_modify",
|
||||
"sanity",
|
||||
"sha",
|
||||
"spoof1",
|
||||
|
@ -17,6 +17,7 @@ static const uint8_t TEST_INSTRUCTION_META_TOO_LARGE = 10;
|
||||
static const uint8_t TEST_RETURN_ERROR = 11;
|
||||
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 12;
|
||||
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 13;
|
||||
static const uint8_t TEST_WRITABLE_DEESCALATION_WRITABLE = 14;
|
||||
|
||||
static const int MINT_INDEX = 0;
|
||||
static const int ARGUMENT_INDEX = 1;
|
||||
@ -271,24 +272,6 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_assert(accounts[ARGUMENT_INDEX].data[i] == 0);
|
||||
}
|
||||
}
|
||||
sol_log("Test writable deescalation");
|
||||
{
|
||||
uint8_t buffer[10];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
buffer[i] = accounts[INVOKED_ARGUMENT_INDEX].data[i];
|
||||
}
|
||||
SolAccountMeta arguments[] = {
|
||||
{accounts[INVOKED_ARGUMENT_INDEX].key, false, false}};
|
||||
uint8_t data[] = {WRITE_ACCOUNT, 10};
|
||||
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
||||
arguments, SOL_ARRAY_SIZE(arguments),
|
||||
data, SOL_ARRAY_SIZE(data)};
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
sol_assert(buffer[i] == accounts[INVOKED_ARGUMENT_INDEX].data[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_PRIVILEGE_ESCALATION_SIGNER: {
|
||||
@ -521,6 +504,25 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||
break;
|
||||
}
|
||||
case TEST_WRITABLE_DEESCALATION_WRITABLE: {
|
||||
sol_log("Test writable deescalation");
|
||||
uint8_t buffer[10];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
buffer[i] = accounts[INVOKED_ARGUMENT_INDEX].data[i];
|
||||
}
|
||||
SolAccountMeta arguments[] = {
|
||||
{accounts[INVOKED_ARGUMENT_INDEX].key, false, false}};
|
||||
uint8_t data[] = {WRITE_ACCOUNT, 10};
|
||||
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
||||
arguments, SOL_ARRAY_SIZE(arguments),
|
||||
data, SOL_ARRAY_SIZE(data)};
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
sol_assert(buffer[i] == accounts[INVOKED_ARGUMENT_INDEX].data[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sol_panic();
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10;
|
||||
const TEST_RETURN_ERROR: u8 = 11;
|
||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
|
||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
|
||||
const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14;
|
||||
|
||||
// const MINT_INDEX: usize = 0;
|
||||
const ARGUMENT_INDEX: usize = 1;
|
||||
@ -354,27 +355,6 @@ fn process_instruction(
|
||||
}
|
||||
}
|
||||
|
||||
msg!("Test writable deescalation");
|
||||
{
|
||||
const NUM_BYTES: usize = 10;
|
||||
let mut buffer = [0; NUM_BYTES];
|
||||
buffer.copy_from_slice(
|
||||
&accounts[INVOKED_ARGUMENT_INDEX].data.borrow_mut()[..NUM_BYTES],
|
||||
);
|
||||
|
||||
let instruction = create_instruction(
|
||||
*accounts[INVOKED_PROGRAM_INDEX].key,
|
||||
&[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)],
|
||||
vec![WRITE_ACCOUNT, NUM_BYTES as u8],
|
||||
);
|
||||
let _ = invoke(&instruction, accounts);
|
||||
|
||||
assert_eq!(
|
||||
buffer,
|
||||
accounts[INVOKED_ARGUMENT_INDEX].data.borrow_mut()[..NUM_BYTES]
|
||||
);
|
||||
}
|
||||
|
||||
msg!("Create account and init data");
|
||||
{
|
||||
let from_lamports = accounts[FROM_INDEX].lamports();
|
||||
@ -603,6 +583,25 @@ fn process_instruction(
|
||||
);
|
||||
invoke(&invoked_instruction, accounts)?;
|
||||
}
|
||||
TEST_WRITABLE_DEESCALATION_WRITABLE => {
|
||||
msg!("Test writable deescalation writable");
|
||||
const NUM_BYTES: usize = 10;
|
||||
let mut buffer = [0; NUM_BYTES];
|
||||
buffer
|
||||
.copy_from_slice(&accounts[INVOKED_ARGUMENT_INDEX].data.borrow_mut()[..NUM_BYTES]);
|
||||
|
||||
let instruction = create_instruction(
|
||||
*accounts[INVOKED_PROGRAM_INDEX].key,
|
||||
&[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)],
|
||||
vec![WRITE_ACCOUNT, NUM_BYTES as u8],
|
||||
);
|
||||
let _ = invoke(&instruction, accounts);
|
||||
|
||||
assert_eq!(
|
||||
buffer,
|
||||
accounts[INVOKED_ARGUMENT_INDEX].data.borrow_mut()[..NUM_BYTES]
|
||||
);
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,18 @@ homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-bpf-rust-mem"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = { path = "../../../../sdk/program", version = "=1.7.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = { path = "../../../../program-test", version = "=1.7.0" }
|
||||
solana-sdk = { path = "../../../../sdk", version = "=1.7.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
39
programs/bpf/rust/mem/src/entrypoint.rs
Normal file
39
programs/bpf/rust/mem/src/entrypoint.rs
Normal file
@ -0,0 +1,39 @@
|
||||
//! @brief Test mem functions
|
||||
|
||||
use crate::{run_mem_tests, MemOps};
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
entrypoint,
|
||||
entrypoint::ProgramResult,
|
||||
program_memory::{sol_memcmp, sol_memcpy, sol_memmove, sol_memset},
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
pub fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_accounts: &[AccountInfo],
|
||||
_instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
// Via syscalls
|
||||
#[derive(Default)]
|
||||
struct MemOpSyscalls();
|
||||
impl MemOps for MemOpSyscalls {
|
||||
fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize) {
|
||||
sol_memcpy(dst, src, n)
|
||||
}
|
||||
unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize) {
|
||||
sol_memmove(dst, src, n)
|
||||
}
|
||||
fn memset(&self, s: &mut [u8], c: u8, n: usize) {
|
||||
sol_memset(s, c, n)
|
||||
}
|
||||
fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32 {
|
||||
sol_memcmp(s1, s2, n)
|
||||
}
|
||||
}
|
||||
run_mem_tests(MemOpSyscalls::default());
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,190 +1,165 @@
|
||||
//! @brief Test builtin mem functions
|
||||
//! @brief Test mem functions
|
||||
|
||||
#![cfg(target_arch = "bpf")]
|
||||
#![feature(rustc_private)]
|
||||
#[cfg(not(feature = "no-entrypoint"))]
|
||||
pub mod entrypoint;
|
||||
|
||||
extern crate compiler_builtins;
|
||||
use solana_program::{custom_panic_default, entrypoint::SUCCESS};
|
||||
pub trait MemOps {
|
||||
fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize);
|
||||
/// # Safety
|
||||
unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize);
|
||||
fn memset(&self, s: &mut [u8], c: u8, n: usize);
|
||||
fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
pub fn run_mem_tests<T: MemOps>(mem_ops: T) {
|
||||
// memcpy
|
||||
let src = &[1_u8; 18];
|
||||
let dst = &mut [0_u8; 1];
|
||||
mem_ops.memcpy(dst, src, 1);
|
||||
assert_eq!(&src[..1], dst);
|
||||
let dst = &mut [0_u8; 3];
|
||||
mem_ops.memcpy(dst, src, 3);
|
||||
assert_eq!(&src[..3], dst);
|
||||
let dst = &mut [0_u8; 8];
|
||||
mem_ops.memcpy(dst, src, 8);
|
||||
assert_eq!(&src[..8], dst);
|
||||
let dst = &mut [0_u8; 9];
|
||||
mem_ops.memcpy(dst, src, 9);
|
||||
assert_eq!(&src[..9], dst);
|
||||
let dst = &mut [0_u8; 16];
|
||||
mem_ops.memcpy(dst, src, 16);
|
||||
assert_eq!(&src[..16], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
mem_ops.memcpy(dst, src, 18);
|
||||
assert_eq!(&src[..18], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
mem_ops.memcpy(dst, &src[1..], 17);
|
||||
assert_eq!(&src[1..], &dst[..17]);
|
||||
let dst = &mut [0_u8; 18];
|
||||
mem_ops.memcpy(&mut dst[1..], &src[1..], 17);
|
||||
assert_eq!(&src[1..], &dst[1..]);
|
||||
|
||||
// memmove
|
||||
unsafe {
|
||||
// memcpy
|
||||
let src = &mut [1_u8; 18];
|
||||
let dst = &mut [0_u8; 1];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 1);
|
||||
assert_eq!(&src[..1], dst);
|
||||
let dst = &mut [0_u8; 3];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 3);
|
||||
assert_eq!(&src[..3], dst);
|
||||
let dst = &mut [0_u8; 8];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 8);
|
||||
assert_eq!(&src[..8], dst);
|
||||
let dst = &mut [0_u8; 9];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 9);
|
||||
assert_eq!(&src[..9], dst);
|
||||
let dst = &mut [0_u8; 16];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 16);
|
||||
assert_eq!(&src[..16], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 18);
|
||||
assert_eq!(&src[..18], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memcpy(&mut src[1] as *mut u8, &mut dst[0] as *mut u8, 17);
|
||||
assert_eq!(&src[1..], &dst[1..]);
|
||||
let dst = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memcpy(&mut src[1] as *mut u8, &mut dst[1] as *mut u8, 17);
|
||||
assert_eq!(&src[1..], &dst[..17]);
|
||||
|
||||
// memmove
|
||||
let buf = &mut [1_u8, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[1] as *mut u8, 1);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[1] as *mut u8, 1);
|
||||
assert_eq!(buf[0], buf[1]);
|
||||
let buf = &mut [1_u8, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 1);
|
||||
mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 1);
|
||||
assert_eq!(buf[0], buf[1]);
|
||||
let buf = &mut [1_u8, 1, 1, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 3);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 3);
|
||||
assert_eq!(buf[..3], buf[3..]);
|
||||
let buf = &mut [1_u8, 1, 1, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[3] as *mut u8, &mut buf[0] as *mut u8, 3);
|
||||
mem_ops.memmove(&mut buf[3] as *mut u8, &mut buf[0] as *mut u8, 3);
|
||||
assert_eq!(buf[..3], buf[3..]);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[8] as *mut u8, 8);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[8] as *mut u8, 8);
|
||||
assert_eq!(buf[..8], buf[8..]);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[8] as *mut u8, &mut buf[0] as *mut u8, 8);
|
||||
mem_ops.memmove(&mut buf[8] as *mut u8, &mut buf[0] as *mut u8, 8);
|
||||
assert_eq!(buf[..8], buf[8..]);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[9] as *mut u8, 9);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[9] as *mut u8, 9);
|
||||
assert_eq!(buf[..9], buf[9..]);
|
||||
let buf = &mut [0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
assert_eq!(&mut [0_u8, 0, 1, 2, 3, 4, 5, 6, 7, 8], buf);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[9] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
mem_ops.memmove(&mut buf[9] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
assert_eq!(buf[..9], buf[9..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[16] as *mut u8, 16);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[16] as *mut u8, 16);
|
||||
assert_eq!(buf[..16], buf[16..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[16] as *mut u8, &mut buf[0] as *mut u8, 16);
|
||||
mem_ops.memmove(&mut buf[16] as *mut u8, &mut buf[0] as *mut u8, 16);
|
||||
assert_eq!(buf[..16], buf[16..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[18] as *mut u8, 18);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[18] as *mut u8, 18);
|
||||
assert_eq!(buf[..18], buf[18..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[18] as *mut u8, &mut buf[0] as *mut u8, 18);
|
||||
mem_ops.memmove(&mut buf[18] as *mut u8, &mut buf[0] as *mut u8, 18);
|
||||
assert_eq!(buf[..18], buf[18..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[18] as *mut u8, 17);
|
||||
mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[18] as *mut u8, 17);
|
||||
assert_eq!(buf[1..17], buf[18..34]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[19] as *mut u8, &mut buf[1] as *mut u8, 17);
|
||||
mem_ops.memmove(&mut buf[19] as *mut u8, &mut buf[1] as *mut u8, 17);
|
||||
assert_eq!(buf[..17], buf[19..]);
|
||||
|
||||
// memset
|
||||
let exp = &[1_u8; 18];
|
||||
let buf = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 1);
|
||||
assert_eq!(exp[..1], buf[..1]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 3);
|
||||
assert_eq!(exp[..3], buf[..3]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 8);
|
||||
assert_eq!(exp[..8], buf[..8]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 9);
|
||||
assert_eq!(exp[..9], buf[..9]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 16);
|
||||
assert_eq!(exp[..16], buf[..16]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 18);
|
||||
assert_eq!(exp[..18], buf[..18]);
|
||||
compiler_builtins::mem::memset(&mut buf[1] as *mut u8, 1, 17);
|
||||
assert_eq!(exp[1..18], buf[1..18]);
|
||||
|
||||
// memcmp
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8] as *const u8, &[1_u8] as *const u8, 1)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 1] as *const u8,
|
||||
3
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
0,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 1] as *const u8,
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 1] as *const u8,
|
||||
10
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
0,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 8] as *const u8, &[0_u8; 8] as *const u8, 8)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 8] as *const u8, &[1_u8; 8] as *const u8, 8)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 16] as *const u8, &[1_u8; 16] as *const u8, 16)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 18] as *const u8, &[1_u8; 18] as *const u8, 18)
|
||||
);
|
||||
let one = &[0_u8; 18];
|
||||
let two = &[1_u8; 18];
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&one[1] as *const u8, &two[0] as *const u8, 17)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&one[1] as *const u8, &two[1] as *const u8, 17)
|
||||
);
|
||||
let buf = &mut [0_u8, 0, 0, 1, 1, 1, 1, 1, 0];
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 5);
|
||||
assert_eq!(buf, &mut [1, 1, 1, 1, 1, 1, 1, 1, 0]);
|
||||
}
|
||||
|
||||
SUCCESS
|
||||
}
|
||||
// memset
|
||||
let exp = &[1_u8; 18];
|
||||
let buf = &mut [0_u8; 18];
|
||||
mem_ops.memset(&mut buf[0..], 1, 1);
|
||||
assert_eq!(exp[..1], buf[..1]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 3);
|
||||
assert_eq!(exp[..3], buf[..3]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 8);
|
||||
assert_eq!(exp[..8], buf[..8]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 9);
|
||||
assert_eq!(exp[..9], buf[..9]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 16);
|
||||
assert_eq!(exp[..16], buf[..16]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 18);
|
||||
assert_eq!(exp[..18], buf[..18]);
|
||||
mem_ops.memset(&mut buf[1..], 1, 17);
|
||||
assert_eq!(exp[1..18], buf[1..18]);
|
||||
|
||||
custom_panic_default!();
|
||||
// memcmp
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8], &[1_u8], 1));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8, 0, 0], &[0_u8, 0, 1], 3));
|
||||
assert_eq!(
|
||||
0,
|
||||
mem_ops.memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
mem_ops.memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
mem_ops.memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
10
|
||||
)
|
||||
);
|
||||
assert_eq!(0, mem_ops.memcmp(&[0_u8; 8], &[0_u8; 8], 8));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8; 8], &[1_u8; 8], 8));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8; 16], &[1_u8; 16], 16));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8; 18], &[1_u8; 18], 18));
|
||||
let one = &[0_u8; 18];
|
||||
let two = &[1_u8; 18];
|
||||
assert_eq!(-1, mem_ops.memcmp(&one[1..], &two[0..], 17));
|
||||
assert_eq!(-1, mem_ops.memcmp(&one[1..], &two[1..], 17));
|
||||
}
|
||||
|
23
programs/bpf/rust/mem/tests/lib.rs
Normal file
23
programs/bpf/rust/mem/tests/lib.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use solana_bpf_rust_mem::entrypoint::process_instruction;
|
||||
use solana_program_test::*;
|
||||
use solana_sdk::{
|
||||
instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mem() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let program_test = ProgramTest::new(
|
||||
"solana_bpf_rust_mem",
|
||||
program_id,
|
||||
processor!(process_instruction),
|
||||
);
|
||||
let (mut banks_client, payer, recent_blockhash) = program_test.start().await;
|
||||
|
||||
let mut transaction = Transaction::new_with_payer(
|
||||
&[Instruction::new_with_bincode(program_id, &(), vec![])],
|
||||
Some(&payer.pubkey()),
|
||||
);
|
||||
transaction.sign(&[&payer], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
}
|
20
programs/bpf/rust/membuiltins/Cargo.toml
Normal file
20
programs/bpf/rust/membuiltins/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "solana-bpf-rust-membuiltins"
|
||||
version = "1.7.0"
|
||||
description = "Solana BPF test program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-bpf-rust-mem"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-bpf-rust-mem = { path = "../mem", version = "=1.7.0", features = [ "no-entrypoint" ] }
|
||||
solana-program = { path = "../../../../sdk/program", version = "=1.7.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
39
programs/bpf/rust/membuiltins/src/lib.rs
Normal file
39
programs/bpf/rust/membuiltins/src/lib.rs
Normal file
@ -0,0 +1,39 @@
|
||||
//! @brief Test builtin mem functions
|
||||
|
||||
#![cfg(target_arch = "bpf")]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate compiler_builtins;
|
||||
use solana_bpf_rust_mem::{run_mem_tests, MemOps};
|
||||
use solana_program::{custom_panic_default, entrypoint::SUCCESS};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
#[derive(Default)]
|
||||
struct MemOpSyscalls();
|
||||
impl MemOps for MemOpSyscalls {
|
||||
fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize) {
|
||||
unsafe {
|
||||
compiler_builtins::mem::memcpy(dst.as_mut_ptr(), src.as_ptr(), n);
|
||||
}
|
||||
}
|
||||
unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize) {
|
||||
compiler_builtins::mem::memmove(dst, src, n);
|
||||
}
|
||||
fn memset(&self, s: &mut [u8], c: u8, n: usize) {
|
||||
unsafe {
|
||||
compiler_builtins::mem::memset(s.as_mut_ptr(), c as i32, n);
|
||||
}
|
||||
}
|
||||
fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32 {
|
||||
unsafe { compiler_builtins::mem::memcmp(s1.as_ptr(), s2.as_ptr(), n) }
|
||||
}
|
||||
}
|
||||
let mem_ops = MemOpSyscalls::default();
|
||||
|
||||
run_mem_tests(mem_ops);
|
||||
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
custom_panic_default!();
|
19
programs/bpf/rust/ro_account_modify/Cargo.toml
Normal file
19
programs/bpf/rust/ro_account_modify/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "solana-bpf-rust-ro-account_modify"
|
||||
version = "1.7.0"
|
||||
description = "Solana BPF test program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-bpf-rust-ro-modify"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-program = { path = "../../../../sdk/program", version = "=1.7.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
70
programs/bpf/rust/ro_account_modify/src/lib.rs
Normal file
70
programs/bpf/rust/ro_account_modify/src/lib.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
entrypoint,
|
||||
entrypoint::ProgramResult,
|
||||
instruction::{AccountMeta, Instruction},
|
||||
msg,
|
||||
program::invoke,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
const ARGUMENT_INDEX: usize = 0;
|
||||
|
||||
const INSTRUCTION_MODIFY: u8 = 0;
|
||||
const INSTRUCTION_INVOKE_MODIFY: u8 = 1;
|
||||
const INSTRUCTION_MODIFY_INVOKE: u8 = 2;
|
||||
const INSTRUCTION_VERIFY_MODIFIED: u8 = 3;
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
fn process_instruction(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
assert!(!accounts[ARGUMENT_INDEX].is_writable);
|
||||
|
||||
match instruction_data[0] {
|
||||
INSTRUCTION_MODIFY => {
|
||||
msg!("modify ro account");
|
||||
assert_eq!(0, accounts[ARGUMENT_INDEX].try_borrow_data()?[0]);
|
||||
accounts[ARGUMENT_INDEX].try_borrow_mut_data()?[0] = 1;
|
||||
}
|
||||
INSTRUCTION_INVOKE_MODIFY => {
|
||||
msg!("invoke and modify ro account");
|
||||
|
||||
assert_eq!(0, accounts[ARGUMENT_INDEX].try_borrow_data()?[0]);
|
||||
|
||||
let instruction = Instruction {
|
||||
program_id: *program_id,
|
||||
accounts: vec![AccountMeta::new_readonly(
|
||||
*accounts[ARGUMENT_INDEX].key,
|
||||
false,
|
||||
)],
|
||||
data: vec![INSTRUCTION_MODIFY],
|
||||
};
|
||||
invoke(&instruction, accounts)?;
|
||||
}
|
||||
INSTRUCTION_MODIFY_INVOKE => {
|
||||
msg!("modify and invoke ro account");
|
||||
|
||||
assert_eq!(0, accounts[ARGUMENT_INDEX].try_borrow_data()?[0]);
|
||||
accounts[ARGUMENT_INDEX].try_borrow_mut_data()?[0] = 1;
|
||||
|
||||
let instruction = Instruction {
|
||||
program_id: *program_id,
|
||||
accounts: vec![AccountMeta::new_readonly(
|
||||
*accounts[ARGUMENT_INDEX].key,
|
||||
false,
|
||||
)],
|
||||
data: vec![INSTRUCTION_VERIFY_MODIFIED],
|
||||
};
|
||||
invoke(&instruction, accounts)?;
|
||||
}
|
||||
INSTRUCTION_VERIFY_MODIFIED => {
|
||||
msg!("verify modified");
|
||||
assert_eq!(1, accounts[ARGUMENT_INDEX].try_borrow_data()?[0])
|
||||
}
|
||||
_ => panic!("Unknown instruction"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -12,7 +12,7 @@ use solana_sdk::{
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_noop() {
|
||||
async fn test_sysvars() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let program_test = ProgramTest::new(
|
||||
"solana_bpf_rust_sysvar",
|
||||
|
@ -17,7 +17,7 @@ use solana_bpf_loader_program::{
|
||||
use solana_cli_output::display::println_transaction;
|
||||
use solana_rbpf::{
|
||||
static_analysis::Analysis,
|
||||
vm::{Config, Executable, Tracer}
|
||||
vm::{Config, Executable, Tracer},
|
||||
};
|
||||
use solana_runtime::{
|
||||
bank::{Bank, ExecuteTimings, NonceRollbackInfo, TransactionBalancesSet, TransactionResults},
|
||||
@ -278,7 +278,6 @@ fn run_program(
|
||||
&bpf_loader::id(),
|
||||
parameter_accounts,
|
||||
parameter_bytes.as_slice(),
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
@ -455,7 +454,7 @@ fn test_program_bpf_sanity() {
|
||||
("solana_bpf_rust_external_spend", false),
|
||||
("solana_bpf_rust_iter", true),
|
||||
("solana_bpf_rust_many_args", true),
|
||||
("solana_bpf_rust_mem", true),
|
||||
("solana_bpf_rust_membuiltins", true),
|
||||
("solana_bpf_rust_noop", true),
|
||||
("solana_bpf_rust_panic", false),
|
||||
("solana_bpf_rust_param_passing", true),
|
||||
@ -757,6 +756,7 @@ fn test_program_bpf_invoke_sanity() {
|
||||
const TEST_RETURN_ERROR: u8 = 11;
|
||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
|
||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
|
||||
const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
@ -874,7 +874,6 @@ fn test_program_bpf_invoke_sanity() {
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
],
|
||||
Languages::Rust => vec![
|
||||
solana_sdk::system_program::id(),
|
||||
@ -894,7 +893,6 @@ fn test_program_bpf_invoke_sanity() {
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
solana_sdk::system_program::id(),
|
||||
],
|
||||
};
|
||||
@ -1000,6 +998,12 @@ fn test_program_bpf_invoke_sanity() {
|
||||
&[invoked_program_id.clone()],
|
||||
);
|
||||
|
||||
do_invoke_failure_test_local(
|
||||
TEST_WRITABLE_DEESCALATION_WRITABLE,
|
||||
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified),
|
||||
&[invoked_program_id.clone()],
|
||||
);
|
||||
|
||||
// Check resulting state
|
||||
|
||||
assert_eq!(43, bank.get_balance(&derived_key1));
|
||||
@ -1252,11 +1256,11 @@ fn assert_instruction_count() {
|
||||
("alloc", 1137),
|
||||
("bpf_to_bpf", 13),
|
||||
("multiple_static", 8),
|
||||
("noop", 5),
|
||||
("noop++", 5),
|
||||
("noop", 42),
|
||||
("noop++", 42),
|
||||
("relative_call", 10),
|
||||
("sanity", 169),
|
||||
("sanity++", 168),
|
||||
("sanity", 174),
|
||||
("sanity++", 174),
|
||||
("sha", 694),
|
||||
("struct_pass", 8),
|
||||
("struct_ret", 22),
|
||||
@ -1265,19 +1269,20 @@ fn assert_instruction_count() {
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
{
|
||||
programs.extend_from_slice(&[
|
||||
("solana_bpf_rust_128bit", 584),
|
||||
("solana_bpf_rust_alloc", 4967),
|
||||
("solana_bpf_rust_custom_heap", 365),
|
||||
("solana_bpf_rust_128bit", 572),
|
||||
("solana_bpf_rust_alloc", 8906),
|
||||
("solana_bpf_rust_custom_heap", 539),
|
||||
("solana_bpf_rust_dep_crate", 2),
|
||||
("solana_bpf_rust_external_spend", 334),
|
||||
("solana_bpf_rust_iter", 8),
|
||||
("solana_bpf_rust_many_args", 189),
|
||||
("solana_bpf_rust_mem", 1665),
|
||||
("solana_bpf_rust_noop", 322),
|
||||
("solana_bpf_rust_external_spend", 521),
|
||||
("solana_bpf_rust_iter", 724),
|
||||
("solana_bpf_rust_many_args", 237),
|
||||
("solana_bpf_rust_mem", 3143),
|
||||
("solana_bpf_rust_membuiltins", 4069),
|
||||
("solana_bpf_rust_noop", 495),
|
||||
("solana_bpf_rust_param_passing", 46),
|
||||
("solana_bpf_rust_rand", 325),
|
||||
("solana_bpf_rust_sanity", 587),
|
||||
("solana_bpf_rust_sha", 22417),
|
||||
("solana_bpf_rust_rand", 498),
|
||||
("solana_bpf_rust_sanity", 917),
|
||||
("solana_bpf_rust_sha", 29099),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -2454,3 +2459,68 @@ fn test_program_bpf_finalize() {
|
||||
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
#[test]
|
||||
fn test_program_bpf_ro_account_modify() {
|
||||
solana_logger::setup();
|
||||
|
||||
let GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(50);
|
||||
let mut bank = Bank::new(&genesis_config);
|
||||
let (name, id, entrypoint) = solana_bpf_loader_program!();
|
||||
bank.add_builtin(&name, id, entrypoint);
|
||||
let bank = Arc::new(bank);
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
let program_id = load_bpf_program(
|
||||
&bank_client,
|
||||
&bpf_loader::id(),
|
||||
&mint_keypair,
|
||||
"solana_bpf_rust_ro_account_modify",
|
||||
);
|
||||
|
||||
let argument_keypair = Keypair::new();
|
||||
let account = AccountSharedData::new(42, 100, &program_id);
|
||||
bank.store_account(&argument_keypair.pubkey(), &account);
|
||||
|
||||
let from_keypair = Keypair::new();
|
||||
let account = AccountSharedData::new(84, 0, &solana_sdk::system_program::id());
|
||||
bank.store_account(&from_keypair.pubkey(), &account);
|
||||
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let account_metas = vec![
|
||||
AccountMeta::new_readonly(argument_keypair.pubkey(), false),
|
||||
AccountMeta::new_readonly(program_id, false),
|
||||
];
|
||||
|
||||
let instruction = Instruction::new_with_bytes(program_id, &[0], account_metas.clone());
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
|
||||
println!("result: {:?}", result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
|
||||
);
|
||||
|
||||
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone());
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
|
||||
println!("result: {:?}", result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
|
||||
);
|
||||
|
||||
let instruction = Instruction::new_with_bytes(program_id, &[2], account_metas.clone());
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
|
||||
println!("result: {:?}", result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
|
||||
);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ use solana_sdk::{
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
clock::Clock,
|
||||
entrypoint::SUCCESS,
|
||||
feature_set::{skip_ro_deserialization, upgradeable_close_instruction},
|
||||
feature_set::upgradeable_close_instruction,
|
||||
ic_logger_msg, ic_msg,
|
||||
instruction::InstructionError,
|
||||
keyed_account::{from_keyed_account, keyed_account_at_index},
|
||||
@ -827,12 +827,7 @@ impl Executor for BpfExecutor {
|
||||
}
|
||||
let mut deserialize_time = Measure::start("deserialize");
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
deserialize_parameters(
|
||||
loader_id,
|
||||
keyed_accounts,
|
||||
parameter_bytes.as_slice(),
|
||||
invoke_context.is_feature_active(&skip_ro_deserialization::id()),
|
||||
)?;
|
||||
deserialize_parameters(loader_id, keyed_accounts, parameter_bytes.as_slice())?;
|
||||
deserialize_time.stop();
|
||||
invoke_context.update_timing(
|
||||
serialize_time.as_us(),
|
||||
|
@ -40,12 +40,11 @@ pub fn deserialize_parameters(
|
||||
loader_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
skip_ro_deserialization: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
if *loader_id == bpf_loader_deprecated::id() {
|
||||
deserialize_parameters_unaligned(keyed_accounts, buffer, skip_ro_deserialization)
|
||||
deserialize_parameters_unaligned(keyed_accounts, buffer)
|
||||
} else {
|
||||
deserialize_parameters_aligned(keyed_accounts, buffer, skip_ro_deserialization)
|
||||
deserialize_parameters_aligned(keyed_accounts, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,33 +126,28 @@ pub fn serialize_parameters_unaligned(
|
||||
pub fn deserialize_parameters_unaligned(
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
skip_ro_deserialization: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
let mut start = size_of::<u64>(); // number of accounts
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
start += 1; // is_dup
|
||||
if !is_dup {
|
||||
if keyed_account.is_writable() || !skip_ro_deserialization {
|
||||
start += size_of::<u8>(); // is_signer
|
||||
start += size_of::<u8>(); // is_writable
|
||||
start += size_of::<Pubkey>(); // key
|
||||
keyed_account
|
||||
.try_account_ref_mut()?
|
||||
.set_lamports(LittleEndian::read_u64(&buffer[start..]));
|
||||
start += size_of::<u64>() // lamports
|
||||
start += size_of::<u8>(); // is_signer
|
||||
start += size_of::<u8>(); // is_writable
|
||||
start += size_of::<Pubkey>(); // key
|
||||
keyed_account
|
||||
.try_account_ref_mut()?
|
||||
.set_lamports(LittleEndian::read_u64(&buffer[start..]));
|
||||
start += size_of::<u64>() // lamports
|
||||
+ size_of::<u64>(); // data length
|
||||
let end = start + keyed_account.data_len()?;
|
||||
keyed_account
|
||||
.try_account_ref_mut()?
|
||||
.set_data_from_slice(&buffer[start..end]);
|
||||
start += keyed_account.data_len()? // data
|
||||
let end = start + keyed_account.data_len()?;
|
||||
keyed_account
|
||||
.try_account_ref_mut()?
|
||||
.set_data_from_slice(&buffer[start..end]);
|
||||
start += keyed_account.data_len()? // data
|
||||
+ size_of::<Pubkey>() // owner
|
||||
+ size_of::<u8>() // executable
|
||||
+ size_of::<u64>(); // rent_epoch
|
||||
} else {
|
||||
start += get_serialized_account_size_unaligned(keyed_account)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -253,7 +247,6 @@ pub fn serialize_parameters_aligned(
|
||||
pub fn deserialize_parameters_aligned(
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
skip_ro_deserialization: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
let mut start = size_of::<u64>(); // number of accounts
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
@ -261,7 +254,7 @@ pub fn deserialize_parameters_aligned(
|
||||
start += size_of::<u8>(); // position
|
||||
if is_dup {
|
||||
start += 7; // padding to 64-bit aligned
|
||||
} else if keyed_account.is_writable() || !skip_ro_deserialization {
|
||||
} else {
|
||||
let mut account = keyed_account.try_account_ref_mut()?;
|
||||
start += size_of::<u8>() // is_signer
|
||||
+ size_of::<u8>() // is_writable
|
||||
@ -286,8 +279,6 @@ pub fn deserialize_parameters_aligned(
|
||||
start += pre_len + MAX_PERMITTED_DATA_INCREASE; // data
|
||||
start += (start as *const u8).align_offset(align_of::<u128>());
|
||||
start += size_of::<u64>(); // rent_epoch
|
||||
} else {
|
||||
start += get_serialized_account_size_aligned(keyed_account)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -454,24 +445,13 @@ mod tests {
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
deserialize_parameters(
|
||||
&bpf_loader::id(),
|
||||
&de_keyed_accounts,
|
||||
serialized.as_slice(),
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
deserialize_parameters(&bpf_loader::id(), &de_keyed_accounts, serialized.as_slice())
|
||||
.unwrap();
|
||||
for ((account, de_keyed_account), key) in
|
||||
accounts.iter().zip(de_keyed_accounts).zip(keys.clone())
|
||||
{
|
||||
assert_eq!(key, *de_keyed_account.unsigned_key());
|
||||
let account = account.borrow();
|
||||
assert_eq!(account.lamports(), de_keyed_account.lamports().unwrap());
|
||||
assert_eq!(
|
||||
account.data(),
|
||||
de_keyed_account.try_account_ref().unwrap().data()
|
||||
);
|
||||
assert_eq!(*account.owner(), de_keyed_account.owner().unwrap());
|
||||
assert_eq!(account.executable(), de_keyed_account.executable().unwrap());
|
||||
assert_eq!(account.rent_epoch(), de_keyed_account.rent_epoch().unwrap());
|
||||
}
|
||||
@ -517,7 +497,6 @@ mod tests {
|
||||
&bpf_loader_deprecated::id(),
|
||||
&de_keyed_accounts,
|
||||
serialized.as_slice(),
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
for ((account, de_keyed_account), key) in
|
||||
|
@ -19,9 +19,9 @@ use solana_sdk::{
|
||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
epoch_schedule::EpochSchedule,
|
||||
feature_set::{
|
||||
cpi_data_cost, cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks,
|
||||
enforce_aligned_host_addrs, keccak256_syscall_enabled,
|
||||
set_upgrade_authority_via_cpi_enabled, sysvar_via_syscall, update_data_on_realloc,
|
||||
cpi_data_cost, demote_sysvar_write_locks, enforce_aligned_host_addrs,
|
||||
keccak256_syscall_enabled, memory_ops_syscalls, set_upgrade_authority_via_cpi_enabled,
|
||||
sysvar_via_syscall, update_data_on_realloc,
|
||||
},
|
||||
hash::{Hasher, HASH_BYTES},
|
||||
ic_msg,
|
||||
@ -74,6 +74,8 @@ pub enum SyscallError {
|
||||
InstructionTooLarge(usize, usize),
|
||||
#[error("Too many accounts passed to inner instruction")]
|
||||
TooManyAccounts,
|
||||
#[error("Overlapping copy")]
|
||||
CopyOverlapping,
|
||||
}
|
||||
impl From<SyscallError> for EbpfError<BpfError> {
|
||||
fn from(error: SyscallError) -> Self {
|
||||
@ -145,10 +147,20 @@ pub fn register_syscalls(
|
||||
.register_syscall_by_name(b"sol_get_rent_sysvar", SyscallGetRentSysvar::call)?;
|
||||
}
|
||||
|
||||
if invoke_context.is_feature_active(&memory_ops_syscalls::id()) {
|
||||
syscall_registry.register_syscall_by_name(b"sol_memcpy_", SyscallMemcpy::call)?;
|
||||
syscall_registry.register_syscall_by_name(b"sol_memmove_", SyscallMemmove::call)?;
|
||||
syscall_registry.register_syscall_by_name(b"sol_memcmp_", SyscallMemcmp::call)?;
|
||||
syscall_registry.register_syscall_by_name(b"sol_memset_", SyscallMemset::call)?;
|
||||
}
|
||||
|
||||
// Cross-program invocation syscalls
|
||||
syscall_registry
|
||||
.register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?;
|
||||
syscall_registry
|
||||
.register_syscall_by_name(b"sol_invoke_signed_rust", SyscallInvokeSignedRust::call)?;
|
||||
|
||||
// Memory allocator
|
||||
syscall_registry.register_syscall_by_name(b"sol_alloc_free_", SyscallAllocFree::call)?;
|
||||
|
||||
Ok(syscall_registry)
|
||||
@ -268,6 +280,43 @@ pub fn bind_syscall_context_objects<'a>(
|
||||
}),
|
||||
);
|
||||
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemcpy {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
);
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemmove {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
);
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemcmp {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
);
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemset {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
);
|
||||
|
||||
let is_sysvar_via_syscall_active = invoke_context.is_feature_active(&sysvar_via_syscall::id());
|
||||
|
||||
let invoke_context = Rc::new(RefCell::new(invoke_context));
|
||||
@ -322,7 +371,6 @@ pub fn bind_syscall_context_objects<'a>(
|
||||
)?;
|
||||
|
||||
// Memory allocator
|
||||
|
||||
vm.bind_syscall_context_object(
|
||||
Box::new(SyscallAllocFree {
|
||||
aligned: *loader_id != bpf_loader_deprecated::id(),
|
||||
@ -1137,6 +1185,150 @@ impl<'a> SyscallObject<BpfError> for SyscallKeccak256<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// memcpy
|
||||
pub struct SyscallMemcpy<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemcpy<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
dst_addr: u64,
|
||||
src_addr: u64,
|
||||
n: u64,
|
||||
_arg4: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
// cannot be overlapping
|
||||
if dst_addr + n > src_addr && src_addr > dst_addr {
|
||||
*result = Err(SyscallError::CopyOverlapping.into());
|
||||
return;
|
||||
}
|
||||
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let dst = question_mark!(
|
||||
translate_slice_mut::<u8>(memory_mapping, dst_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let src = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, src_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), n as usize);
|
||||
}
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
/// memcpy
|
||||
pub struct SyscallMemmove<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemmove<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
dst_addr: u64,
|
||||
src_addr: u64,
|
||||
n: u64,
|
||||
_arg4: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let dst = question_mark!(
|
||||
translate_slice_mut::<u8>(memory_mapping, dst_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let src = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, src_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
unsafe {
|
||||
std::ptr::copy(src.as_ptr(), dst.as_mut_ptr(), n as usize);
|
||||
}
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
/// memcmp
|
||||
pub struct SyscallMemcmp<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemcmp<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
s1_addr: u64,
|
||||
s2_addr: u64,
|
||||
n: u64,
|
||||
cmp_result_addr: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let s1 = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, s1_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let s2 = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, s2_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let cmp_result = question_mark!(
|
||||
translate_type_mut::<i32>(memory_mapping, cmp_result_addr, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let mut i = 0;
|
||||
while i < n as usize {
|
||||
let a = s1[i];
|
||||
let b = s2[i];
|
||||
if a != b {
|
||||
*cmp_result = a as i32 - b as i32;
|
||||
*result = Ok(0);
|
||||
return;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
*cmp_result = 0;
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
/// memset
|
||||
pub struct SyscallMemset<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemset<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
s_addr: u64,
|
||||
c: u64,
|
||||
n: u64,
|
||||
_arg4: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let s = question_mark!(
|
||||
translate_slice_mut::<u8>(memory_mapping, s_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
for val in s.iter_mut().take(n as usize) {
|
||||
*val = c as u8;
|
||||
}
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-program invocation syscalls
|
||||
|
||||
struct AccountReferences<'a> {
|
||||
@ -1169,7 +1361,6 @@ trait SyscallInvokeSigned<'a> {
|
||||
fn translate_accounts(
|
||||
&self,
|
||||
account_keys: &[Pubkey],
|
||||
caller_write_privileges: &[bool],
|
||||
program_account_index: usize,
|
||||
account_infos_addr: u64,
|
||||
account_infos_len: u64,
|
||||
@ -1246,7 +1437,6 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
||||
fn translate_accounts(
|
||||
&self,
|
||||
account_keys: &[Pubkey],
|
||||
caller_write_privileges: &[bool],
|
||||
program_account_index: usize,
|
||||
account_infos_addr: u64,
|
||||
account_infos_len: u64,
|
||||
@ -1368,7 +1558,6 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
||||
|
||||
get_translated_accounts(
|
||||
account_keys,
|
||||
caller_write_privileges,
|
||||
program_account_index,
|
||||
&account_info_keys,
|
||||
account_infos,
|
||||
@ -1585,7 +1774,6 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
||||
fn translate_accounts(
|
||||
&self,
|
||||
account_keys: &[Pubkey],
|
||||
caller_write_privileges: &[bool],
|
||||
program_account_index: usize,
|
||||
account_infos_addr: u64,
|
||||
account_infos_len: u64,
|
||||
@ -1689,7 +1877,6 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
||||
|
||||
get_translated_accounts(
|
||||
account_keys,
|
||||
caller_write_privileges,
|
||||
program_account_index,
|
||||
&account_info_keys,
|
||||
account_infos,
|
||||
@ -1779,7 +1966,6 @@ impl<'a> SyscallObject<BpfError> for SyscallInvokeSignedC<'a> {
|
||||
|
||||
fn get_translated_accounts<'a, T, F>(
|
||||
account_keys: &[Pubkey],
|
||||
caller_write_privileges: &[bool],
|
||||
program_account_index: usize,
|
||||
account_info_keys: &[&Pubkey],
|
||||
account_infos: &[T],
|
||||
@ -1801,11 +1987,7 @@ where
|
||||
SyscallError::InstructionError(InstructionError::MissingAccount)
|
||||
})?;
|
||||
|
||||
if i == program_account_index
|
||||
|| account.borrow().executable()
|
||||
|| (invoke_context.is_feature_active(&cpi_share_ro_and_exec_accounts::id())
|
||||
&& !caller_write_privileges[i])
|
||||
{
|
||||
if i == program_account_index || account.borrow().executable() {
|
||||
// Use the known account
|
||||
accounts.push(account);
|
||||
refs.push(None);
|
||||
@ -2000,7 +2182,6 @@ fn call<'a>(
|
||||
check_authorized_program(&callee_program_id, &instruction.data, &invoke_context)?;
|
||||
let (accounts, account_refs) = syscall.translate_accounts(
|
||||
&message.account_keys,
|
||||
&caller_write_privileges,
|
||||
callee_program_id_index,
|
||||
account_infos_addr,
|
||||
account_infos_len,
|
||||
|
@ -186,7 +186,7 @@ pub struct AuthorizeWithSeedArgs {
|
||||
pub authority_owner: Pubkey,
|
||||
}
|
||||
|
||||
fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction {
|
||||
pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction {
|
||||
Instruction::new_with_bincode(
|
||||
id(),
|
||||
&StakeInstruction::Initialize(*authorized, *lockup),
|
||||
|
@ -11,7 +11,7 @@ documentation = "https://docs.rs/solana-remote-wallet"
|
||||
|
||||
[dependencies]
|
||||
base32 = "0.4.0"
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
dialoguer = "0.6.2"
|
||||
hidapi = { version = "1.2.5", default-features = false }
|
||||
log = "0.4.11"
|
||||
|
@ -8,10 +8,7 @@ use solana_sdk::{
|
||||
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
||||
account_utils::StateMut,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
feature_set::{
|
||||
cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks, instructions_sysvar_enabled,
|
||||
FeatureSet,
|
||||
},
|
||||
feature_set::{demote_sysvar_write_locks, instructions_sysvar_enabled, FeatureSet},
|
||||
ic_msg,
|
||||
instruction::{CompiledInstruction, Instruction, InstructionError},
|
||||
keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount},
|
||||
@ -254,7 +251,6 @@ pub struct ThisInvokeContext<'a> {
|
||||
rent: Rent,
|
||||
message: &'a Message,
|
||||
pre_accounts: Vec<PreAccount>,
|
||||
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||
account_deps: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
|
||||
logger: Rc<RefCell<dyn Logger>>,
|
||||
@ -301,7 +297,6 @@ impl<'a> ThisInvokeContext<'a> {
|
||||
rent,
|
||||
message,
|
||||
pre_accounts,
|
||||
executable_accounts,
|
||||
account_deps,
|
||||
programs,
|
||||
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
|
||||
@ -454,41 +449,22 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
||||
self.feature_set.is_active(feature_id)
|
||||
}
|
||||
fn get_account(&self, pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>> {
|
||||
if self.is_feature_active(&cpi_share_ro_and_exec_accounts::id()) {
|
||||
if let Some((_, account)) = self
|
||||
.executable_accounts
|
||||
.iter()
|
||||
.find(|(key, _)| key == pubkey)
|
||||
{
|
||||
Some(account.clone())
|
||||
} else if let Some((_, account)) =
|
||||
self.account_deps.iter().find(|(key, _)| key == pubkey)
|
||||
{
|
||||
if let Some(account) = self.pre_accounts.iter().find_map(|pre| {
|
||||
if pre.key == *pubkey {
|
||||
Some(pre.account.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
return Some(account);
|
||||
}
|
||||
self.account_deps.iter().find_map(|(key, account)| {
|
||||
if key == pubkey {
|
||||
Some(account.clone())
|
||||
} else {
|
||||
self.pre_accounts
|
||||
.iter()
|
||||
.find(|pre| pre.key == *pubkey)
|
||||
.map(|pre| pre.account.clone())
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(account) = self.pre_accounts.iter().find_map(|pre| {
|
||||
if pre.key == *pubkey {
|
||||
Some(pre.account.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
return Some(account);
|
||||
}
|
||||
self.account_deps.iter().find_map(|(key, account)| {
|
||||
if key == pubkey {
|
||||
Some(account.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
fn update_timing(
|
||||
&mut self,
|
||||
|
@ -100,5 +100,5 @@ EOF
|
||||
|
||||
|
||||
_ example_helloworld
|
||||
_ spl
|
||||
#_ spl
|
||||
_ serum_dex
|
||||
|
@ -15,7 +15,7 @@ OUT_DIR ?= ./out
|
||||
OS := $(shell uname)
|
||||
|
||||
LLVM_DIR = $(LOCAL_PATH)../dependencies/bpf-tools/llvm
|
||||
LLVM_SYSTEM_INC_DIRS := $(LLVM_DIR)/lib/clang/12.0.1/include
|
||||
LLVM_SYSTEM_INC_DIRS := $(LLVM_DIR)/lib/clang/11.0.1/include
|
||||
COMPILER_RT_DIR = $(LOCAL_PATH)../dependencies/bpf-tools/rust/lib/rustlib/bpfel-unknown-unknown/lib
|
||||
|
||||
ifdef LLVM_DIR
|
||||
|
@ -14,3 +14,5 @@ export CC="$bpf_sdk/dependencies/bpf-tools/llvm/bin/clang"
|
||||
export AR="$bpf_sdk/dependencies/bpf-tools/llvm/bin/llvm-ar"
|
||||
export OBJDUMP="$bpf_sdk/dependencies/bpf-tools/llvm/bin/llvm-objdump"
|
||||
export OBJCOPY="$bpf_sdk/dependencies/bpf-tools/llvm/bin/llvm-objcopy"
|
||||
|
||||
export RUSTFLAGS="-C lto=no -C opt-level=2"
|
||||
|
@ -92,7 +92,7 @@ if [[ ! -e criterion-$version.md || ! -e criterion ]]; then
|
||||
fi
|
||||
|
||||
# Install Rust-BPF
|
||||
version=v1.9
|
||||
version=v1.8
|
||||
if [[ ! -e bpf-tools-$version.md || ! -e bpf-tools ]]; then
|
||||
(
|
||||
set -e
|
||||
|
@ -360,7 +360,7 @@ fn build_bpf_package(config: &Config, target_directory: &Path, package: &cargo_m
|
||||
install_if_missing(
|
||||
&config,
|
||||
"bpf-tools",
|
||||
"v1.9",
|
||||
"v1.8",
|
||||
"https://github.com/solana-labs/bpf-tools/releases/download",
|
||||
&PathBuf::from(bpf_tools_filename),
|
||||
)
|
||||
@ -377,7 +377,9 @@ fn build_bpf_package(config: &Config, target_directory: &Path, package: &cargo_m
|
||||
env::set_var("AR", llvm_bin.join("llvm-ar"));
|
||||
env::set_var("OBJDUMP", llvm_bin.join("llvm-objdump"));
|
||||
env::set_var("OBJCOPY", llvm_bin.join("llvm-objcopy"));
|
||||
|
||||
let mut rust_flags = String::from("-C lto=no");
|
||||
rust_flags.push_str(" -C opt-level=2");
|
||||
env::set_var("RUSTFLAGS", rust_flags);
|
||||
let cargo_build = PathBuf::from("cargo");
|
||||
let mut cargo_build_args = vec![
|
||||
"+bpf",
|
||||
|
@ -30,6 +30,7 @@ pub mod native_token;
|
||||
pub mod nonce;
|
||||
pub mod program;
|
||||
pub mod program_error;
|
||||
pub mod program_memory;
|
||||
pub mod program_option;
|
||||
pub mod program_pack;
|
||||
pub mod program_stubs;
|
||||
|
89
sdk/program/src/program_memory.rs
Normal file
89
sdk/program/src/program_memory.rs
Normal file
@ -0,0 +1,89 @@
|
||||
//! @brief Solana Rust-based BPF memory operations
|
||||
|
||||
/// Memcpy
|
||||
///
|
||||
/// @param dst - Destination
|
||||
/// @param src - Source
|
||||
/// @param n - Number of bytes to copy
|
||||
#[inline]
|
||||
pub fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) {
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memcpy_(dst: *mut u8, src: *const u8, n: u64);
|
||||
}
|
||||
unsafe {
|
||||
sol_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memcpy(dst.as_mut_ptr(), src.as_ptr(), n);
|
||||
}
|
||||
|
||||
/// Memmove
|
||||
///
|
||||
/// @param dst - Destination
|
||||
/// @param src - Source
|
||||
/// @param n - Number of bytes to copy
|
||||
///
|
||||
/// # Safety
|
||||
#[inline]
|
||||
pub unsafe fn sol_memmove(dst: *mut u8, src: *mut u8, n: usize) {
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memmove_(dst: *mut u8, src: *const u8, n: u64);
|
||||
}
|
||||
sol_memmove_(dst, src, n as u64);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memmove(dst, src, n);
|
||||
}
|
||||
|
||||
/// Memcmp
|
||||
///
|
||||
/// @param s1 - Slice to be compared
|
||||
/// @param s2 - Slice to be compared
|
||||
/// @param n - Number of bytes to compare
|
||||
#[inline]
|
||||
pub fn sol_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
|
||||
let mut result = 0;
|
||||
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memcmp_(s1: *const u8, s2: *const u8, n: u64, result: *mut i32);
|
||||
}
|
||||
unsafe {
|
||||
sol_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, &mut result as *mut i32);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memcmp(s1.as_ptr(), s2.as_ptr(), n, &mut result as *mut i32);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Memset
|
||||
///
|
||||
/// @param s1 - Slice to be compared
|
||||
/// @param s2 - Slice to be compared
|
||||
/// @param n - Number of bytes to compare
|
||||
#[inline]
|
||||
pub fn sol_memset(s: &mut [u8], c: u8, n: usize) {
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memset_(s: *mut u8, c: u8, n: u64);
|
||||
}
|
||||
unsafe {
|
||||
sol_memset_(s.as_mut_ptr(), c, n as u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memset(s.as_mut_ptr(), c, n);
|
||||
}
|
@ -12,12 +12,13 @@ lazy_static::lazy_static! {
|
||||
static ref SYSCALL_STUBS: Arc<RwLock<Box<dyn SyscallStubs>>> = Arc::new(RwLock::new(Box::new(DefaultSyscallStubs {})));
|
||||
}
|
||||
|
||||
// The default syscall stubs don't do much, but `set_syscalls()` can be used to swap in
|
||||
// alternatives
|
||||
// The default syscall stubs may not do much, but `set_syscalls()` can be used
|
||||
// to swap in alternatives
|
||||
pub fn set_syscall_stubs(syscall_stubs: Box<dyn SyscallStubs>) -> Box<dyn SyscallStubs> {
|
||||
std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs)
|
||||
}
|
||||
|
||||
#[allow(clippy::integer_arithmetic)]
|
||||
pub trait SyscallStubs: Sync + Send {
|
||||
fn sol_log(&self, message: &str) {
|
||||
println!("{}", message);
|
||||
@ -34,7 +35,6 @@ pub trait SyscallStubs: Sync + Send {
|
||||
sol_log("SyscallStubs: sol_invoke_signed() not available");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||
UNSUPPORTED_SYSVAR
|
||||
}
|
||||
@ -47,6 +47,39 @@ pub trait SyscallStubs: Sync + Send {
|
||||
fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||
UNSUPPORTED_SYSVAR
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
|
||||
// cannot be overlapping
|
||||
if dst as usize + n > src as usize && src as usize > dst as usize {
|
||||
panic!("memcpy does not support oveerlapping regions");
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(src, dst, n as usize);
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memmove(&self, dst: *mut u8, src: *const u8, n: usize) {
|
||||
std::ptr::copy(src, dst, n as usize);
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memcmp(&self, s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let a = *s1.add(i);
|
||||
let b = *s2.add(i);
|
||||
if a != b {
|
||||
*result = a as i32 - b as i32;
|
||||
return;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
*result = 0
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memset(&self, s: *mut u8, c: u8, n: usize) {
|
||||
let s = std::slice::from_raw_parts_mut(s, n);
|
||||
for val in s.iter_mut().take(n) {
|
||||
*val = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultSyscallStubs {}
|
||||
@ -96,3 +129,27 @@ pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 {
|
||||
pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 {
|
||||
SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr)
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memcpy(dst, src, n);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memmove(dst, src, n);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memcmp(s1, s2, n, result);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memset(s: *mut u8, c: u8, n: usize) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memset(s, c, n);
|
||||
}
|
||||
}
|
||||
|
@ -91,14 +91,6 @@ pub mod check_program_owner {
|
||||
solana_sdk::declare_id!("5XnbR5Es9YXEARRuP6mdvoxiW3hx5atNNeBmwVd8P3QD");
|
||||
}
|
||||
|
||||
pub mod cpi_share_ro_and_exec_accounts {
|
||||
solana_sdk::declare_id!("6VgVBi3uRVqp56TtEwNou8idgdmhCD1aYqX8FaJ1fnJb");
|
||||
}
|
||||
|
||||
pub mod skip_ro_deserialization {
|
||||
solana_sdk::declare_id!("6Sw5JV84f7QkDe8gvRxpcPWFnPpfpgEnNziiy8sELaCp");
|
||||
}
|
||||
|
||||
pub mod require_stake_for_gossip {
|
||||
solana_sdk::declare_id!("6oNzd5Z3M2L1xo4Q5hoox7CR2DuW7m1ETLWH5jHJthwa");
|
||||
}
|
||||
@ -143,6 +135,10 @@ pub mod stake_program_v4 {
|
||||
solana_sdk::declare_id!("Dc7djyhP9aLfdq2zktpvskeAjpG56msCU1yexpxXiWZb");
|
||||
}
|
||||
|
||||
pub mod memory_ops_syscalls {
|
||||
solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
@ -166,8 +162,6 @@ lazy_static! {
|
||||
(warp_timestamp_again::id(), "warp timestamp again, adjust bounding to 25% fast 80% slow #15204"),
|
||||
(check_init_vote_data::id(), "check initialized Vote data"),
|
||||
(check_program_owner::id(), "limit programs to operating on accounts owned by itself"),
|
||||
(cpi_share_ro_and_exec_accounts::id(), "share RO and Executable accounts during cross-program invocations"),
|
||||
(skip_ro_deserialization::id(), "skip deserialization of read-only accounts"),
|
||||
(require_stake_for_gossip::id(), "require stakes for propagating crds values through gossip #15561"),
|
||||
(cpi_data_cost::id(), "charge the compute budget for data passed via CPI"),
|
||||
(upgradeable_close_instruction::id(), "close upgradeable buffer accounts"),
|
||||
@ -179,6 +173,7 @@ lazy_static! {
|
||||
(update_data_on_realloc::id(), "Retain updated data values modified after realloc via CPI"),
|
||||
(keccak256_syscall_enabled::id(), "keccak256 syscall"),
|
||||
(stake_program_v4::id(), "solana_stake_program v4"),
|
||||
(memory_ops_syscalls::id(), "add syscalls for memory operations"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
@ -11,7 +11,7 @@ documentation = "https://docs.rs/solana-stake-monitor"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
log = "0.4.11"
|
||||
serde = "1.0.122"
|
||||
serde_yaml = "0.8.13"
|
||||
|
@ -12,7 +12,7 @@ documentation = "https://docs.rs/solana-tokens"
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
clap = "2.33.0"
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
csv = "1.1.3"
|
||||
ctrlc = { version = "3.1.5", features = ["termination"] }
|
||||
dirs-next = "2.0.0"
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::args::{
|
||||
Args, BalancesArgs, Command, DistributeTokensArgs, SplTokenArgs, StakeArgs, TransactionLogArgs,
|
||||
Args, BalancesArgs, Command, DistributeTokensArgs, SenderStakeArgs, SplTokenArgs, StakeArgs,
|
||||
TransactionLogArgs,
|
||||
};
|
||||
use clap::{
|
||||
crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand,
|
||||
@ -102,9 +103,80 @@ where
|
||||
.help("Fee payer"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("create-stake")
|
||||
.about("Create stake accounts")
|
||||
.arg(
|
||||
Arg::with_name("db_path")
|
||||
.long("db-path")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.value_name("FILE")
|
||||
.help(
|
||||
"Location for storing distribution database. \
|
||||
The database is used for tracking transactions as they are finalized \
|
||||
and preventing double spends.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("input_csv")
|
||||
.long("input-csv")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.value_name("FILE")
|
||||
.help("Allocations CSV file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("dry_run")
|
||||
.long("dry-run")
|
||||
.help("Do not execute any transfers"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("output_path")
|
||||
.long("output-path")
|
||||
.short("o")
|
||||
.value_name("FILE")
|
||||
.takes_value(true)
|
||||
.help("Write the transaction log to this file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("sender_keypair")
|
||||
.long("from")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.value_name("SENDING_KEYPAIR")
|
||||
.validator(is_valid_signer)
|
||||
.help("Keypair to fund accounts"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unlocked_sol")
|
||||
.default_value("1.0")
|
||||
.long("unlocked-sol")
|
||||
.takes_value(true)
|
||||
.value_name("SOL_AMOUNT")
|
||||
.help("Amount of SOL to put in system account to pay for fees"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lockup_authority")
|
||||
.long("lockup-authority")
|
||||
.takes_value(true)
|
||||
.value_name("PUBKEY")
|
||||
.validator(is_valid_pubkey)
|
||||
.help("Lockup Authority Address"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("fee_payer")
|
||||
.long("fee-payer")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.value_name("KEYPAIR")
|
||||
.validator(is_valid_signer)
|
||||
.help("Fee payer"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("distribute-stake")
|
||||
.about("Distribute stake accounts")
|
||||
.about("Split to stake accounts")
|
||||
.arg(
|
||||
Arg::with_name("db_path")
|
||||
.long("db-path")
|
||||
@ -363,6 +435,58 @@ fn parse_distribute_tokens_args(
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_create_stake_args(
|
||||
matches: &ArgMatches<'_>,
|
||||
) -> Result<DistributeTokensArgs, Box<dyn Error>> {
|
||||
let mut wallet_manager = maybe_wallet_manager()?;
|
||||
let signer_matches = ArgMatches::default(); // No default signer
|
||||
|
||||
let sender_keypair_str = value_t_or_exit!(matches, "sender_keypair", String);
|
||||
let sender_keypair = signer_from_path(
|
||||
&signer_matches,
|
||||
&sender_keypair_str,
|
||||
"sender",
|
||||
&mut wallet_manager,
|
||||
)?;
|
||||
|
||||
let fee_payer_str = value_t_or_exit!(matches, "fee_payer", String);
|
||||
let fee_payer = signer_from_path(
|
||||
&signer_matches,
|
||||
&fee_payer_str,
|
||||
"fee-payer",
|
||||
&mut wallet_manager,
|
||||
)?;
|
||||
|
||||
let lockup_authority_str = value_t!(matches, "lockup_authority", String).ok();
|
||||
let lockup_authority = lockup_authority_str
|
||||
.map(|path| {
|
||||
pubkey_from_path(
|
||||
&signer_matches,
|
||||
&path,
|
||||
"lockup authority",
|
||||
&mut wallet_manager,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let stake_args = StakeArgs {
|
||||
unlocked_sol: sol_to_lamports(value_t_or_exit!(matches, "unlocked_sol", f64)),
|
||||
lockup_authority,
|
||||
sender_stake_args: None,
|
||||
};
|
||||
Ok(DistributeTokensArgs {
|
||||
input_csv: value_t_or_exit!(matches, "input_csv", String),
|
||||
transaction_db: value_t_or_exit!(matches, "db_path", String),
|
||||
output_path: matches.value_of("output_path").map(|path| path.to_string()),
|
||||
dry_run: matches.is_present("dry_run"),
|
||||
sender_keypair,
|
||||
fee_payer,
|
||||
stake_args: Some(stake_args),
|
||||
spl_token_args: None,
|
||||
transfer_amount: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_distribute_stake_args(
|
||||
matches: &ArgMatches<'_>,
|
||||
) -> Result<DistributeTokensArgs, Box<dyn Error>> {
|
||||
@ -421,13 +545,18 @@ fn parse_distribute_stake_args(
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let stake_args = StakeArgs {
|
||||
let lockup_authority_address = lockup_authority.as_ref().map(|keypair| keypair.pubkey());
|
||||
let sender_stake_args = SenderStakeArgs {
|
||||
stake_account_address,
|
||||
unlocked_sol: sol_to_lamports(value_t_or_exit!(matches, "unlocked_sol", f64)),
|
||||
stake_authority,
|
||||
withdraw_authority,
|
||||
lockup_authority,
|
||||
};
|
||||
let stake_args = StakeArgs {
|
||||
unlocked_sol: sol_to_lamports(value_t_or_exit!(matches, "unlocked_sol", f64)),
|
||||
lockup_authority: lockup_authority_address,
|
||||
sender_stake_args: Some(sender_stake_args),
|
||||
};
|
||||
Ok(DistributeTokensArgs {
|
||||
input_csv: value_t_or_exit!(matches, "input_csv", String),
|
||||
transaction_db: value_t_or_exit!(matches, "db_path", String),
|
||||
@ -520,6 +649,9 @@ where
|
||||
("distribute-tokens", Some(matches)) => {
|
||||
Command::DistributeTokens(parse_distribute_tokens_args(matches)?)
|
||||
}
|
||||
("create-stake", Some(matches)) => {
|
||||
Command::DistributeTokens(parse_create_stake_args(matches)?)
|
||||
}
|
||||
("distribute-stake", Some(matches)) => {
|
||||
Command::DistributeTokens(parse_distribute_stake_args(matches)?)
|
||||
}
|
||||
|
@ -1,5 +1,18 @@
|
||||
use solana_sdk::{pubkey::Pubkey, signature::Signer};
|
||||
|
||||
pub struct SenderStakeArgs {
|
||||
pub stake_account_address: Pubkey,
|
||||
pub stake_authority: Box<dyn Signer>,
|
||||
pub withdraw_authority: Box<dyn Signer>,
|
||||
pub lockup_authority: Option<Box<dyn Signer>>,
|
||||
}
|
||||
|
||||
pub struct StakeArgs {
|
||||
pub unlocked_sol: u64,
|
||||
pub lockup_authority: Option<Pubkey>,
|
||||
pub sender_stake_args: Option<SenderStakeArgs>,
|
||||
}
|
||||
|
||||
pub struct DistributeTokensArgs {
|
||||
pub input_csv: String,
|
||||
pub transaction_db: String,
|
||||
@ -12,14 +25,6 @@ pub struct DistributeTokensArgs {
|
||||
pub transfer_amount: Option<u64>,
|
||||
}
|
||||
|
||||
pub struct StakeArgs {
|
||||
pub unlocked_sol: u64,
|
||||
pub stake_account_address: Pubkey,
|
||||
pub stake_authority: Box<dyn Signer>,
|
||||
pub withdraw_authority: Box<dyn Signer>,
|
||||
pub lockup_authority: Option<Box<dyn Signer>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SplTokenArgs {
|
||||
pub token_account_address: Pubkey,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
args::{BalancesArgs, DistributeTokensArgs, StakeArgs, TransactionLogArgs},
|
||||
args::{BalancesArgs, DistributeTokensArgs, SenderStakeArgs, StakeArgs, TransactionLogArgs},
|
||||
db::{self, TransactionInfo},
|
||||
spl_token::*,
|
||||
token_display::Token,
|
||||
@ -178,77 +178,108 @@ fn distribution_instructions(
|
||||
lockup_date: Option<DateTime<Utc>>,
|
||||
do_create_associated_token_account: bool,
|
||||
) -> Vec<Instruction> {
|
||||
if args.stake_args.is_none() && args.spl_token_args.is_none() {
|
||||
let from = args.sender_keypair.pubkey();
|
||||
let to = allocation.recipient.parse().unwrap();
|
||||
let lamports = allocation.amount;
|
||||
let instruction = system_instruction::transfer(&from, &to, lamports);
|
||||
return vec![instruction];
|
||||
}
|
||||
|
||||
if args.spl_token_args.is_some() {
|
||||
return build_spl_token_instructions(allocation, args, do_create_associated_token_account);
|
||||
}
|
||||
|
||||
let stake_args = args.stake_args.as_ref().unwrap();
|
||||
let unlocked_sol = stake_args.unlocked_sol;
|
||||
let sender_pubkey = args.sender_keypair.pubkey();
|
||||
let stake_authority = stake_args.stake_authority.pubkey();
|
||||
let withdraw_authority = stake_args.withdraw_authority.pubkey();
|
||||
match &args.stake_args {
|
||||
// No stake args; a simple token transfer.
|
||||
None => {
|
||||
let from = args.sender_keypair.pubkey();
|
||||
let to = allocation.recipient.parse().unwrap();
|
||||
let lamports = allocation.amount;
|
||||
let instruction = system_instruction::transfer(&from, &to, lamports);
|
||||
vec![instruction]
|
||||
}
|
||||
|
||||
let mut instructions = stake_instruction::split(
|
||||
&stake_args.stake_account_address,
|
||||
&stake_authority,
|
||||
allocation.amount - unlocked_sol,
|
||||
&new_stake_account_address,
|
||||
);
|
||||
// Stake args provided, so create a recipient stake account.
|
||||
Some(stake_args) => {
|
||||
let unlocked_sol = stake_args.unlocked_sol;
|
||||
let sender_pubkey = args.sender_keypair.pubkey();
|
||||
let recipient = allocation.recipient.parse().unwrap();
|
||||
|
||||
let recipient = allocation.recipient.parse().unwrap();
|
||||
let mut instructions = match &stake_args.sender_stake_args {
|
||||
// No source stake account, so create a recipient stake account directly.
|
||||
None => {
|
||||
// Make the recipient both the new stake and withdraw authority
|
||||
let authorized = Authorized {
|
||||
staker: recipient,
|
||||
withdrawer: recipient,
|
||||
};
|
||||
let mut lockup = Lockup::default();
|
||||
if let Some(lockup_date) = lockup_date {
|
||||
lockup.unix_timestamp = lockup_date.timestamp();
|
||||
}
|
||||
if let Some(lockup_authority) = stake_args.lockup_authority {
|
||||
lockup.custodian = lockup_authority;
|
||||
}
|
||||
stake_instruction::create_account(
|
||||
&sender_pubkey,
|
||||
&new_stake_account_address,
|
||||
&authorized,
|
||||
&lockup,
|
||||
allocation.amount - unlocked_sol,
|
||||
)
|
||||
}
|
||||
|
||||
// Make the recipient the new stake authority
|
||||
instructions.push(stake_instruction::authorize(
|
||||
&new_stake_account_address,
|
||||
&stake_authority,
|
||||
&recipient,
|
||||
StakeAuthorize::Staker,
|
||||
None,
|
||||
));
|
||||
// A sender stake account was provided, so create a recipient stake account by
|
||||
// splitting the sender account.
|
||||
Some(sender_stake_args) => {
|
||||
let stake_authority = sender_stake_args.stake_authority.pubkey();
|
||||
let withdraw_authority = sender_stake_args.withdraw_authority.pubkey();
|
||||
let mut instructions = stake_instruction::split(
|
||||
&sender_stake_args.stake_account_address,
|
||||
&stake_authority,
|
||||
allocation.amount - unlocked_sol,
|
||||
&new_stake_account_address,
|
||||
);
|
||||
|
||||
// Make the recipient the new withdraw authority
|
||||
instructions.push(stake_instruction::authorize(
|
||||
&new_stake_account_address,
|
||||
&withdraw_authority,
|
||||
&recipient,
|
||||
StakeAuthorize::Withdrawer,
|
||||
None,
|
||||
));
|
||||
// Make the recipient the new stake authority
|
||||
instructions.push(stake_instruction::authorize(
|
||||
&new_stake_account_address,
|
||||
&stake_authority,
|
||||
&recipient,
|
||||
StakeAuthorize::Staker,
|
||||
None,
|
||||
));
|
||||
|
||||
// Add lockup
|
||||
if let Some(lockup_date) = lockup_date {
|
||||
let lockup_authority = stake_args
|
||||
.lockup_authority
|
||||
.as_ref()
|
||||
.map(|signer| signer.pubkey())
|
||||
.unwrap();
|
||||
let lockup = LockupArgs {
|
||||
unix_timestamp: Some(lockup_date.timestamp()),
|
||||
epoch: None,
|
||||
custodian: None,
|
||||
};
|
||||
instructions.push(stake_instruction::set_lockup(
|
||||
&new_stake_account_address,
|
||||
&lockup,
|
||||
&lockup_authority,
|
||||
));
|
||||
// Make the recipient the new withdraw authority
|
||||
instructions.push(stake_instruction::authorize(
|
||||
&new_stake_account_address,
|
||||
&withdraw_authority,
|
||||
&recipient,
|
||||
StakeAuthorize::Withdrawer,
|
||||
None,
|
||||
));
|
||||
|
||||
// Add lockup
|
||||
if let Some(lockup_date) = lockup_date {
|
||||
let lockup = LockupArgs {
|
||||
unix_timestamp: Some(lockup_date.timestamp()),
|
||||
epoch: None,
|
||||
custodian: None,
|
||||
};
|
||||
instructions.push(stake_instruction::set_lockup(
|
||||
&new_stake_account_address,
|
||||
&lockup,
|
||||
&stake_args.lockup_authority.unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
instructions
|
||||
}
|
||||
};
|
||||
|
||||
// Transfer some unlocked tokens to recipient, which they can use for transaction fees.
|
||||
instructions.push(system_instruction::transfer(
|
||||
&sender_pubkey,
|
||||
&recipient,
|
||||
unlocked_sol,
|
||||
));
|
||||
|
||||
instructions
|
||||
}
|
||||
}
|
||||
|
||||
instructions.push(system_instruction::transfer(
|
||||
&sender_pubkey,
|
||||
&recipient,
|
||||
unlocked_sol,
|
||||
));
|
||||
|
||||
instructions
|
||||
}
|
||||
|
||||
fn build_messages(
|
||||
@ -336,14 +367,17 @@ fn send_messages(
|
||||
|
||||
let mut signers = vec![&*args.fee_payer, &*args.sender_keypair];
|
||||
if let Some(stake_args) = &args.stake_args {
|
||||
signers.push(&*stake_args.stake_authority);
|
||||
signers.push(&*stake_args.withdraw_authority);
|
||||
signers.push(&new_stake_account_keypair);
|
||||
if !allocation.lockup_date.is_empty() {
|
||||
if let Some(lockup_authority) = &stake_args.lockup_authority {
|
||||
signers.push(&**lockup_authority);
|
||||
} else {
|
||||
return Err(Error::MissingLockupAuthority);
|
||||
if let Some(sender_stake_args) = &stake_args.sender_stake_args {
|
||||
signers.push(&*sender_stake_args.stake_authority);
|
||||
signers.push(&*sender_stake_args.withdraw_authority);
|
||||
signers.push(&new_stake_account_keypair);
|
||||
if !allocation.lockup_date.is_empty() {
|
||||
if let Some(lockup_authority) = &sender_stake_args.lockup_authority {
|
||||
signers.push(&**lockup_authority);
|
||||
} else {
|
||||
return Err(Error::MissingLockupAuthority);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -366,12 +400,14 @@ fn send_messages(
|
||||
};
|
||||
match result {
|
||||
Ok((transaction, last_valid_slot)) => {
|
||||
let new_stake_account_address_option =
|
||||
args.stake_args.as_ref().map(|_| &new_stake_account_address);
|
||||
db::set_transaction_info(
|
||||
db,
|
||||
&allocation.recipient.parse().unwrap(),
|
||||
allocation.amount,
|
||||
&transaction,
|
||||
args.stake_args.as_ref().map(|_| &new_stake_account_address),
|
||||
new_stake_account_address_option,
|
||||
false,
|
||||
last_valid_slot,
|
||||
lockup_date,
|
||||
@ -708,8 +744,13 @@ fn check_payer_balances(
|
||||
let (distribution_source, unlocked_sol_source) = if let Some(stake_args) = &args.stake_args {
|
||||
let total_unlocked_sol = allocations.len() as u64 * stake_args.unlocked_sol;
|
||||
undistributed_tokens -= total_unlocked_sol;
|
||||
let from_pubkey = if let Some(sender_stake_args) = &stake_args.sender_stake_args {
|
||||
sender_stake_args.stake_account_address
|
||||
} else {
|
||||
args.sender_keypair.pubkey()
|
||||
};
|
||||
(
|
||||
stake_args.stake_account_address,
|
||||
from_pubkey,
|
||||
Some((args.sender_keypair.pubkey(), total_unlocked_sol)),
|
||||
)
|
||||
} else {
|
||||
@ -909,7 +950,7 @@ pub fn test_process_distribute_tokens_with_client(
|
||||
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
||||
}
|
||||
|
||||
pub fn test_process_distribute_stake_with_client(client: &RpcClient, sender_keypair: Keypair) {
|
||||
pub fn test_process_create_stake_with_client(client: &RpcClient, sender_keypair: Keypair) {
|
||||
let exit = Arc::new(AtomicBool::default());
|
||||
let fee_payer = Keypair::new();
|
||||
let transaction = transfer(
|
||||
@ -975,11 +1016,137 @@ pub fn test_process_distribute_stake_with_client(client: &RpcClient, sender_keyp
|
||||
let output_path = output_file.path().to_str().unwrap().to_string();
|
||||
|
||||
let stake_args = StakeArgs {
|
||||
lockup_authority: None,
|
||||
unlocked_sol: sol_to_lamports(1.0),
|
||||
sender_stake_args: None,
|
||||
};
|
||||
let args = DistributeTokensArgs {
|
||||
fee_payer: Box::new(fee_payer),
|
||||
dry_run: false,
|
||||
input_csv,
|
||||
transaction_db: transaction_db.clone(),
|
||||
output_path: Some(output_path.clone()),
|
||||
stake_args: Some(stake_args),
|
||||
spl_token_args: None,
|
||||
sender_keypair: Box::new(sender_keypair),
|
||||
transfer_amount: None,
|
||||
};
|
||||
let confirmations = process_allocations(client, &args, exit.clone()).unwrap();
|
||||
assert_eq!(confirmations, None);
|
||||
|
||||
let transaction_infos =
|
||||
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
||||
assert_eq!(transaction_infos.len(), 1);
|
||||
assert_eq!(transaction_infos[0].recipient, alice_pubkey);
|
||||
assert_eq!(transaction_infos[0].amount, expected_amount);
|
||||
|
||||
assert_eq!(
|
||||
client.get_balance(&alice_pubkey).unwrap(),
|
||||
sol_to_lamports(1.0),
|
||||
);
|
||||
let new_stake_account_address = transaction_infos[0].new_stake_account_address.unwrap();
|
||||
assert_eq!(
|
||||
client.get_balance(&new_stake_account_address).unwrap(),
|
||||
expected_amount - sol_to_lamports(1.0),
|
||||
);
|
||||
|
||||
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
||||
|
||||
// Now, run it again, and check there's no double-spend.
|
||||
process_allocations(client, &args, exit).unwrap();
|
||||
let transaction_infos =
|
||||
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
||||
assert_eq!(transaction_infos.len(), 1);
|
||||
assert_eq!(transaction_infos[0].recipient, alice_pubkey);
|
||||
assert_eq!(transaction_infos[0].amount, expected_amount);
|
||||
|
||||
assert_eq!(
|
||||
client.get_balance(&alice_pubkey).unwrap(),
|
||||
sol_to_lamports(1.0),
|
||||
);
|
||||
assert_eq!(
|
||||
client.get_balance(&new_stake_account_address).unwrap(),
|
||||
expected_amount - sol_to_lamports(1.0),
|
||||
);
|
||||
|
||||
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
||||
}
|
||||
|
||||
pub fn test_process_distribute_stake_with_client(client: &RpcClient, sender_keypair: Keypair) {
|
||||
let exit = Arc::new(AtomicBool::default());
|
||||
let fee_payer = Keypair::new();
|
||||
let transaction = transfer(
|
||||
client,
|
||||
sol_to_lamports(1.0),
|
||||
&sender_keypair,
|
||||
&fee_payer.pubkey(),
|
||||
)
|
||||
.unwrap();
|
||||
client
|
||||
.send_and_confirm_transaction_with_spinner(&transaction)
|
||||
.unwrap();
|
||||
|
||||
let stake_account_keypair = Keypair::new();
|
||||
let stake_account_address = stake_account_keypair.pubkey();
|
||||
let stake_authority = Keypair::new();
|
||||
let withdraw_authority = Keypair::new();
|
||||
|
||||
let authorized = Authorized {
|
||||
staker: stake_authority.pubkey(),
|
||||
withdrawer: withdraw_authority.pubkey(),
|
||||
};
|
||||
let lockup = Lockup::default();
|
||||
let instructions = stake_instruction::create_account(
|
||||
&sender_keypair.pubkey(),
|
||||
&stake_account_address,
|
||||
&authorized,
|
||||
&lockup,
|
||||
sol_to_lamports(3000.0),
|
||||
);
|
||||
let message = Message::new(&instructions, Some(&sender_keypair.pubkey()));
|
||||
let signers = [&sender_keypair, &stake_account_keypair];
|
||||
let (blockhash, _fees) = client.get_recent_blockhash().unwrap();
|
||||
let transaction = Transaction::new(&signers, message, blockhash);
|
||||
client
|
||||
.send_and_confirm_transaction_with_spinner(&transaction)
|
||||
.unwrap();
|
||||
|
||||
let expected_amount = sol_to_lamports(1000.0);
|
||||
let alice_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
let input_csv = file.path().to_str().unwrap().to_string();
|
||||
let mut wtr = csv::WriterBuilder::new().from_writer(file);
|
||||
wtr.write_record(&["recipient", "amount", "lockup_date"])
|
||||
.unwrap();
|
||||
wtr.write_record(&[
|
||||
alice_pubkey.to_string(),
|
||||
lamports_to_sol(expected_amount).to_string(),
|
||||
"".to_string(),
|
||||
])
|
||||
.unwrap();
|
||||
wtr.flush().unwrap();
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let transaction_db = dir
|
||||
.path()
|
||||
.join("transactions.db")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let output_file = NamedTempFile::new().unwrap();
|
||||
let output_path = output_file.path().to_str().unwrap().to_string();
|
||||
|
||||
let sender_stake_args = SenderStakeArgs {
|
||||
stake_account_address,
|
||||
stake_authority: Box::new(stake_authority),
|
||||
withdraw_authority: Box::new(withdraw_authority),
|
||||
lockup_authority: None,
|
||||
};
|
||||
let stake_args = StakeArgs {
|
||||
unlocked_sol: sol_to_lamports(1.0),
|
||||
lockup_authority: None,
|
||||
sender_stake_args: Some(sender_stake_args),
|
||||
};
|
||||
let args = DistributeTokensArgs {
|
||||
fee_payer: Box::new(fee_payer),
|
||||
@ -1061,6 +1228,16 @@ mod tests {
|
||||
test_process_distribute_tokens_with_client(&client, alice, Some(sol_to_lamports(1.5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_stake_allocations() {
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey(), None);
|
||||
let url = test_validator.rpc_url();
|
||||
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
|
||||
test_process_create_stake_with_client(&client, alice);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_stake_allocations() {
|
||||
let alice = Keypair::new();
|
||||
@ -1293,7 +1470,7 @@ mod tests {
|
||||
const SET_LOCKUP_INDEX: usize = 4;
|
||||
|
||||
#[test]
|
||||
fn test_set_stake_lockup() {
|
||||
fn test_set_split_stake_lockup() {
|
||||
let lockup_date_str = "2021-01-07T00:00:00Z";
|
||||
let allocation = Allocation {
|
||||
recipient: Pubkey::default().to_string(),
|
||||
@ -1303,12 +1480,17 @@ mod tests {
|
||||
let stake_account_address = solana_sdk::pubkey::new_rand();
|
||||
let new_stake_account_address = solana_sdk::pubkey::new_rand();
|
||||
let lockup_authority = Keypair::new();
|
||||
let stake_args = StakeArgs {
|
||||
let lockup_authority_address = lockup_authority.pubkey();
|
||||
let sender_stake_args = SenderStakeArgs {
|
||||
stake_account_address,
|
||||
stake_authority: Box::new(Keypair::new()),
|
||||
withdraw_authority: Box::new(Keypair::new()),
|
||||
lockup_authority: Some(Box::new(lockup_authority)),
|
||||
};
|
||||
let stake_args = StakeArgs {
|
||||
lockup_authority: Some(lockup_authority_address),
|
||||
unlocked_sol: sol_to_lamports(1.0),
|
||||
sender_stake_args: Some(sender_stake_args),
|
||||
};
|
||||
let args = DistributeTokensArgs {
|
||||
fee_payer: Box::new(Keypair::new()),
|
||||
@ -1558,12 +1740,17 @@ mod tests {
|
||||
.send_and_confirm_transaction_with_spinner(&transaction)
|
||||
.unwrap();
|
||||
|
||||
StakeArgs {
|
||||
let sender_stake_args = SenderStakeArgs {
|
||||
stake_account_address,
|
||||
stake_authority: Box::new(stake_authority),
|
||||
withdraw_authority: Box::new(withdraw_authority),
|
||||
lockup_authority: None,
|
||||
};
|
||||
|
||||
StakeArgs {
|
||||
lockup_authority: None,
|
||||
unlocked_sol,
|
||||
sender_stake_args: Some(sender_stake_args),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ base64 = "0.12.3"
|
||||
bincode = "1.3.1"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = "2.33.1"
|
||||
console = "0.11.3"
|
||||
console = "0.14.1"
|
||||
core_affinity = "0.5.10"
|
||||
fd-lock = "2.0.0"
|
||||
indicatif = "0.15.0"
|
||||
|
@ -42,6 +42,8 @@ use {
|
||||
*/
|
||||
const DEFAULT_MAX_LEDGER_SHREDS: u64 = 10_000;
|
||||
|
||||
const DEFAULT_FAUCET_SOL: f64 = 1_000_000.;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Output {
|
||||
None,
|
||||
@ -53,6 +55,7 @@ fn main() {
|
||||
let default_rpc_port = rpc_port::DEFAULT_RPC_PORT.to_string();
|
||||
let default_faucet_port = FAUCET_PORT.to_string();
|
||||
let default_limit_ledger_size = DEFAULT_MAX_LEDGER_SHREDS.to_string();
|
||||
let default_faucet_sol = DEFAULT_FAUCET_SOL.to_string();
|
||||
|
||||
let matches = App::new("solana-test-validator")
|
||||
.about("Test Validator")
|
||||
@ -264,6 +267,17 @@ fn main() {
|
||||
.default_value(default_limit_ledger_size.as_str())
|
||||
.help("Keep this amount of shreds in root slots."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("faucet_sol")
|
||||
.long("faucet-sol")
|
||||
.takes_value(true)
|
||||
.value_name("SOL")
|
||||
.default_value(default_faucet_sol.as_str())
|
||||
.help(
|
||||
"Give the faucet address this much SOL in genesis. \
|
||||
If the ledger already exists then this parameter is silently ignored",
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let cli_config = if let Some(config_file) = matches.value_of("config_file") {
|
||||
@ -429,7 +443,7 @@ fn main() {
|
||||
};
|
||||
let _logger_thread = redirect_stderr_to_file(logfile);
|
||||
|
||||
let faucet_lamports = sol_to_lamports(1_000_000.);
|
||||
let faucet_lamports = sol_to_lamports(value_of(&matches, "faucet_sol").unwrap());
|
||||
let faucet_keypair_file = ledger_path.join("faucet-keypair.json");
|
||||
if !faucet_keypair_file.exists() {
|
||||
write_keypair_file(&Keypair::new(), faucet_keypair_file.to_str().unwrap()).unwrap_or_else(
|
||||
@ -470,6 +484,7 @@ fn main() {
|
||||
("clone_account", "--clone"),
|
||||
("mint_address", "--mint"),
|
||||
("slots_per_epoch", "--slots-per-epoch"),
|
||||
("faucet_sol", "--faucet-sol"),
|
||||
] {
|
||||
if matches.is_present(name) {
|
||||
println!("{} argument ignored, ledger already exists", long);
|
||||
|
Reference in New Issue
Block a user