Compare commits

...

15 Commits

Author SHA1 Message Date
2798271da0 Add memory operation syscalls (backport #16447) (#17648)
* Add memory operation syscalls (#16447)

(cherry picked from commit 2b50529265)

# Conflicts:
#	programs/bpf/Cargo.lock

* Resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2021-06-01 18:43:50 -07:00
dc258cebab Add create-stake command to solana-tokens CLI (#17550) (#17649)
* Add create-stake command to solana-tokens CLI

* Add --unlocked-sol arg

Thanks @CriesofCarrots!

(cherry picked from commit 1b7f8777d6)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2021-06-02 01:16:48 +00:00
4b8c5194c7 Purge expired BlockHeight data from blockstore (backport #17634) (#17641)
* Purge expired BlockHeight data from blockstore (#17634)

* Purge expired BlockHeight data from blockstore

* Also call compact_storage and add comment....

(cherry picked from commit 96cdbfdcc0)

# Conflicts:
#	ledger/src/blockstore_db.rs

* Fix conflict

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-06-02 01:04:11 +00:00
3c7c6dacfb Add BPF rustc option to reduce the optimizations to safer level (#17590) (#17594)
(cherry picked from commit 831e87c65d)

Co-authored-by: Dmitri Makarov <dmakarov@users.noreply.github.com>
2021-05-31 19:15:56 +00:00
e36337a764 Make the sys-tuner oneliner actually copy-pastable (#17615) (#17619)
* Make the sys-tuner oneliner actually copy-pastable

* Use `command -v`

(cherry picked from commit 41975016b9)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-05-31 18:13:47 +00:00
a49856b898 Rework #17486 (backport #17566) (#17597)
* Revert "Improve missing default signer error messaging (#17486)"

This reverts commit 6d40d0d141.

(cherry picked from commit ca8c1c6c42)

* Improve missing default filepath signer error messaging

(cherry picked from commit 06a926f2f4)

* CI: temporarily skip spl downstream build

(cherry picked from commit d01b4f80f9)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-05-31 17:28:06 +00:00
8ca2f52041 Make initialize public (#17605) (#17607)
(cherry picked from commit 2896fc3987)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-05-31 08:51:05 +00:00
2f7f243022 Always bail if program modifies a ro account (backport #17569) (#17584)
* Always bail if program modifies a ro account (#17569)

(cherry picked from commit a3240aebde)

* resolve conflicts

* nudge

Co-authored-by: Jack May <jack@solana.com>
2021-05-28 20:34:10 +00:00
7e443770d7 test-validator: add an arg to control faucet genesis balance (#17582)
(cherry picked from commit 974a96738a)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-05-28 17:50:44 +00:00
8ec09884b8 Revert bpf-tools to version 1.8 because of a codegen bug suspicion (#17568) (#17577)
(cherry picked from commit 2316ddb90a)

Co-authored-by: Dmitri Makarov <dmakarov@users.noreply.github.com>
2021-05-28 11:10:14 +00:00
88c7e636d6 Bump console from 0.11.3 to 0.14.1 (#16301) (#17552)
* Bump console from 0.11.3 to 0.14.1

Bumps [console](https://github.com/mitsuhiko/console) from 0.11.3 to 0.14.1.
- [Release notes](https://github.com/mitsuhiko/console/releases)
- [Changelog](https://github.com/mitsuhiko/console/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/console/compare/0.11.3...0.14.1)

Signed-off-by: dependabot[bot] <support@github.com>

* Update all Cargo lock files

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
(cherry picked from commit ec1a307a7c)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 21:30:17 +00:00
add3fd479d chore: bump chrono-humanize from 0.1.1 to 0.2.1 (#16895) (#17549)
* chore: bump chrono-humanize from 0.1.1 to 0.2.1

Bumps [chrono-humanize](https://gitlab.com/imp/chrono-humanize-rs) from 0.1.1 to 0.2.1.
- [Release notes](https://gitlab.com/imp/chrono-humanize-rs/tags)
- [Commits](https://gitlab.com/imp/chrono-humanize-rs/compare/0.1.1...0.2.1)

Signed-off-by: dependabot[bot] <support@github.com>

* [auto-commit] Update all Cargo lock files

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot-buildkite <anatoly+githubjenkins@solana.io>
(cherry picked from commit 4f74c77146)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 20:37:42 +00:00
70410536b9 Bump serde_bytes from 0.11.4 to 0.11.5 (#16299) (#17546)
* Bump serde_bytes from 0.11.4 to 0.11.5

Bumps [serde_bytes](https://github.com/serde-rs/bytes) from 0.11.4 to 0.11.5.
- [Release notes](https://github.com/serde-rs/bytes/releases)
- [Commits](https://github.com/serde-rs/bytes/compare/0.11.4...0.11.5)

Signed-off-by: dependabot[bot] <support@github.com>

* [auto-commit] Update all Cargo lock files

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot-buildkite <dependabot-buildkite@noreply.solana.com>
(cherry picked from commit 6e2ae68643)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 20:04:59 +00:00
6f3f9b485c Allow configuring testnet slots-per-epoch (#17545) (#17551)
(cherry picked from commit cb1fb28247)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-05-27 19:46:49 +00:00
bd9ce3590d Remove redundant copy from RocksDB get_cf() wrapper (#17529) (#17543)
(cherry picked from commit 983828a2a9)

Co-authored-by: steviez <steven@solana.com>
2021-05-27 18:58:47 +00:00
56 changed files with 1453 additions and 486 deletions

47
Cargo.lock generated
View File

@ -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",

View File

@ -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!(

View File

@ -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"

View File

@ -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"

View File

@ -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();

View File

@ -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()

View File

@ -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,

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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",

View File

@ -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

View File

@ -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"] }

View File

@ -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"

View File

@ -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" }

View File

@ -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 {

View File

@ -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)
}

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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",

View File

@ -78,11 +78,13 @@ fn main() {
"iter",
"many_args",
"mem",
"membuiltins",
"noop",
"panic",
"param_passing",
"rand",
"ro_modify",
"ro_account_modify",
"sanity",
"sha",
"spoof1",

View File

@ -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();
}

View File

@ -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!(),
}

View File

@ -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"]

View 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(())
}

View File

@ -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));
}

View 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();
}

View 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"]

View 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!();

View 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"]

View 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(())
}

View File

@ -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",

View File

@ -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)
);
}

View File

@ -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(),

View File

@ -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

View File

@ -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,

View File

@ -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),

View File

@ -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"

View File

@ -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,

View File

@ -100,5 +100,5 @@ EOF
_ example_helloworld
_ spl
#_ spl
_ serum_dex

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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",

View File

@ -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;

View 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);
}

View File

@ -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);
}
}

View File

@ -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()

View File

@ -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"

View File

@ -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"

View File

@ -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)?)
}

View File

@ -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,

View File

@ -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),
}
}

View File

@ -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"

View File

@ -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);