Drop write lock on sysvars (#15497)

* Drop write lock on sysvars

* adds env var for demoting sysvar write lock demotion

* moves demote logic to is_writable

* feature gates sysvar write lock demotion

* adds builtins to write lock demotion

* adds system program id to builtins

* adds Feature111...

* adds an abi-freeze test

* mvines set of builtin program keys

Co-authored-by: Michael Vines <mvines@gmail.com>

* update tests

* adds bpf loader keys

* Add test sysvar

* Plumb demote_sysvar to is_writable

* more plumbing of demote_sysvar_write_locks to is_writable

* patches test_program_bpf_instruction_introspection

* hard codes demote_sysvar_write_locks to false for serialization/encoding methods

* Revert "hard codes demote_sysvar_write_locks to false for serialization/encoding methods"

This reverts commit ae3e2d2e777437bddd753933097a210dcbc1b1fc.

* change the hardcoded ones to demote_sysvar_write_locks=true

* Use data_as_mut_slice

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
sakridge
2021-03-30 10:05:09 -07:00
committed by GitHub
parent 527adbed34
commit 54c68ea83f
12 changed files with 316 additions and 69 deletions

View File

@ -6,13 +6,34 @@ use crate::serialize_utils::{
append_slice, append_u16, append_u8, read_pubkey, read_slice, read_u16, read_u8,
};
use crate::{
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
hash::Hash,
instruction::{AccountMeta, CompiledInstruction, Instruction},
pubkey::Pubkey,
short_vec, system_instruction,
short_vec, system_instruction, system_program, sysvar,
};
use itertools::Itertools;
use std::convert::TryFrom;
use lazy_static::lazy_static;
use std::{convert::TryFrom, str::FromStr};
lazy_static! {
// Copied keys over since direct references create cyclical dependency.
static ref BUILTIN_PROGRAMS_KEYS: [Pubkey; 10] = {
let parse = |s| Pubkey::from_str(s).unwrap();
[
parse("Config1111111111111111111111111111111111111"),
parse("Feature111111111111111111111111111111111111"),
parse("NativeLoader1111111111111111111111111111111"),
parse("Stake11111111111111111111111111111111111111"),
parse("StakeConfig11111111111111111111111111111111"),
parse("Vote111111111111111111111111111111111111111"),
system_program::id(),
bpf_loader::id(),
bpf_loader_deprecated::id(),
bpf_loader_upgradeable::id(),
]
};
}
fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
keys.iter().position(|k| k == key).unwrap() as u8
@ -326,23 +347,31 @@ impl Message {
self.program_position(i).is_some()
}
pub fn is_writable(&self, i: usize) -> bool {
i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts)
pub fn is_writable(&self, i: usize, demote_sysvar_write_locks: bool) -> bool {
(i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts)
as usize
|| (i >= self.header.num_required_signatures as usize
&& i < self.account_keys.len()
- self.header.num_readonly_unsigned_accounts as usize)
- self.header.num_readonly_unsigned_accounts as usize))
&& !{
let key = self.account_keys[i];
demote_sysvar_write_locks
&& (sysvar::is_sysvar_id(&key) || BUILTIN_PROGRAMS_KEYS.contains(&key))
}
}
pub fn is_signer(&self, i: usize) -> bool {
i < self.header.num_required_signatures as usize
}
pub fn get_account_keys_by_lock_type(&self) -> (Vec<&Pubkey>, Vec<&Pubkey>) {
pub fn get_account_keys_by_lock_type(
&self,
demote_sysvar_write_locks: bool,
) -> (Vec<&Pubkey>, Vec<&Pubkey>) {
let mut writable_keys = vec![];
let mut readonly_keys = vec![];
for (i, key) in self.account_keys.iter().enumerate() {
if self.is_writable(i) {
if self.is_writable(i, demote_sysvar_write_locks) {
writable_keys.push(key);
} else {
readonly_keys.push(key);
@ -364,7 +393,7 @@ impl Message {
// 36..64 - program_id
// 33..34 - data len - u16
// 35..data_len - data
pub fn serialize_instructions(&self) -> Vec<u8> {
pub fn serialize_instructions(&self, demote_sysvar_write_locks: bool) -> Vec<u8> {
// 64 bytes is a reasonable guess, calculating exactly is slower in benchmarks
let mut data = Vec::with_capacity(self.instructions.len() * (32 * 2));
append_u16(&mut data, self.instructions.len() as u16);
@ -379,7 +408,7 @@ impl Message {
for account_index in &instruction.accounts {
let account_index = *account_index as usize;
let is_signer = self.is_signer(account_index);
let is_writable = self.is_writable(account_index);
let is_writable = self.is_writable(account_index, demote_sysvar_write_locks);
let mut meta_byte = 0;
if is_signer {
meta_byte |= 1 << Self::IS_SIGNER_BIT;
@ -459,7 +488,8 @@ impl Message {
#[cfg(test)]
mod tests {
use super::*;
use crate::instruction::AccountMeta;
use crate::{hash, instruction::AccountMeta};
use std::collections::HashSet;
#[test]
fn test_message_unique_program_ids() {
@ -471,6 +501,27 @@ mod tests {
assert_eq!(program_ids, vec![program_id0]);
}
#[test]
fn test_builtin_program_keys() {
let keys: HashSet<Pubkey> = BUILTIN_PROGRAMS_KEYS.iter().copied().collect();
assert_eq!(keys.len(), 10);
for k in keys {
let k = format!("{}", k);
assert!(k.ends_with("11111111111111111111111"));
}
}
#[test]
fn test_builtin_program_keys_abi_freeze() {
// Once the feature is flipped on, we can't further modify
// BUILTIN_PROGRAMS_KEYS without the risk of breaking consensus.
let builtins = format!("{:?}", *BUILTIN_PROGRAMS_KEYS);
assert_eq!(
format!("{}", hash::hash(builtins.as_bytes())),
"ACqmMkYbo9eqK6QrRSrB3HLyR6uHhLf31SCfGUAJjiWj"
);
}
#[test]
fn test_message_unique_program_ids_not_adjacent() {
let program_id0 = Pubkey::default();
@ -796,12 +847,13 @@ mod tests {
recent_blockhash: Hash::default(),
instructions: vec![],
};
assert_eq!(message.is_writable(0), true);
assert_eq!(message.is_writable(1), false);
assert_eq!(message.is_writable(2), false);
assert_eq!(message.is_writable(3), true);
assert_eq!(message.is_writable(4), true);
assert_eq!(message.is_writable(5), false);
let demote_sysvar_write_locks = true;
assert_eq!(message.is_writable(0, demote_sysvar_write_locks), true);
assert_eq!(message.is_writable(1, demote_sysvar_write_locks), false);
assert_eq!(message.is_writable(2, demote_sysvar_write_locks), false);
assert_eq!(message.is_writable(3, demote_sysvar_write_locks), true);
assert_eq!(message.is_writable(4, demote_sysvar_write_locks), true);
assert_eq!(message.is_writable(5, demote_sysvar_write_locks), false);
}
#[test]
@ -829,7 +881,9 @@ mod tests {
Some(&id1),
);
assert_eq!(
message.get_account_keys_by_lock_type(),
message.get_account_keys_by_lock_type(
true, // demote_sysvar_write_locks
),
(vec![&id1, &id0], vec![&id3, &id2, &program_id])
);
}
@ -859,7 +913,9 @@ mod tests {
];
let message = Message::new(&instructions, Some(&id1));
let serialized = message.serialize_instructions();
let serialized = message.serialize_instructions(
true, // demote_sysvar_write_locks
);
for (i, instruction) in instructions.iter().enumerate() {
assert_eq!(
Message::deserialize_instruction(i, &serialized).unwrap(),
@ -880,7 +936,9 @@ mod tests {
];
let message = Message::new(&instructions, Some(&id1));
let serialized = message.serialize_instructions();
let serialized = message.serialize_instructions(
true, // demote_sysvar_write_locks
);
assert_eq!(
Message::deserialize_instruction(instructions.len(), &serialized).unwrap_err(),
SanitizeError::IndexOutOfBounds,