Remove credit-only account handling (#6726)

* Renaming
- credit-only/credit-debit to read-only/read-write
- debitable to writable

* Remove credit handling, making credit-only accounts read-only

* Update programs to remove deprecated credit-only account designation

* Use readonly and writable instead of underscored types
This commit is contained in:
Tyera Eulberg
2019-11-05 09:38:35 -07:00
committed by GitHub
parent cea13e964c
commit c6931dcb07
20 changed files with 344 additions and 621 deletions

View File

@ -109,13 +109,11 @@ impl Account {
}
}
pub type LamportCredit = u64;
#[repr(C)]
#[derive(Debug)]
pub struct KeyedAccount<'a> {
is_signer: bool, // Transaction was signed by this account's key
is_debitable: bool,
is_writable: bool,
key: &'a Pubkey,
pub account: &'a mut Account,
}
@ -133,27 +131,27 @@ impl<'a> KeyedAccount<'a> {
self.key
}
pub fn is_debitable(&self) -> bool {
self.is_debitable
pub fn is_writable(&self) -> bool {
self.is_writable
}
pub fn new(key: &'a Pubkey, is_signer: bool, account: &'a mut Account) -> KeyedAccount<'a> {
KeyedAccount {
is_signer,
is_debitable: true,
is_writable: true,
key,
account,
}
}
pub fn new_credit_only(
pub fn new_readonly(
key: &'a Pubkey,
is_signer: bool,
account: &'a mut Account,
) -> KeyedAccount<'a> {
KeyedAccount {
is_signer,
is_debitable: false,
is_writable: false,
key,
account,
}
@ -164,7 +162,7 @@ impl<'a> From<(&'a Pubkey, &'a mut Account)> for KeyedAccount<'a> {
fn from((key, account): (&'a Pubkey, &'a mut Account)) -> Self {
KeyedAccount {
is_signer: false,
is_debitable: true,
is_writable: true,
key,
account,
}
@ -175,7 +173,7 @@ impl<'a> From<&'a mut (Pubkey, Account)> for KeyedAccount<'a> {
fn from((key, account): &'a mut (Pubkey, Account)) -> Self {
KeyedAccount {
is_signer: false,
is_debitable: true,
is_writable: true,
key,
account,
}
@ -186,12 +184,12 @@ pub fn create_keyed_accounts(accounts: &mut [(Pubkey, Account)]) -> Vec<KeyedAcc
accounts.iter_mut().map(Into::into).collect()
}
pub fn create_keyed_credit_only_accounts(accounts: &mut [(Pubkey, Account)]) -> Vec<KeyedAccount> {
pub fn create_keyed_readonly_accounts(accounts: &mut [(Pubkey, Account)]) -> Vec<KeyedAccount> {
accounts
.iter_mut()
.map(|(key, account)| KeyedAccount {
is_signer: false,
is_debitable: false,
is_writable: false,
key,
account,
})

View File

@ -52,11 +52,11 @@ pub enum InstructionError {
/// Program modified the data of an account that doesn't belong to it
ExternalAccountDataModified,
/// Credit-only account spent lamports
CreditOnlyLamportSpend,
/// Read-only account modified lamports
ReadonlyLamportChange,
/// Credit-only account modified data
CreditOnlyDataModified,
/// Read-only account modified data
ReadonlyDataModified,
/// An account was referenced more than once in a single instruction
DuplicateAccountIndex,
@ -116,8 +116,8 @@ pub struct AccountMeta {
pub pubkey: Pubkey,
/// True if an Instruciton requires a Transaction signature matching `pubkey`.
pub is_signer: bool,
/// True if the `pubkey` can be loaded as a credit-debit account.
pub is_debitable: bool,
/// True if the `pubkey` can be loaded as a read-write account.
pub is_writable: bool,
}
impl AccountMeta {
@ -125,15 +125,15 @@ impl AccountMeta {
Self {
pubkey,
is_signer,
is_debitable: true,
is_writable: true,
}
}
pub fn new_credit_only(pubkey: Pubkey, is_signer: bool) -> Self {
pub fn new_readonly(pubkey: Pubkey, is_signer: bool) -> Self {
Self {
pubkey,
is_signer,
is_debitable: false,
is_writable: false,
}
}
}

View File

@ -30,34 +30,34 @@ fn compile_instructions(ixs: Vec<Instruction>, keys: &[Pubkey]) -> Vec<CompiledI
.collect()
}
/// A helper struct to collect pubkeys referenced by a set of instructions and credit-only counts
/// A helper struct to collect pubkeys referenced by a set of instructions and read-only counts
#[derive(Debug, PartialEq, Eq)]
struct InstructionKeys {
pub signed_keys: Vec<Pubkey>,
pub unsigned_keys: Vec<Pubkey>,
pub num_credit_only_signed_accounts: u8,
pub num_credit_only_unsigned_accounts: u8,
pub num_readonly_signed_accounts: u8,
pub num_readonly_unsigned_accounts: u8,
}
impl InstructionKeys {
fn new(
signed_keys: Vec<Pubkey>,
unsigned_keys: Vec<Pubkey>,
num_credit_only_signed_accounts: u8,
num_credit_only_unsigned_accounts: u8,
num_readonly_signed_accounts: u8,
num_readonly_unsigned_accounts: u8,
) -> Self {
Self {
signed_keys,
unsigned_keys,
num_credit_only_signed_accounts,
num_credit_only_unsigned_accounts,
num_readonly_signed_accounts,
num_readonly_unsigned_accounts,
}
}
}
/// Return pubkeys referenced by all instructions, with the ones needing signatures first. If the
/// payer key is provided, it is always placed first in the list of signed keys. Credit-only signed
/// accounts are placed last in the set of signed accounts. Credit-only unsigned accounts,
/// payer key is provided, it is always placed first in the list of signed keys. Read-only signed
/// accounts are placed last in the set of signed accounts. Read-only unsigned accounts,
/// including program ids, are placed last in the set. No duplicates and order is preserved.
fn get_keys(instructions: &[Instruction], payer: Option<&Pubkey>) -> InstructionKeys {
let programs: Vec<_> = get_program_ids(instructions)
@ -65,7 +65,7 @@ fn get_keys(instructions: &[Instruction], payer: Option<&Pubkey>) -> Instruction
.map(|program_id| AccountMeta {
pubkey: *program_id,
is_signer: false,
is_debitable: false,
is_writable: false,
})
.collect();
let mut keys_and_signed: Vec<_> = instructions
@ -76,7 +76,7 @@ fn get_keys(instructions: &[Instruction], payer: Option<&Pubkey>) -> Instruction
keys_and_signed.sort_by(|x, y| {
y.is_signer
.cmp(&x.is_signer)
.then(y.is_debitable.cmp(&x.is_debitable))
.then(y.is_writable.cmp(&x.is_writable))
});
let payer_account_meta;
@ -84,33 +84,33 @@ fn get_keys(instructions: &[Instruction], payer: Option<&Pubkey>) -> Instruction
payer_account_meta = AccountMeta {
pubkey: *payer,
is_signer: true,
is_debitable: true,
is_writable: true,
};
keys_and_signed.insert(0, &payer_account_meta);
}
let mut signed_keys = vec![];
let mut unsigned_keys = vec![];
let mut num_credit_only_signed_accounts = 0;
let mut num_credit_only_unsigned_accounts = 0;
let mut num_readonly_signed_accounts = 0;
let mut num_readonly_unsigned_accounts = 0;
for account_meta in keys_and_signed.into_iter().unique_by(|x| x.pubkey) {
if account_meta.is_signer {
signed_keys.push(account_meta.pubkey);
if !account_meta.is_debitable {
num_credit_only_signed_accounts += 1;
if !account_meta.is_writable {
num_readonly_signed_accounts += 1;
}
} else {
unsigned_keys.push(account_meta.pubkey);
if !account_meta.is_debitable {
num_credit_only_unsigned_accounts += 1;
if !account_meta.is_writable {
num_readonly_unsigned_accounts += 1;
}
}
}
InstructionKeys::new(
signed_keys,
unsigned_keys,
num_credit_only_signed_accounts,
num_credit_only_unsigned_accounts,
num_readonly_signed_accounts,
num_readonly_unsigned_accounts,
)
}
@ -130,19 +130,19 @@ pub struct MessageHeader {
/// NOTE: Serialization-related changes must be paired with the direct read at sigverify.
pub num_required_signatures: u8,
/// The last num_credit_only_signed_accounts of the signed keys are credit-only accounts.
/// Programs may process multiple transactions that add lamports to the same credit-only
/// account within a single PoH entry, but are not permitted to debit lamports or modify
/// account data. Transactions targeting the same debit account are evaluated sequentially.
pub num_credit_only_signed_accounts: u8,
/// The last num_readonly_signed_accounts of the signed keys are read-only accounts. Programs
/// may process multiple transactions that load read-only accounts within a single PoH entry,
/// but are not permitted to credit or debit lamports or modify account data. Transactions
/// targeting the same read-write account are evaluated sequentially.
pub num_readonly_signed_accounts: u8,
/// The last num_credit_only_unsigned_accounts of the unsigned keys are credit-only accounts.
pub num_credit_only_unsigned_accounts: u8,
/// The last num_readonly_unsigned_accounts of the unsigned keys are read-only accounts.
pub num_readonly_unsigned_accounts: u8,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Message {
/// The message header, identifying signed and credit-only `account_keys`
/// The message header, identifying signed and read-only `account_keys`
/// NOTE: Serialization-related changes must be paired with the direct read at sigverify.
pub header: MessageHeader,
@ -162,8 +162,8 @@ pub struct Message {
impl Message {
pub fn new_with_compiled_instructions(
num_required_signatures: u8,
num_credit_only_signed_accounts: u8,
num_credit_only_unsigned_accounts: u8,
num_readonly_signed_accounts: u8,
num_readonly_unsigned_accounts: u8,
account_keys: Vec<Pubkey>,
recent_blockhash: Hash,
instructions: Vec<CompiledInstruction>,
@ -171,8 +171,8 @@ impl Message {
Self {
header: MessageHeader {
num_required_signatures,
num_credit_only_signed_accounts,
num_credit_only_unsigned_accounts,
num_readonly_signed_accounts,
num_readonly_unsigned_accounts,
},
account_keys,
recent_blockhash,
@ -188,16 +188,16 @@ impl Message {
let InstructionKeys {
mut signed_keys,
unsigned_keys,
num_credit_only_signed_accounts,
num_credit_only_unsigned_accounts,
num_readonly_signed_accounts,
num_readonly_unsigned_accounts,
} = get_keys(&instructions, payer);
let num_required_signatures = signed_keys.len() as u8;
signed_keys.extend(&unsigned_keys);
let instructions = compile_instructions(instructions, &signed_keys);
Self::new_with_compiled_instructions(
num_required_signatures,
num_credit_only_signed_accounts,
num_credit_only_unsigned_accounts,
num_readonly_signed_accounts,
num_readonly_unsigned_accounts,
signed_keys,
Hash::default(),
instructions,
@ -218,25 +218,25 @@ impl Message {
.position(|&&pubkey| pubkey == self.account_keys[index])
}
pub fn is_debitable(&self, i: usize) -> bool {
i < (self.header.num_required_signatures - self.header.num_credit_only_signed_accounts)
pub fn is_writable(&self, i: usize) -> 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_credit_only_unsigned_accounts as usize)
- self.header.num_readonly_unsigned_accounts as usize)
}
pub fn get_account_keys_by_lock_type(&self) -> (Vec<&Pubkey>, Vec<&Pubkey>) {
let mut credit_debit_keys = vec![];
let mut credit_only_keys = vec![];
let mut writable_keys = vec![];
let mut readonly_keys = vec![];
for (i, key) in self.account_keys.iter().enumerate() {
if self.is_debitable(i) {
credit_debit_keys.push(key);
if self.is_writable(i) {
writable_keys.push(key);
} else {
credit_only_keys.push(key);
readonly_keys.push(key);
}
}
(credit_debit_keys, credit_only_keys)
(writable_keys, readonly_keys)
}
}
@ -399,7 +399,7 @@ mod tests {
}
#[test]
fn test_message_credit_only_keys_last() {
fn test_message_readonly_keys_last() {
let program_id = Pubkey::default();
let id0 = Pubkey::default(); // Identical key/program_id should be de-duped
let id1 = Pubkey::new_rand();
@ -407,16 +407,8 @@ mod tests {
let id3 = Pubkey::new_rand();
let keys = get_keys(
&[
Instruction::new(
program_id,
&0,
vec![AccountMeta::new_credit_only(id0, false)],
),
Instruction::new(
program_id,
&0,
vec![AccountMeta::new_credit_only(id1, true)],
),
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id1, true)]),
Instruction::new(program_id, &0, vec![AccountMeta::new(id2, false)]),
Instruction::new(program_id, &0, vec![AccountMeta::new(id3, true)]),
],
@ -484,16 +476,8 @@ mod tests {
let id1 = Pubkey::new_rand();
let keys = get_keys(
&[
Instruction::new(
program_id,
&0,
vec![AccountMeta::new_credit_only(id0, false)],
),
Instruction::new(
program_id,
&0,
vec![AccountMeta::new_credit_only(id1, true)],
),
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id1, true)]),
],
None,
);
@ -518,7 +502,7 @@ mod tests {
}
#[test]
fn test_is_debitable() {
fn test_is_writable() {
let key0 = Pubkey::new_rand();
let key1 = Pubkey::new_rand();
let key2 = Pubkey::new_rand();
@ -529,19 +513,19 @@ mod tests {
let message = Message {
header: MessageHeader {
num_required_signatures: 3,
num_credit_only_signed_accounts: 2,
num_credit_only_unsigned_accounts: 1,
num_readonly_signed_accounts: 2,
num_readonly_unsigned_accounts: 1,
},
account_keys: vec![key0, key1, key2, key3, key4, key5],
recent_blockhash: Hash::default(),
instructions: vec![],
};
assert_eq!(message.is_debitable(0), true);
assert_eq!(message.is_debitable(1), false);
assert_eq!(message.is_debitable(2), false);
assert_eq!(message.is_debitable(3), true);
assert_eq!(message.is_debitable(4), true);
assert_eq!(message.is_debitable(5), false);
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);
}
#[test]
@ -554,16 +538,8 @@ mod tests {
let message = Message::new(vec![
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
Instruction::new(program_id, &0, vec![AccountMeta::new(id1, true)]),
Instruction::new(
program_id,
&0,
vec![AccountMeta::new_credit_only(id2, false)],
),
Instruction::new(
program_id,
&0,
vec![AccountMeta::new_credit_only(id3, true)],
),
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id2, false)]),
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id3, true)]),
]);
assert_eq!(
message.get_account_keys_by_lock_type(),

View File

@ -418,12 +418,12 @@ mod tests {
let len_size = 1;
let num_required_sigs_size = 1;
let num_credit_only_accounts_size = 2;
let num_readonly_accounts_size = 2;
let blockhash_size = size_of::<Hash>();
let expected_transaction_size = len_size
+ (tx.signatures.len() * size_of::<Signature>())
+ num_required_sigs_size
+ num_credit_only_accounts_size
+ num_readonly_accounts_size
+ len_size
+ (tx.message.account_keys.len() * size_of::<Pubkey>())
+ blockhash_size