* Migrate from address maps to address lookup tables
* update sanitize error
* cargo fmt
* update abi
(cherry picked from commit 6c108c8fc3)
Co-authored-by: Justin Starry <justin@solana.com>
			
			
This commit is contained in:
		| @@ -113,9 +113,10 @@ CREATE TYPE "TransactionMessage" AS ( | |||||||
|     instructions "CompiledInstruction"[] |     instructions "CompiledInstruction"[] | ||||||
| ); | ); | ||||||
|  |  | ||||||
| CREATE TYPE "AddressMapIndexes" AS ( | CREATE TYPE "TransactionMessageAddressTableLookup" AS ( | ||||||
|     writable SMALLINT[], |     account_key: BYTEA[], | ||||||
|     readonly SMALLINT[] |     writable_indexes SMALLINT[], | ||||||
|  |     readonly_indexes SMALLINT[] | ||||||
| ); | ); | ||||||
|  |  | ||||||
| CREATE TYPE "TransactionMessageV0" AS ( | CREATE TYPE "TransactionMessageV0" AS ( | ||||||
| @@ -123,17 +124,17 @@ CREATE TYPE "TransactionMessageV0" AS ( | |||||||
|     account_keys BYTEA[], |     account_keys BYTEA[], | ||||||
|     recent_blockhash BYTEA, |     recent_blockhash BYTEA, | ||||||
|     instructions "CompiledInstruction"[], |     instructions "CompiledInstruction"[], | ||||||
|     address_map_indexes "AddressMapIndexes"[] |     address_table_lookups "TransactionMessageAddressTableLookup"[] | ||||||
| ); | ); | ||||||
|  |  | ||||||
| CREATE TYPE "MappedAddresses" AS ( | CREATE TYPE "LoadedAddresses" AS ( | ||||||
|     writable BYTEA[], |     writable BYTEA[], | ||||||
|     readonly BYTEA[] |     readonly BYTEA[] | ||||||
| ); | ); | ||||||
|  |  | ||||||
| CREATE TYPE "MappedMessage" AS ( | CREATE TYPE "LoadedMessageV0" AS ( | ||||||
|     message "TransactionMessageV0", |     message "TransactionMessageV0", | ||||||
|     mapped_addresses "MappedAddresses" |     loaded_addresses "LoadedAddresses" | ||||||
| ); | ); | ||||||
|  |  | ||||||
| -- The table storing transactions | -- The table storing transactions | ||||||
| @@ -143,7 +144,7 @@ CREATE TABLE transaction ( | |||||||
|     is_vote BOOL NOT NULL, |     is_vote BOOL NOT NULL, | ||||||
|     message_type SMALLINT, -- 0: legacy, 1: v0 message |     message_type SMALLINT, -- 0: legacy, 1: v0 message | ||||||
|     legacy_message "TransactionMessage", |     legacy_message "TransactionMessage", | ||||||
|     v0_mapped_message "MappedMessage", |     v0_loaded_message "LoadedMessageV0", | ||||||
|     signatures BYTEA[], |     signatures BYTEA[], | ||||||
|     message_hash BYTEA, |     message_hash BYTEA, | ||||||
|     meta "TransactionStatusMeta", |     meta "TransactionStatusMeta", | ||||||
|   | |||||||
| @@ -11,12 +11,12 @@ DROP TABLE transaction; | |||||||
|  |  | ||||||
| DROP TYPE "TransactionError" CASCADE; | DROP TYPE "TransactionError" CASCADE; | ||||||
| DROP TYPE "TransactionErrorCode" CASCADE; | DROP TYPE "TransactionErrorCode" CASCADE; | ||||||
| DROP TYPE "MappedMessage" CASCADE; | DROP TYPE "LoadedMessageV0" CASCADE; | ||||||
| DROP TYPE "MappedAddresses" CASCADE; | DROP TYPE "LoadedAddresses" CASCADE; | ||||||
| DROP TYPE "TransactionMessageV0" CASCADE; | DROP TYPE "TransactionMessageV0" CASCADE; | ||||||
| DROP TYPE "AddressMapIndexes" CASCADE; |  | ||||||
| DROP TYPE "TransactionMessage" CASCADE; | DROP TYPE "TransactionMessage" CASCADE; | ||||||
| DROP TYPE "TransactionMessageHeader" CASCADE; | DROP TYPE "TransactionMessageHeader" CASCADE; | ||||||
|  | DROP TYPE "TransactionMessageAddressTableLookup" CASCADE; | ||||||
| DROP TYPE "TransactionStatusMeta" CASCADE; | DROP TYPE "TransactionStatusMeta" CASCADE; | ||||||
| DROP TYPE "RewardType" CASCADE; | DROP TYPE "RewardType" CASCADE; | ||||||
| DROP TYPE "Reward" CASCADE; | DROP TYPE "Reward" CASCADE; | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ use { | |||||||
|     solana_sdk::{ |     solana_sdk::{ | ||||||
|         instruction::CompiledInstruction, |         instruction::CompiledInstruction, | ||||||
|         message::{ |         message::{ | ||||||
|             v0::{self, AddressMapIndexes}, |             v0::{self, LoadedAddresses, MessageAddressTableLookup}, | ||||||
|             MappedAddresses, MappedMessage, Message, MessageHeader, SanitizedMessage, |             Message, MessageHeader, SanitizedMessage, | ||||||
|         }, |         }, | ||||||
|         transaction::TransactionError, |         transaction::TransactionError, | ||||||
|     }, |     }, | ||||||
| @@ -105,10 +105,11 @@ pub struct DbTransactionMessage { | |||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, Debug, ToSql)] | #[derive(Clone, Debug, ToSql)] | ||||||
| #[postgres(name = "AddressMapIndexes")] | #[postgres(name = "TransactionMessageAddressTableLookup")] | ||||||
| pub struct DbAddressMapIndexes { | pub struct DbTransactionMessageAddressTableLookup { | ||||||
|     pub writable: Vec<i16>, |     pub account_key: Vec<u8>, | ||||||
|     pub readonly: Vec<i16>, |     pub writable_indexes: Vec<i16>, | ||||||
|  |     pub readonly_indexes: Vec<i16>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, Debug, ToSql)] | #[derive(Clone, Debug, ToSql)] | ||||||
| @@ -118,21 +119,21 @@ pub struct DbTransactionMessageV0 { | |||||||
|     pub account_keys: Vec<Vec<u8>>, |     pub account_keys: Vec<Vec<u8>>, | ||||||
|     pub recent_blockhash: Vec<u8>, |     pub recent_blockhash: Vec<u8>, | ||||||
|     pub instructions: Vec<DbCompiledInstruction>, |     pub instructions: Vec<DbCompiledInstruction>, | ||||||
|     pub address_map_indexes: Vec<DbAddressMapIndexes>, |     pub address_table_lookups: Vec<DbTransactionMessageAddressTableLookup>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, Debug, ToSql)] | #[derive(Clone, Debug, ToSql)] | ||||||
| #[postgres(name = "MappedAddresses")] | #[postgres(name = "LoadedAddresses")] | ||||||
| pub struct DbMappedAddresses { | pub struct DbLoadedAddresses { | ||||||
|     pub writable: Vec<Vec<u8>>, |     pub writable: Vec<Vec<u8>>, | ||||||
|     pub readonly: Vec<Vec<u8>>, |     pub readonly: Vec<Vec<u8>>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, Debug, ToSql)] | #[derive(Clone, Debug, ToSql)] | ||||||
| #[postgres(name = "MappedMessage")] | #[postgres(name = "LoadedMessageV0")] | ||||||
| pub struct DbMappedMessage { | pub struct DbLoadedMessageV0 { | ||||||
|     pub message: DbTransactionMessageV0, |     pub message: DbTransactionMessageV0, | ||||||
|     pub mapped_addresses: DbMappedAddresses, |     pub loaded_addresses: DbLoadedAddresses, | ||||||
| } | } | ||||||
|  |  | ||||||
| pub struct DbTransaction { | pub struct DbTransaction { | ||||||
| @@ -141,7 +142,7 @@ pub struct DbTransaction { | |||||||
|     pub slot: i64, |     pub slot: i64, | ||||||
|     pub message_type: i16, |     pub message_type: i16, | ||||||
|     pub legacy_message: Option<DbTransactionMessage>, |     pub legacy_message: Option<DbTransactionMessage>, | ||||||
|     pub v0_mapped_message: Option<DbMappedMessage>, |     pub v0_loaded_message: Option<DbLoadedMessageV0>, | ||||||
|     pub message_hash: Vec<u8>, |     pub message_hash: Vec<u8>, | ||||||
|     pub meta: DbTransactionStatusMeta, |     pub meta: DbTransactionStatusMeta, | ||||||
|     pub signatures: Vec<Vec<u8>>, |     pub signatures: Vec<Vec<u8>>, | ||||||
| @@ -151,32 +152,33 @@ pub struct LogTransactionRequest { | |||||||
|     pub transaction_info: DbTransaction, |     pub transaction_info: DbTransaction, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<&AddressMapIndexes> for DbAddressMapIndexes { | impl From<&MessageAddressTableLookup> for DbTransactionMessageAddressTableLookup { | ||||||
|     fn from(address_map_indexes: &AddressMapIndexes) -> Self { |     fn from(address_table_lookup: &MessageAddressTableLookup) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             writable: address_map_indexes |             account_key: address_table_lookup.account_key.as_ref().to_vec(), | ||||||
|                 .writable |             writable_indexes: address_table_lookup | ||||||
|  |                 .writable_indexes | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .map(|address_idx| *address_idx as i16) |                 .map(|idx| *idx as i16) | ||||||
|                 .collect(), |                 .collect(), | ||||||
|             readonly: address_map_indexes |             readonly_indexes: address_table_lookup | ||||||
|                 .readonly |                 .readonly_indexes | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .map(|address_idx| *address_idx as i16) |                 .map(|idx| *idx as i16) | ||||||
|                 .collect(), |                 .collect(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<&MappedAddresses> for DbMappedAddresses { | impl From<&LoadedAddresses> for DbLoadedAddresses { | ||||||
|     fn from(mapped_addresses: &MappedAddresses) -> Self { |     fn from(loaded_addresses: &LoadedAddresses) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             writable: mapped_addresses |             writable: loaded_addresses | ||||||
|                 .writable |                 .writable | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .map(|pubkey| pubkey.as_ref().to_vec()) |                 .map(|pubkey| pubkey.as_ref().to_vec()) | ||||||
|                 .collect(), |                 .collect(), | ||||||
|             readonly: mapped_addresses |             readonly: loaded_addresses | ||||||
|                 .readonly |                 .readonly | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .map(|pubkey| pubkey.as_ref().to_vec()) |                 .map(|pubkey| pubkey.as_ref().to_vec()) | ||||||
| @@ -243,20 +245,20 @@ impl From<&v0::Message> for DbTransactionMessageV0 { | |||||||
|                 .iter() |                 .iter() | ||||||
|                 .map(DbCompiledInstruction::from) |                 .map(DbCompiledInstruction::from) | ||||||
|                 .collect(), |                 .collect(), | ||||||
|             address_map_indexes: message |             address_table_lookups: message | ||||||
|                 .address_map_indexes |                 .address_table_lookups | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .map(DbAddressMapIndexes::from) |                 .map(DbTransactionMessageAddressTableLookup::from) | ||||||
|                 .collect(), |                 .collect(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<&MappedMessage> for DbMappedMessage { | impl From<&v0::LoadedMessage> for DbLoadedMessageV0 { | ||||||
|     fn from(message: &MappedMessage) -> Self { |     fn from(message: &v0::LoadedMessage) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             message: DbTransactionMessageV0::from(&message.message), |             message: DbTransactionMessageV0::from(&message.message), | ||||||
|             mapped_addresses: DbMappedAddresses::from(&message.mapped_addresses), |             loaded_addresses: DbLoadedAddresses::from(&message.loaded_addresses), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -460,8 +462,8 @@ fn build_db_transaction(slot: u64, transaction_info: &ReplicaTransactionInfo) -> | |||||||
|             } |             } | ||||||
|             _ => None, |             _ => None, | ||||||
|         }, |         }, | ||||||
|         v0_mapped_message: match transaction_info.transaction.message() { |         v0_loaded_message: match transaction_info.transaction.message() { | ||||||
|             SanitizedMessage::V0(mapped_message) => Some(DbMappedMessage::from(mapped_message)), |             SanitizedMessage::V0(loaded_message) => Some(DbLoadedMessageV0::from(loaded_message)), | ||||||
|             _ => None, |             _ => None, | ||||||
|         }, |         }, | ||||||
|         signatures: transaction_info |         signatures: transaction_info | ||||||
| @@ -485,7 +487,7 @@ impl SimplePostgresClient { | |||||||
|         config: &AccountsDbPluginPostgresConfig, |         config: &AccountsDbPluginPostgresConfig, | ||||||
|     ) -> Result<Statement, AccountsDbPluginError> { |     ) -> Result<Statement, AccountsDbPluginError> { | ||||||
|         let stmt = "INSERT INTO transaction AS txn (signature, is_vote, slot, message_type, legacy_message, \ |         let stmt = "INSERT INTO transaction AS txn (signature, is_vote, slot, message_type, legacy_message, \ | ||||||
|         v0_mapped_message, signatures, message_hash, meta, updated_on) \ |         v0_loaded_message, signatures, message_hash, meta, updated_on) \ | ||||||
|         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"; |         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"; | ||||||
|  |  | ||||||
|         let stmt = client.prepare(stmt); |         let stmt = client.prepare(stmt); | ||||||
| @@ -521,7 +523,7 @@ impl SimplePostgresClient { | |||||||
|                 &transaction_info.slot, |                 &transaction_info.slot, | ||||||
|                 &transaction_info.message_type, |                 &transaction_info.message_type, | ||||||
|                 &transaction_info.legacy_message, |                 &transaction_info.legacy_message, | ||||||
|                 &transaction_info.v0_mapped_message, |                 &transaction_info.v0_loaded_message, | ||||||
|                 &transaction_info.signatures, |                 &transaction_info.signatures, | ||||||
|                 &transaction_info.message_hash, |                 &transaction_info.message_hash, | ||||||
|                 &transaction_info.meta, |                 &transaction_info.meta, | ||||||
| @@ -670,42 +672,44 @@ pub(crate) mod tests { | |||||||
|         check_inner_instructions_equality(&inner_instructions, &db_inner_instructions); |         check_inner_instructions_equality(&inner_instructions, &db_inner_instructions); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn check_address_map_indexes_equality( |     fn check_address_table_lookups_equality( | ||||||
|         address_map_indexes: &AddressMapIndexes, |         address_table_lookups: &MessageAddressTableLookup, | ||||||
|         db_address_map_indexes: &DbAddressMapIndexes, |         db_address_table_lookups: &DbTransactionMessageAddressTableLookup, | ||||||
|     ) { |     ) { | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             address_map_indexes.writable.len(), |             address_table_lookups.writable_indexes.len(), | ||||||
|             db_address_map_indexes.writable.len() |             db_address_table_lookups.writable_indexes.len() | ||||||
|         ); |         ); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             address_map_indexes.readonly.len(), |             address_table_lookups.readonly_indexes.len(), | ||||||
|             db_address_map_indexes.readonly.len() |             db_address_table_lookups.readonly_indexes.len() | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         for i in 0..address_map_indexes.writable.len() { |         for i in 0..address_table_lookups.writable_indexes.len() { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 address_map_indexes.writable[i], |                 address_table_lookups.writable_indexes[i], | ||||||
|                 db_address_map_indexes.writable[i] as u8 |                 db_address_table_lookups.writable_indexes[i] as u8 | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|         for i in 0..address_map_indexes.readonly.len() { |         for i in 0..address_table_lookups.readonly_indexes.len() { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 address_map_indexes.readonly[i], |                 address_table_lookups.readonly_indexes[i], | ||||||
|                 db_address_map_indexes.readonly[i] as u8 |                 db_address_table_lookups.readonly_indexes[i] as u8 | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_transform_address_map_indexes() { |     fn test_transform_address_table_lookups() { | ||||||
|         let address_map_indexes = AddressMapIndexes { |         let address_table_lookups = MessageAddressTableLookup { | ||||||
|             writable: vec![1, 2, 3], |             account_key: Pubkey::new_unique(), | ||||||
|             readonly: vec![4, 5, 6], |             writable_indexes: vec![1, 2, 3], | ||||||
|  |             readonly_indexes: vec![4, 5, 6], | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let db_address_map_indexes = DbAddressMapIndexes::from(&address_map_indexes); |         let db_address_table_lookups = | ||||||
|         check_address_map_indexes_equality(&address_map_indexes, &db_address_map_indexes); |             DbTransactionMessageAddressTableLookup::from(&address_table_lookups); | ||||||
|  |         check_address_table_lookups_equality(&address_table_lookups, &db_address_table_lookups); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn check_reward_equality(reward: &Reward, db_reward: &DbReward) { |     fn check_reward_equality(reward: &Reward, db_reward: &DbReward) { | ||||||
| @@ -1089,7 +1093,7 @@ pub(crate) mod tests { | |||||||
|         check_transaction_message_equality(&message, &db_message); |         check_transaction_message_equality(&message, &db_message); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn check_transaction_messagev0_equality( |     fn check_transaction_message_v0_equality( | ||||||
|         message: &v0::Message, |         message: &v0::Message, | ||||||
|         db_message: &DbTransactionMessageV0, |         db_message: &DbTransactionMessageV0, | ||||||
|     ) { |     ) { | ||||||
| @@ -1106,18 +1110,18 @@ pub(crate) mod tests { | |||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             message.address_map_indexes.len(), |             message.address_table_lookups.len(), | ||||||
|             db_message.address_map_indexes.len() |             db_message.address_table_lookups.len() | ||||||
|         ); |         ); | ||||||
|         for i in 0..message.address_map_indexes.len() { |         for i in 0..message.address_table_lookups.len() { | ||||||
|             check_address_map_indexes_equality( |             check_address_table_lookups_equality( | ||||||
|                 &message.address_map_indexes[i], |                 &message.address_table_lookups[i], | ||||||
|                 &db_message.address_map_indexes[i], |                 &db_message.address_table_lookups[i], | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn build_transaction_messagev0() -> v0::Message { |     fn build_transaction_message_v0() -> v0::Message { | ||||||
|         v0::Message { |         v0::Message { | ||||||
|             header: MessageHeader { |             header: MessageHeader { | ||||||
|                 num_readonly_signed_accounts: 2, |                 num_readonly_signed_accounts: 2, | ||||||
| @@ -1144,71 +1148,76 @@ pub(crate) mod tests { | |||||||
|                     data: vec![14, 15, 16], |                     data: vec![14, 15, 16], | ||||||
|                 }, |                 }, | ||||||
|             ], |             ], | ||||||
|             address_map_indexes: vec![ |             address_table_lookups: vec![ | ||||||
|                 AddressMapIndexes { |                 MessageAddressTableLookup { | ||||||
|                     writable: vec![0], |                     account_key: Pubkey::new_unique(), | ||||||
|                     readonly: vec![1, 2], |                     writable_indexes: vec![0], | ||||||
|  |                     readonly_indexes: vec![1, 2], | ||||||
|                 }, |                 }, | ||||||
|                 AddressMapIndexes { |                 MessageAddressTableLookup { | ||||||
|                     writable: vec![1], |                     account_key: Pubkey::new_unique(), | ||||||
|                     readonly: vec![0, 2], |                     writable_indexes: vec![1], | ||||||
|  |                     readonly_indexes: vec![0, 2], | ||||||
|                 }, |                 }, | ||||||
|             ], |             ], | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_transform_transaction_messagev0() { |     fn test_transform_transaction_message_v0() { | ||||||
|         let message = build_transaction_messagev0(); |         let message = build_transaction_message_v0(); | ||||||
|  |  | ||||||
|         let db_message = DbTransactionMessageV0::from(&message); |         let db_message = DbTransactionMessageV0::from(&message); | ||||||
|         check_transaction_messagev0_equality(&message, &db_message); |         check_transaction_message_v0_equality(&message, &db_message); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn check_mapped_addresses( |     fn check_loaded_addresses( | ||||||
|         mapped_addresses: &MappedAddresses, |         loaded_addresses: &LoadedAddresses, | ||||||
|         db_mapped_addresses: &DbMappedAddresses, |         db_loaded_addresses: &DbLoadedAddresses, | ||||||
|     ) { |     ) { | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             mapped_addresses.writable.len(), |             loaded_addresses.writable.len(), | ||||||
|             db_mapped_addresses.writable.len() |             db_loaded_addresses.writable.len() | ||||||
|         ); |         ); | ||||||
|         for i in 0..mapped_addresses.writable.len() { |         for i in 0..loaded_addresses.writable.len() { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 mapped_addresses.writable[i].as_ref(), |                 loaded_addresses.writable[i].as_ref(), | ||||||
|                 db_mapped_addresses.writable[i] |                 db_loaded_addresses.writable[i] | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             mapped_addresses.readonly.len(), |             loaded_addresses.readonly.len(), | ||||||
|             db_mapped_addresses.readonly.len() |             db_loaded_addresses.readonly.len() | ||||||
|         ); |         ); | ||||||
|         for i in 0..mapped_addresses.readonly.len() { |         for i in 0..loaded_addresses.readonly.len() { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 mapped_addresses.readonly[i].as_ref(), |                 loaded_addresses.readonly[i].as_ref(), | ||||||
|                 db_mapped_addresses.readonly[i] |                 db_loaded_addresses.readonly[i] | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn check_mapped_message_equality(message: &MappedMessage, db_message: &DbMappedMessage) { |     fn check_loaded_message_v0_equality( | ||||||
|         check_transaction_messagev0_equality(&message.message, &db_message.message); |         message: &v0::LoadedMessage, | ||||||
|         check_mapped_addresses(&message.mapped_addresses, &db_message.mapped_addresses); |         db_message: &DbLoadedMessageV0, | ||||||
|  |     ) { | ||||||
|  |         check_transaction_message_v0_equality(&message.message, &db_message.message); | ||||||
|  |         check_loaded_addresses(&message.loaded_addresses, &db_message.loaded_addresses); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_transform_mapped_message() { |     fn test_transform_loaded_message_v0() { | ||||||
|         let message = MappedMessage { |         let message = v0::LoadedMessage { | ||||||
|             message: build_transaction_messagev0(), |             message: build_transaction_message_v0(), | ||||||
|             mapped_addresses: MappedAddresses { |             loaded_addresses: LoadedAddresses { | ||||||
|                 writable: vec![Pubkey::new_unique(), Pubkey::new_unique()], |                 writable: vec![Pubkey::new_unique(), Pubkey::new_unique()], | ||||||
|                 readonly: vec![Pubkey::new_unique(), Pubkey::new_unique()], |                 readonly: vec![Pubkey::new_unique(), Pubkey::new_unique()], | ||||||
|             }, |             }, | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let db_message = DbMappedMessage::from(&message); |         let db_message = DbLoadedMessageV0::from(&message); | ||||||
|         check_mapped_message_equality(&message, &db_message); |         check_loaded_message_v0_equality(&message, &db_message); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn check_transaction( |     fn check_transaction( | ||||||
| @@ -1229,9 +1238,9 @@ pub(crate) mod tests { | |||||||
|             } |             } | ||||||
|             SanitizedMessage::V0(message) => { |             SanitizedMessage::V0(message) => { | ||||||
|                 assert_eq!(db_transaction.message_type, 1); |                 assert_eq!(db_transaction.message_type, 1); | ||||||
|                 check_mapped_message_equality( |                 check_loaded_message_v0_equality( | ||||||
|                     message, |                     message, | ||||||
|                     db_transaction.v0_mapped_message.as_ref().unwrap(), |                     db_transaction.v0_loaded_message.as_ref().unwrap(), | ||||||
|                 ); |                 ); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1298,7 +1307,7 @@ pub(crate) mod tests { | |||||||
|                 Signature::new(&[2u8; 64]), |                 Signature::new(&[2u8; 64]), | ||||||
|                 Signature::new(&[3u8; 64]), |                 Signature::new(&[3u8; 64]), | ||||||
|             ], |             ], | ||||||
|             message: VersionedMessage::V0(build_transaction_messagev0()), |             message: VersionedMessage::V0(build_transaction_message_v0()), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1313,7 +1322,7 @@ pub(crate) mod tests { | |||||||
|  |  | ||||||
|         let transaction = |         let transaction = | ||||||
|             SanitizedTransaction::try_create(transaction, message_hash, Some(true), |_message| { |             SanitizedTransaction::try_create(transaction, message_hash, Some(true), |_message| { | ||||||
|                 Ok(MappedAddresses { |                 Ok(LoadedAddresses { | ||||||
|                     writable: vec![Pubkey::new_unique(), Pubkey::new_unique()], |                     writable: vec![Pubkey::new_unique(), Pubkey::new_unique()], | ||||||
|                     readonly: vec![Pubkey::new_unique(), Pubkey::new_unique()], |                     readonly: vec![Pubkey::new_unique(), Pubkey::new_unique()], | ||||||
|                 }) |                 }) | ||||||
|   | |||||||
| @@ -5,12 +5,10 @@ pub mod legacy; | |||||||
| #[cfg(not(target_arch = "bpf"))] | #[cfg(not(target_arch = "bpf"))] | ||||||
| #[path = ""] | #[path = ""] | ||||||
| mod non_bpf_modules { | mod non_bpf_modules { | ||||||
|     mod mapped; |  | ||||||
|     mod sanitized; |     mod sanitized; | ||||||
|     pub mod v0; |  | ||||||
|     mod versions; |     mod versions; | ||||||
|  |  | ||||||
|     pub use {mapped::*, sanitized::*, versions::*}; |     pub use {sanitized::*, versions::*}; | ||||||
| } | } | ||||||
|  |  | ||||||
| pub use legacy::Message; | pub use legacy::Message; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ use { | |||||||
|     crate::{ |     crate::{ | ||||||
|         hash::Hash, |         hash::Hash, | ||||||
|         instruction::{CompiledInstruction, Instruction}, |         instruction::{CompiledInstruction, Instruction}, | ||||||
|         message::{MappedAddresses, MappedMessage, Message, MessageHeader}, |         message::{v0::{self, LoadedAddresses}, legacy::Message as LegacyMessage, MessageHeader}, | ||||||
|         pubkey::Pubkey, |         pubkey::Pubkey, | ||||||
|         sanitize::{Sanitize, SanitizeError}, |         sanitize::{Sanitize, SanitizeError}, | ||||||
|         serialize_utils::{append_slice, append_u16, append_u8}, |         serialize_utils::{append_slice, append_u16, append_u8}, | ||||||
| @@ -17,9 +17,9 @@ use { | |||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
| pub enum SanitizedMessage { | pub enum SanitizedMessage { | ||||||
|     /// Sanitized legacy message |     /// Sanitized legacy message | ||||||
|     Legacy(Message), |     Legacy(LegacyMessage), | ||||||
|     /// Sanitized version #0 message with mapped addresses |     /// Sanitized version #0 message with mapped addresses | ||||||
|     V0(MappedMessage), |     V0(v0::LoadedMessage), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(PartialEq, Debug, Error, Eq, Clone)] | #[derive(PartialEq, Debug, Error, Eq, Clone)] | ||||||
| @@ -44,9 +44,9 @@ impl From<SanitizeError> for SanitizeMessageError { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl TryFrom<Message> for SanitizedMessage { | impl TryFrom<LegacyMessage> for SanitizedMessage { | ||||||
|     type Error = SanitizeMessageError; |     type Error = SanitizeMessageError; | ||||||
|     fn try_from(message: Message) -> Result<Self, Self::Error> { |     fn try_from(message: LegacyMessage) -> Result<Self, Self::Error> { | ||||||
|         message.sanitize()?; |         message.sanitize()?; | ||||||
|  |  | ||||||
|         let sanitized_msg = Self::Legacy(message); |         let sanitized_msg = Self::Legacy(message); | ||||||
| @@ -80,12 +80,12 @@ impl SanitizedMessage { | |||||||
|     pub fn header(&self) -> &MessageHeader { |     pub fn header(&self) -> &MessageHeader { | ||||||
|         match self { |         match self { | ||||||
|             Self::Legacy(message) => &message.header, |             Self::Legacy(message) => &message.header, | ||||||
|             Self::V0(mapped_msg) => &mapped_msg.message.header, |             Self::V0(message) => &message.header, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns a legacy message if this sanitized message wraps one |     /// Returns a legacy message if this sanitized message wraps one | ||||||
|     pub fn legacy_message(&self) -> Option<&Message> { |     pub fn legacy_message(&self) -> Option<&LegacyMessage> { | ||||||
|         if let Self::Legacy(message) = &self { |         if let Self::Legacy(message) = &self { | ||||||
|             Some(message) |             Some(message) | ||||||
|         } else { |         } else { | ||||||
| @@ -103,7 +103,7 @@ impl SanitizedMessage { | |||||||
|     pub fn recent_blockhash(&self) -> &Hash { |     pub fn recent_blockhash(&self) -> &Hash { | ||||||
|         match self { |         match self { | ||||||
|             Self::Legacy(message) => &message.recent_blockhash, |             Self::Legacy(message) => &message.recent_blockhash, | ||||||
|             Self::V0(mapped_msg) => &mapped_msg.message.recent_blockhash, |             Self::V0(message) => &message.recent_blockhash, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -112,7 +112,7 @@ impl SanitizedMessage { | |||||||
|     pub fn instructions(&self) -> &[CompiledInstruction] { |     pub fn instructions(&self) -> &[CompiledInstruction] { | ||||||
|         match self { |         match self { | ||||||
|             Self::Legacy(message) => &message.instructions, |             Self::Legacy(message) => &message.instructions, | ||||||
|             Self::V0(mapped_msg) => &mapped_msg.message.instructions, |             Self::V0(message) => &message.instructions, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -123,7 +123,7 @@ impl SanitizedMessage { | |||||||
|     ) -> impl Iterator<Item = (&Pubkey, &CompiledInstruction)> { |     ) -> impl Iterator<Item = (&Pubkey, &CompiledInstruction)> { | ||||||
|         match self { |         match self { | ||||||
|             Self::Legacy(message) => message.instructions.iter(), |             Self::Legacy(message) => message.instructions.iter(), | ||||||
|             Self::V0(mapped_msg) => mapped_msg.message.instructions.iter(), |             Self::V0(message) => message.instructions.iter(), | ||||||
|         } |         } | ||||||
|         .map(move |ix| { |         .map(move |ix| { | ||||||
|             ( |             ( | ||||||
| @@ -138,7 +138,7 @@ impl SanitizedMessage { | |||||||
|     pub fn account_keys_iter(&self) -> Box<dyn Iterator<Item = &Pubkey> + '_> { |     pub fn account_keys_iter(&self) -> Box<dyn Iterator<Item = &Pubkey> + '_> { | ||||||
|         match self { |         match self { | ||||||
|             Self::Legacy(message) => Box::new(message.account_keys.iter()), |             Self::Legacy(message) => Box::new(message.account_keys.iter()), | ||||||
|             Self::V0(mapped_msg) => Box::new(mapped_msg.account_keys_iter()), |             Self::V0(message) => Box::new(message.account_keys_iter()), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -146,7 +146,7 @@ impl SanitizedMessage { | |||||||
|     pub fn account_keys_len(&self) -> usize { |     pub fn account_keys_len(&self) -> usize { | ||||||
|         match self { |         match self { | ||||||
|             Self::Legacy(message) => message.account_keys.len(), |             Self::Legacy(message) => message.account_keys.len(), | ||||||
|             Self::V0(mapped_msg) => mapped_msg.account_keys_len(), |             Self::V0(message) => message.account_keys_len(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -251,10 +251,10 @@ impl SanitizedMessage { | |||||||
|         data |         data | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Return the mapped addresses for this message if it has any. |     /// Return the resolved addresses for this message if it has any. | ||||||
|     fn mapped_addresses(&self) -> Option<&MappedAddresses> { |     fn loaded_lookup_table_addresses(&self) -> Option<&LoadedAddresses> { | ||||||
|         match &self { |         match &self { | ||||||
|             SanitizedMessage::V0(message) => Some(&message.mapped_addresses), |             SanitizedMessage::V0(message) => Some(&message.loaded_addresses), | ||||||
|             _ => None, |             _ => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -262,7 +262,7 @@ impl SanitizedMessage { | |||||||
|     /// Return the number of readonly accounts loaded by this message. |     /// Return the number of readonly accounts loaded by this message. | ||||||
|     pub fn num_readonly_accounts(&self) -> usize { |     pub fn num_readonly_accounts(&self) -> usize { | ||||||
|         let mapped_readonly_addresses = self |         let mapped_readonly_addresses = self | ||||||
|             .mapped_addresses() |             .loaded_lookup_table_addresses() | ||||||
|             .map(|keys| keys.readonly.len()) |             .map(|keys| keys.readonly.len()) | ||||||
|             .unwrap_or_default(); |             .unwrap_or_default(); | ||||||
|         mapped_readonly_addresses |         mapped_readonly_addresses | ||||||
| @@ -311,13 +311,13 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn test_try_from_message() { |     fn test_try_from_message() { | ||||||
|         let dupe_key = Pubkey::new_unique(); |         let dupe_key = Pubkey::new_unique(); | ||||||
|         let legacy_message_with_dupes = Message { |         let legacy_message_with_dupes = LegacyMessage { | ||||||
|             header: MessageHeader { |             header: MessageHeader { | ||||||
|                 num_required_signatures: 1, |                 num_required_signatures: 1, | ||||||
|                 ..MessageHeader::default() |                 ..MessageHeader::default() | ||||||
|             }, |             }, | ||||||
|             account_keys: vec![dupe_key, dupe_key], |             account_keys: vec![dupe_key, dupe_key], | ||||||
|             ..Message::default() |             ..LegacyMessage::default() | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
| @@ -325,9 +325,9 @@ mod tests { | |||||||
|             Some(SanitizeMessageError::DuplicateAccountKey), |             Some(SanitizeMessageError::DuplicateAccountKey), | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         let legacy_message_with_no_signers = Message { |         let legacy_message_with_no_signers = LegacyMessage { | ||||||
|             account_keys: vec![Pubkey::new_unique()], |             account_keys: vec![Pubkey::new_unique()], | ||||||
|             ..Message::default() |             ..LegacyMessage::default() | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
| @@ -346,7 +346,7 @@ mod tests { | |||||||
|             CompiledInstruction::new(2, &(), vec![0, 1]), |             CompiledInstruction::new(2, &(), vec![0, 1]), | ||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|         let message = SanitizedMessage::try_from(Message::new_with_compiled_instructions( |         let message = SanitizedMessage::try_from(LegacyMessage::new_with_compiled_instructions( | ||||||
|             1, |             1, | ||||||
|             0, |             0, | ||||||
|             2, |             2, | ||||||
| @@ -370,20 +370,20 @@ mod tests { | |||||||
|         let key4 = Pubkey::new_unique(); |         let key4 = Pubkey::new_unique(); | ||||||
|         let key5 = Pubkey::new_unique(); |         let key5 = Pubkey::new_unique(); | ||||||
|  |  | ||||||
|         let legacy_message = SanitizedMessage::try_from(Message { |         let legacy_message = SanitizedMessage::try_from(LegacyMessage { | ||||||
|             header: MessageHeader { |             header: MessageHeader { | ||||||
|                 num_required_signatures: 2, |                 num_required_signatures: 2, | ||||||
|                 num_readonly_signed_accounts: 1, |                 num_readonly_signed_accounts: 1, | ||||||
|                 num_readonly_unsigned_accounts: 1, |                 num_readonly_unsigned_accounts: 1, | ||||||
|             }, |             }, | ||||||
|             account_keys: vec![key0, key1, key2, key3], |             account_keys: vec![key0, key1, key2, key3], | ||||||
|             ..Message::default() |             ..LegacyMessage::default() | ||||||
|         }) |         }) | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|         assert_eq!(legacy_message.num_readonly_accounts(), 2); |         assert_eq!(legacy_message.num_readonly_accounts(), 2); | ||||||
|  |  | ||||||
|         let mapped_message = SanitizedMessage::V0(MappedMessage { |         let v0_message = SanitizedMessage::V0(v0::LoadedMessage { | ||||||
|             message: v0::Message { |             message: v0::Message { | ||||||
|                 header: MessageHeader { |                 header: MessageHeader { | ||||||
|                     num_required_signatures: 2, |                     num_required_signatures: 2, | ||||||
| @@ -393,13 +393,13 @@ mod tests { | |||||||
|                 account_keys: vec![key0, key1, key2, key3], |                 account_keys: vec![key0, key1, key2, key3], | ||||||
|                 ..v0::Message::default() |                 ..v0::Message::default() | ||||||
|             }, |             }, | ||||||
|             mapped_addresses: MappedAddresses { |             loaded_addresses: LoadedAddresses { | ||||||
|                 writable: vec![key4], |                 writable: vec![key4], | ||||||
|                 readonly: vec![key5], |                 readonly: vec![key5], | ||||||
|             }, |             }, | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         assert_eq!(mapped_message.num_readonly_accounts(), 3); |         assert_eq!(v0_message.num_readonly_accounts(), 3); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -427,7 +427,7 @@ mod tests { | |||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|         let demote_program_write_locks = true; |         let demote_program_write_locks = true; | ||||||
|         let message = Message::new(&instructions, Some(&id1)); |         let message = LegacyMessage::new(&instructions, Some(&id1)); | ||||||
|         let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap(); |         let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap(); | ||||||
|         let serialized = sanitized_message.serialize_instructions(demote_program_write_locks); |         let serialized = sanitized_message.serialize_instructions(demote_program_write_locks); | ||||||
|  |  | ||||||
| @@ -438,7 +438,7 @@ mod tests { | |||||||
|         // assert that Message::deserialize_instruction is compatible with SanitizedMessage::serialize_instructions |         // assert that Message::deserialize_instruction is compatible with SanitizedMessage::serialize_instructions | ||||||
|         for (i, instruction) in instructions.iter().enumerate() { |         for (i, instruction) in instructions.iter().enumerate() { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 Message::deserialize_instruction(i, &serialized).unwrap(), |                 LegacyMessage::deserialize_instruction(i, &serialized).unwrap(), | ||||||
|                 *instruction |                 *instruction | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
| @@ -481,18 +481,18 @@ mod tests { | |||||||
|             data: vec![], |             data: vec![], | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let legacy_message = SanitizedMessage::try_from(Message { |         let legacy_message = SanitizedMessage::try_from(LegacyMessage { | ||||||
|             header: MessageHeader { |             header: MessageHeader { | ||||||
|                 num_required_signatures: 1, |                 num_required_signatures: 1, | ||||||
|                 num_readonly_signed_accounts: 0, |                 num_readonly_signed_accounts: 0, | ||||||
|                 num_readonly_unsigned_accounts: 0, |                 num_readonly_unsigned_accounts: 0, | ||||||
|             }, |             }, | ||||||
|             account_keys: vec![key0, key1, key2, program_id], |             account_keys: vec![key0, key1, key2, program_id], | ||||||
|             ..Message::default() |             ..LegacyMessage::default() | ||||||
|         }) |         }) | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|         let mapped_message = SanitizedMessage::V0(MappedMessage { |         let v0_message = SanitizedMessage::V0(v0::LoadedMessage { | ||||||
|             message: v0::Message { |             message: v0::Message { | ||||||
|                 header: MessageHeader { |                 header: MessageHeader { | ||||||
|                     num_required_signatures: 1, |                     num_required_signatures: 1, | ||||||
| @@ -502,13 +502,13 @@ mod tests { | |||||||
|                 account_keys: vec![key0, key1], |                 account_keys: vec![key0, key1], | ||||||
|                 ..v0::Message::default() |                 ..v0::Message::default() | ||||||
|             }, |             }, | ||||||
|             mapped_addresses: MappedAddresses { |             loaded_addresses: LoadedAddresses { | ||||||
|                 writable: vec![key2], |                 writable: vec![key2], | ||||||
|                 readonly: vec![program_id], |                 readonly: vec![program_id], | ||||||
|             }, |             }, | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         for message in vec![legacy_message, mapped_message] { |         for message in vec![legacy_message, v0_message] { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 message.try_compile_instruction(&valid_instruction), |                 message.try_compile_instruction(&valid_instruction), | ||||||
|                 Some(CompiledInstruction { |                 Some(CompiledInstruction { | ||||||
|   | |||||||
| @@ -1,396 +0,0 @@ | |||||||
| use crate::{ |  | ||||||
|     hash::Hash, |  | ||||||
|     instruction::CompiledInstruction, |  | ||||||
|     message::{MessageHeader, MESSAGE_VERSION_PREFIX}, |  | ||||||
|     pubkey::Pubkey, |  | ||||||
|     sanitize::{Sanitize, SanitizeError}, |  | ||||||
|     short_vec, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /// Indexes that are mapped to addresses using an on-chain address map for |  | ||||||
| /// succinctly loading readonly and writable accounts. |  | ||||||
| #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] |  | ||||||
| #[serde(rename_all = "camelCase")] |  | ||||||
| pub struct AddressMapIndexes { |  | ||||||
|     #[serde(with = "short_vec")] |  | ||||||
|     pub writable: Vec<u8>, |  | ||||||
|     #[serde(with = "short_vec")] |  | ||||||
|     pub readonly: Vec<u8>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Transaction message format which supports succinct account loading with |  | ||||||
| /// indexes for on-chain address maps. |  | ||||||
| #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] |  | ||||||
| #[serde(rename_all = "camelCase")] |  | ||||||
| pub struct Message { |  | ||||||
|     /// The message header, identifying signed and read-only `account_keys` |  | ||||||
|     pub header: MessageHeader, |  | ||||||
|  |  | ||||||
|     /// List of accounts loaded by this transaction. |  | ||||||
|     #[serde(with = "short_vec")] |  | ||||||
|     pub account_keys: Vec<Pubkey>, |  | ||||||
|  |  | ||||||
|     /// The blockhash of a recent block. |  | ||||||
|     pub recent_blockhash: Hash, |  | ||||||
|  |  | ||||||
|     /// Instructions that invoke a designated program, are executed in sequence, |  | ||||||
|     /// and committed in one atomic transaction if all succeed. |  | ||||||
|     /// |  | ||||||
|     /// # Notes |  | ||||||
|     /// |  | ||||||
|     /// Account and program indexes will index into the list of addresses |  | ||||||
|     /// constructed from the concatenation of `account_keys`, flattened list of |  | ||||||
|     /// `writable` address map indexes, and the flattened `readonly` address |  | ||||||
|     /// map indexes. |  | ||||||
|     #[serde(with = "short_vec")] |  | ||||||
|     pub instructions: Vec<CompiledInstruction>, |  | ||||||
|  |  | ||||||
|     /// List of address map indexes used to succinctly load additional accounts |  | ||||||
|     /// for this transaction. |  | ||||||
|     /// |  | ||||||
|     /// # Notes |  | ||||||
|     /// |  | ||||||
|     /// The last `address_map_indexes.len()` accounts of the read-only unsigned |  | ||||||
|     /// accounts are loaded as address maps. |  | ||||||
|     #[serde(with = "short_vec")] |  | ||||||
|     pub address_map_indexes: Vec<AddressMapIndexes>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Sanitize for Message { |  | ||||||
|     fn sanitize(&self) -> Result<(), SanitizeError> { |  | ||||||
|         // signing area and read-only non-signing area should not |  | ||||||
|         // overlap |  | ||||||
|         if usize::from(self.header.num_required_signatures) |  | ||||||
|             .saturating_add(usize::from(self.header.num_readonly_unsigned_accounts)) |  | ||||||
|             > self.account_keys.len() |  | ||||||
|         { |  | ||||||
|             return Err(SanitizeError::IndexOutOfBounds); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // there should be at least 1 RW fee-payer account. |  | ||||||
|         if self.header.num_readonly_signed_accounts >= self.header.num_required_signatures { |  | ||||||
|             return Err(SanitizeError::IndexOutOfBounds); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // there cannot be more address maps than read-only unsigned accounts. |  | ||||||
|         let num_address_map_indexes = self.address_map_indexes.len(); |  | ||||||
|         if num_address_map_indexes > usize::from(self.header.num_readonly_unsigned_accounts) { |  | ||||||
|             return Err(SanitizeError::IndexOutOfBounds); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // each map must load at least one entry |  | ||||||
|         let mut num_loaded_accounts = self.account_keys.len(); |  | ||||||
|         for indexes in &self.address_map_indexes { |  | ||||||
|             let num_loaded_map_entries = indexes |  | ||||||
|                 .writable |  | ||||||
|                 .len() |  | ||||||
|                 .saturating_add(indexes.readonly.len()); |  | ||||||
|  |  | ||||||
|             if num_loaded_map_entries == 0 { |  | ||||||
|                 return Err(SanitizeError::InvalidValue); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             num_loaded_accounts = num_loaded_accounts.saturating_add(num_loaded_map_entries); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // the number of loaded accounts must be <= 256 since account indices are |  | ||||||
|         // encoded as `u8` |  | ||||||
|         if num_loaded_accounts > 256 { |  | ||||||
|             return Err(SanitizeError::IndexOutOfBounds); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for ci in &self.instructions { |  | ||||||
|             if usize::from(ci.program_id_index) >= num_loaded_accounts { |  | ||||||
|                 return Err(SanitizeError::IndexOutOfBounds); |  | ||||||
|             } |  | ||||||
|             // A program cannot be a payer. |  | ||||||
|             if ci.program_id_index == 0 { |  | ||||||
|                 return Err(SanitizeError::IndexOutOfBounds); |  | ||||||
|             } |  | ||||||
|             for ai in &ci.accounts { |  | ||||||
|                 if usize::from(*ai) >= num_loaded_accounts { |  | ||||||
|                     return Err(SanitizeError::IndexOutOfBounds); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Message { |  | ||||||
|     /// Serialize this message with a version #0 prefix using bincode encoding. |  | ||||||
|     pub fn serialize(&self) -> Vec<u8> { |  | ||||||
|         bincode::serialize(&(MESSAGE_VERSION_PREFIX, self)).unwrap() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use {super::*, crate::message::VersionedMessage}; |  | ||||||
|  |  | ||||||
|     fn simple_message() -> Message { |  | ||||||
|         Message { |  | ||||||
|             header: MessageHeader { |  | ||||||
|                 num_required_signatures: 1, |  | ||||||
|                 num_readonly_signed_accounts: 0, |  | ||||||
|                 num_readonly_unsigned_accounts: 1, |  | ||||||
|             }, |  | ||||||
|             account_keys: vec![Pubkey::new_unique(), Pubkey::new_unique()], |  | ||||||
|             address_map_indexes: vec![AddressMapIndexes { |  | ||||||
|                 writable: vec![], |  | ||||||
|                 readonly: vec![0], |  | ||||||
|             }], |  | ||||||
|             ..Message::default() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn two_map_message() -> Message { |  | ||||||
|         Message { |  | ||||||
|             header: MessageHeader { |  | ||||||
|                 num_required_signatures: 1, |  | ||||||
|                 num_readonly_signed_accounts: 0, |  | ||||||
|                 num_readonly_unsigned_accounts: 2, |  | ||||||
|             }, |  | ||||||
|             account_keys: vec![ |  | ||||||
|                 Pubkey::new_unique(), |  | ||||||
|                 Pubkey::new_unique(), |  | ||||||
|                 Pubkey::new_unique(), |  | ||||||
|             ], |  | ||||||
|             address_map_indexes: vec![ |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: vec![1], |  | ||||||
|                     readonly: vec![0], |  | ||||||
|                 }, |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: vec![0], |  | ||||||
|                     readonly: vec![1], |  | ||||||
|                 }, |  | ||||||
|             ], |  | ||||||
|             ..Message::default() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #[test] |  | ||||||
|     fn test_sanitize_account_indices() { |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             address_map_indexes: vec![], |  | ||||||
|             instructions: vec![CompiledInstruction { |  | ||||||
|                 program_id_index: 1, |  | ||||||
|                 accounts: vec![u8::MAX], |  | ||||||
|                 data: vec![], |  | ||||||
|             }], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..u8::MAX).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             address_map_indexes: vec![], |  | ||||||
|             instructions: vec![CompiledInstruction { |  | ||||||
|                 program_id_index: 1, |  | ||||||
|                 accounts: vec![u8::MAX], |  | ||||||
|                 data: vec![], |  | ||||||
|             }], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..u8::MAX).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             instructions: vec![CompiledInstruction { |  | ||||||
|                 program_id_index: 1, |  | ||||||
|                 accounts: vec![u8::MAX], |  | ||||||
|                 data: vec![], |  | ||||||
|             }], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..u8::MAX - 1).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             instructions: vec![CompiledInstruction { |  | ||||||
|                 program_id_index: 1, |  | ||||||
|                 accounts: vec![u8::MAX], |  | ||||||
|                 data: vec![], |  | ||||||
|             }], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             address_map_indexes: vec![ |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..200).step_by(2).collect(), |  | ||||||
|                     readonly: (1..200).step_by(2).collect(), |  | ||||||
|                 }, |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..53).step_by(2).collect(), |  | ||||||
|                     readonly: (1..53).step_by(2).collect(), |  | ||||||
|                 }, |  | ||||||
|             ], |  | ||||||
|             instructions: vec![CompiledInstruction { |  | ||||||
|                 program_id_index: 1, |  | ||||||
|                 accounts: vec![u8::MAX], |  | ||||||
|                 data: vec![], |  | ||||||
|             }], |  | ||||||
|             ..two_map_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             address_map_indexes: vec![ |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..200).step_by(2).collect(), |  | ||||||
|                     readonly: (1..200).step_by(2).collect(), |  | ||||||
|                 }, |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..52).step_by(2).collect(), |  | ||||||
|                     readonly: (1..52).step_by(2).collect(), |  | ||||||
|                 }, |  | ||||||
|             ], |  | ||||||
|             instructions: vec![CompiledInstruction { |  | ||||||
|                 program_id_index: 1, |  | ||||||
|                 accounts: vec![u8::MAX], |  | ||||||
|                 data: vec![], |  | ||||||
|             }], |  | ||||||
|             ..two_map_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #[test] |  | ||||||
|     fn test_sanitize_excessive_loaded_accounts() { |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             address_map_indexes: vec![], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..257).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             address_map_indexes: vec![], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..u8::MAX).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             account_keys: (0..256).map(|_| Pubkey::new_unique()).collect(), |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             address_map_indexes: vec![ |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..200).step_by(2).collect(), |  | ||||||
|                     readonly: (1..200).step_by(2).collect(), |  | ||||||
|                 }, |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..53).step_by(2).collect(), |  | ||||||
|                     readonly: (1..53).step_by(2).collect(), |  | ||||||
|                 } |  | ||||||
|             ], |  | ||||||
|             ..two_map_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             address_map_indexes: vec![ |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..200).step_by(2).collect(), |  | ||||||
|                     readonly: (1..200).step_by(2).collect(), |  | ||||||
|                 }, |  | ||||||
|                 AddressMapIndexes { |  | ||||||
|                     writable: (0..200).step_by(2).collect(), |  | ||||||
|                     readonly: (1..200).step_by(2).collect(), |  | ||||||
|                 } |  | ||||||
|             ], |  | ||||||
|             ..two_map_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #[test] |  | ||||||
|     fn test_sanitize_excessive_maps() { |  | ||||||
|         assert!(Message { |  | ||||||
|             header: MessageHeader { |  | ||||||
|                 num_readonly_unsigned_accounts: 1, |  | ||||||
|                 ..simple_message().header |  | ||||||
|             }, |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             header: MessageHeader { |  | ||||||
|                 num_readonly_unsigned_accounts: 0, |  | ||||||
|                 ..simple_message().header |  | ||||||
|             }, |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #[test] |  | ||||||
|     fn test_sanitize_address_map() { |  | ||||||
|         assert!(Message { |  | ||||||
|             address_map_indexes: vec![AddressMapIndexes { |  | ||||||
|                 writable: vec![0], |  | ||||||
|                 readonly: vec![], |  | ||||||
|             }], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             address_map_indexes: vec![AddressMapIndexes { |  | ||||||
|                 writable: vec![], |  | ||||||
|                 readonly: vec![0], |  | ||||||
|             }], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_ok()); |  | ||||||
|  |  | ||||||
|         assert!(Message { |  | ||||||
|             address_map_indexes: vec![AddressMapIndexes { |  | ||||||
|                 writable: vec![], |  | ||||||
|                 readonly: vec![], |  | ||||||
|             }], |  | ||||||
|             ..simple_message() |  | ||||||
|         } |  | ||||||
|         .sanitize() |  | ||||||
|         .is_err()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #[test] |  | ||||||
|     fn test_serialize() { |  | ||||||
|         let message = simple_message(); |  | ||||||
|         let versioned_msg = VersionedMessage::V0(message.clone()); |  | ||||||
|         assert_eq!(message.serialize(), versioned_msg.serialize()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -2,7 +2,7 @@ use { | |||||||
|     crate::{ |     crate::{ | ||||||
|         hash::Hash, |         hash::Hash, | ||||||
|         instruction::CompiledInstruction, |         instruction::CompiledInstruction, | ||||||
|         message::{v0, Message, MessageHeader}, |         message::{legacy::Message as LegacyMessage, MessageHeader}, | ||||||
|         pubkey::Pubkey, |         pubkey::Pubkey, | ||||||
|         sanitize::{Sanitize, SanitizeError}, |         sanitize::{Sanitize, SanitizeError}, | ||||||
|         short_vec, |         short_vec, | ||||||
| @@ -15,6 +15,8 @@ use { | |||||||
|     std::fmt, |     std::fmt, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | pub mod v0; | ||||||
|  | 
 | ||||||
| /// Bit mask that indicates whether a serialized message is versioned.
 | /// Bit mask that indicates whether a serialized message is versioned.
 | ||||||
| pub const MESSAGE_VERSION_PREFIX: u8 = 0x80; | pub const MESSAGE_VERSION_PREFIX: u8 = 0x80; | ||||||
| 
 | 
 | ||||||
| @@ -26,10 +28,10 @@ pub const MESSAGE_VERSION_PREFIX: u8 = 0x80; | |||||||
| /// which message version is serialized starting from version `0`. If the first
 | /// which message version is serialized starting from version `0`. If the first
 | ||||||
| /// is bit is not set, all bytes are used to encode the legacy `Message`
 | /// is bit is not set, all bytes are used to encode the legacy `Message`
 | ||||||
| /// format.
 | /// format.
 | ||||||
| #[frozen_abi(digest = "x2F3RG2RhJQWN6L2N3jebvcAvNYFrhE3sKTPJ4sENvL")] | #[frozen_abi(digest = "G4EAiqmGgBprgf5ePYemLJcoFfx4R7rhC1Weo2FVJ7fn")] | ||||||
| #[derive(Debug, PartialEq, Eq, Clone, AbiEnumVisitor, AbiExample)] | #[derive(Debug, PartialEq, Eq, Clone, AbiEnumVisitor, AbiExample)] | ||||||
| pub enum VersionedMessage { | pub enum VersionedMessage { | ||||||
|     Legacy(Message), |     Legacy(LegacyMessage), | ||||||
|     V0(v0::Message), |     V0(v0::Message), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -98,7 +100,7 @@ impl VersionedMessage { | |||||||
| 
 | 
 | ||||||
| impl Default for VersionedMessage { | impl Default for VersionedMessage { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self::Legacy(Message::default()) |         Self::Legacy(LegacyMessage::default()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -206,7 +208,7 @@ impl<'de> Deserialize<'de> for VersionedMessage { | |||||||
|                                 de::Error::invalid_length(1, &self) |                                 de::Error::invalid_length(1, &self) | ||||||
|                             })?; |                             })?; | ||||||
| 
 | 
 | ||||||
|                         Ok(VersionedMessage::Legacy(Message { |                         Ok(VersionedMessage::Legacy(LegacyMessage { | ||||||
|                             header: MessageHeader { |                             header: MessageHeader { | ||||||
|                                 num_required_signatures, |                                 num_required_signatures, | ||||||
|                                 num_readonly_signed_accounts: message.num_readonly_signed_accounts, |                                 num_readonly_signed_accounts: message.num_readonly_signed_accounts, | ||||||
| @@ -247,7 +249,7 @@ mod tests { | |||||||
|         super::*, |         super::*, | ||||||
|         crate::{ |         crate::{ | ||||||
|             instruction::{AccountMeta, Instruction}, |             instruction::{AccountMeta, Instruction}, | ||||||
|             message::v0::AddressMapIndexes, |             message::v0::MessageAddressTableLookup, | ||||||
|         }, |         }, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @@ -274,7 +276,7 @@ mod tests { | |||||||
|             ), |             ), | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         let mut message = Message::new(&instructions, Some(&id1)); |         let mut message = LegacyMessage::new(&instructions, Some(&id1)); | ||||||
|         message.recent_blockhash = Hash::new_unique(); |         message.recent_blockhash = Hash::new_unique(); | ||||||
| 
 | 
 | ||||||
|         let bytes1 = bincode::serialize(&message).unwrap(); |         let bytes1 = bincode::serialize(&message).unwrap(); | ||||||
| @@ -282,7 +284,7 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|         assert_eq!(bytes1, bytes2); |         assert_eq!(bytes1, bytes2); | ||||||
| 
 | 
 | ||||||
|         let message1: Message = bincode::deserialize(&bytes1).unwrap(); |         let message1: LegacyMessage = bincode::deserialize(&bytes1).unwrap(); | ||||||
|         let message2: VersionedMessage = bincode::deserialize(&bytes2).unwrap(); |         let message2: VersionedMessage = bincode::deserialize(&bytes2).unwrap(); | ||||||
| 
 | 
 | ||||||
|         if let VersionedMessage::Legacy(message2) = message2 { |         if let VersionedMessage::Legacy(message2) = message2 { | ||||||
| @@ -299,27 +301,27 @@ mod tests { | |||||||
|             header: MessageHeader { |             header: MessageHeader { | ||||||
|                 num_required_signatures: 1, |                 num_required_signatures: 1, | ||||||
|                 num_readonly_signed_accounts: 0, |                 num_readonly_signed_accounts: 0, | ||||||
|                 num_readonly_unsigned_accounts: 2, |                 num_readonly_unsigned_accounts: 0, | ||||||
|             }, |             }, | ||||||
|             recent_blockhash: Hash::new_unique(), |             recent_blockhash: Hash::new_unique(), | ||||||
|             account_keys: vec![ |             account_keys: vec![ | ||||||
|                 Pubkey::new_unique(), |                 Pubkey::new_unique(), | ||||||
|                 Pubkey::new_unique(), |  | ||||||
|                 Pubkey::new_unique(), |  | ||||||
|             ], |             ], | ||||||
|             address_map_indexes: vec![ |             address_table_lookups: vec![ | ||||||
|                 AddressMapIndexes { |                 MessageAddressTableLookup { | ||||||
|                     writable: vec![1], |                     account_key: Pubkey::new_unique(), | ||||||
|                     readonly: vec![0], |                     writable_indexes: vec![1], | ||||||
|  |                     readonly_indexes: vec![0], | ||||||
|                 }, |                 }, | ||||||
|                 AddressMapIndexes { |                 MessageAddressTableLookup { | ||||||
|                     writable: vec![0], |                     account_key: Pubkey::new_unique(), | ||||||
|                     readonly: vec![1], |                     writable_indexes: vec![0], | ||||||
|  |                     readonly_indexes: vec![1], | ||||||
|                 }, |                 }, | ||||||
|             ], |             ], | ||||||
|             instructions: vec![CompiledInstruction { |             instructions: vec![CompiledInstruction { | ||||||
|                 program_id_index: 1, |                 program_id_index: 1, | ||||||
|                 accounts: vec![0], |                 accounts: vec![0, 2, 3, 4], | ||||||
|                 data: vec![], |                 data: vec![], | ||||||
|             }], |             }], | ||||||
|         }; |         }; | ||||||
| @@ -5,37 +5,44 @@ use { | |||||||
|         pubkey::Pubkey, |         pubkey::Pubkey, | ||||||
|         sysvar, |         sysvar, | ||||||
|     }, |     }, | ||||||
|     std::{collections::HashSet, convert::TryFrom}, |     std::{collections::HashSet, ops::Deref, convert::TryFrom}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Combination of a version #0 message and its mapped addresses
 | /// Combination of a version #0 message and its loaded addresses
 | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
| pub struct MappedMessage { | pub struct LoadedMessage { | ||||||
|     /// Message which loaded a collection of mapped addresses
 |     /// Message which loaded a collection of lookup table addresses
 | ||||||
|     pub message: v0::Message, |     pub message: v0::Message, | ||||||
|     /// Collection of mapped addresses loaded by this message
 |     /// Addresses loaded with on-chain address lookup tables
 | ||||||
|     pub mapped_addresses: MappedAddresses, |     pub loaded_addresses: LoadedAddresses, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Collection of mapped addresses loaded succinctly by a transaction using
 | impl Deref for LoadedMessage { | ||||||
| /// on-chain address map accounts.
 |     type Target = v0::Message; | ||||||
|  |     fn deref(&self) -> &Self::Target { | ||||||
|  |         &self.message | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Collection of addresses loaded from on-chain lookup tables, split
 | ||||||
|  | /// by readonly and writable.
 | ||||||
| #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] | #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] | ||||||
| pub struct MappedAddresses { | pub struct LoadedAddresses { | ||||||
|     /// List of addresses for writable loaded accounts
 |     /// List of addresses for writable loaded accounts
 | ||||||
|     pub writable: Vec<Pubkey>, |     pub writable: Vec<Pubkey>, | ||||||
|     /// List of addresses for read-only loaded accounts
 |     /// List of addresses for read-only loaded accounts
 | ||||||
|     pub readonly: Vec<Pubkey>, |     pub readonly: Vec<Pubkey>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl MappedMessage { | impl LoadedMessage { | ||||||
|     /// Returns an iterator of account key segments. The ordering of segments
 |     /// Returns an iterator of account key segments. The ordering of segments
 | ||||||
|     /// affects how account indexes from compiled instructions are resolved and
 |     /// affects how account indexes from compiled instructions are resolved and
 | ||||||
|     /// so should not be changed.
 |     /// so should not be changed.
 | ||||||
|     fn account_keys_segment_iter(&self) -> impl Iterator<Item = &Vec<Pubkey>> { |     fn account_keys_segment_iter(&self) -> impl Iterator<Item = &Vec<Pubkey>> { | ||||||
|         vec![ |         vec![ | ||||||
|             &self.message.account_keys, |             &self.message.account_keys, | ||||||
|             &self.mapped_addresses.writable, |             &self.loaded_addresses.writable, | ||||||
|             &self.mapped_addresses.readonly, |             &self.loaded_addresses.readonly, | ||||||
|         ] |         ] | ||||||
|         .into_iter() |         .into_iter() | ||||||
|     } |     } | ||||||
| @@ -82,7 +89,7 @@ impl MappedMessage { | |||||||
|         let num_signed_accounts = usize::from(header.num_required_signatures); |         let num_signed_accounts = usize::from(header.num_required_signatures); | ||||||
|         if key_index >= num_account_keys { |         if key_index >= num_account_keys { | ||||||
|             let mapped_addresses_index = key_index.saturating_sub(num_account_keys); |             let mapped_addresses_index = key_index.saturating_sub(num_account_keys); | ||||||
|             mapped_addresses_index < self.mapped_addresses.writable.len() |             mapped_addresses_index < self.loaded_addresses.writable.len() | ||||||
|         } else if key_index >= num_signed_accounts { |         } else if key_index >= num_signed_accounts { | ||||||
|             let num_unsigned_accounts = num_account_keys.saturating_sub(num_signed_accounts); |             let num_unsigned_accounts = num_account_keys.saturating_sub(num_signed_accounts); | ||||||
|             let num_writable_unsigned_accounts = num_unsigned_accounts |             let num_writable_unsigned_accounts = num_unsigned_accounts | ||||||
| @@ -138,7 +145,7 @@ mod tests { | |||||||
|         itertools::Itertools, |         itertools::Itertools, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fn create_test_mapped_message() -> (MappedMessage, [Pubkey; 6]) { |     fn check_test_loaded_message() -> (LoadedMessage, [Pubkey; 6]) { | ||||||
|         let key0 = Pubkey::new_unique(); |         let key0 = Pubkey::new_unique(); | ||||||
|         let key1 = Pubkey::new_unique(); |         let key1 = Pubkey::new_unique(); | ||||||
|         let key2 = Pubkey::new_unique(); |         let key2 = Pubkey::new_unique(); | ||||||
| @@ -146,7 +153,7 @@ mod tests { | |||||||
|         let key4 = Pubkey::new_unique(); |         let key4 = Pubkey::new_unique(); | ||||||
|         let key5 = Pubkey::new_unique(); |         let key5 = Pubkey::new_unique(); | ||||||
| 
 | 
 | ||||||
|         let message = MappedMessage { |         let message = LoadedMessage { | ||||||
|             message: v0::Message { |             message: v0::Message { | ||||||
|                 header: MessageHeader { |                 header: MessageHeader { | ||||||
|                     num_required_signatures: 2, |                     num_required_signatures: 2, | ||||||
| @@ -156,7 +163,7 @@ mod tests { | |||||||
|                 account_keys: vec![key0, key1, key2, key3], |                 account_keys: vec![key0, key1, key2, key3], | ||||||
|                 ..v0::Message::default() |                 ..v0::Message::default() | ||||||
|             }, |             }, | ||||||
|             mapped_addresses: MappedAddresses { |             loaded_addresses: LoadedAddresses { | ||||||
|                 writable: vec![key4], |                 writable: vec![key4], | ||||||
|                 readonly: vec![key5], |                 readonly: vec![key5], | ||||||
|             }, |             }, | ||||||
| @@ -167,7 +174,7 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_account_keys_segment_iter() { |     fn test_account_keys_segment_iter() { | ||||||
|         let (message, keys) = create_test_mapped_message(); |         let (message, keys) = check_test_loaded_message(); | ||||||
| 
 | 
 | ||||||
|         let expected_segments = vec![ |         let expected_segments = vec![ | ||||||
|             vec![keys[0], keys[1], keys[2], keys[3]], |             vec![keys[0], keys[1], keys[2], keys[3]], | ||||||
| @@ -183,14 +190,14 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_account_keys_len() { |     fn test_account_keys_len() { | ||||||
|         let (message, keys) = create_test_mapped_message(); |         let (message, keys) = check_test_loaded_message(); | ||||||
| 
 | 
 | ||||||
|         assert_eq!(message.account_keys_len(), keys.len()); |         assert_eq!(message.account_keys_len(), keys.len()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_account_keys_iter() { |     fn test_account_keys_iter() { | ||||||
|         let (message, keys) = create_test_mapped_message(); |         let (message, keys) = check_test_loaded_message(); | ||||||
| 
 | 
 | ||||||
|         let mut iter = message.account_keys_iter(); |         let mut iter = message.account_keys_iter(); | ||||||
|         for expected_key in keys { |         for expected_key in keys { | ||||||
| @@ -200,19 +207,19 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_has_duplicates() { |     fn test_has_duplicates() { | ||||||
|         let message = create_test_mapped_message().0; |         let message = check_test_loaded_message().0; | ||||||
| 
 | 
 | ||||||
|         assert!(!message.has_duplicates()); |         assert!(!message.has_duplicates()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_has_duplicates_with_dupe_keys() { |     fn test_has_duplicates_with_dupe_keys() { | ||||||
|         let create_message_with_dupe_keys = |mut keys: Vec<Pubkey>| MappedMessage { |         let create_message_with_dupe_keys = |mut keys: Vec<Pubkey>| LoadedMessage { | ||||||
|             message: v0::Message { |             message: v0::Message { | ||||||
|                 account_keys: keys.split_off(2), |                 account_keys: keys.split_off(2), | ||||||
|                 ..v0::Message::default() |                 ..v0::Message::default() | ||||||
|             }, |             }, | ||||||
|             mapped_addresses: MappedAddresses { |             loaded_addresses: LoadedAddresses { | ||||||
|                 writable: keys.split_off(2), |                 writable: keys.split_off(2), | ||||||
|                 readonly: keys, |                 readonly: keys, | ||||||
|             }, |             }, | ||||||
| @@ -234,7 +241,7 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_get_account_key() { |     fn test_get_account_key() { | ||||||
|         let (message, keys) = create_test_mapped_message(); |         let (message, keys) = check_test_loaded_message(); | ||||||
| 
 | 
 | ||||||
|         assert_eq!(message.get_account_key(0), Some(&keys[0])); |         assert_eq!(message.get_account_key(0), Some(&keys[0])); | ||||||
|         assert_eq!(message.get_account_key(1), Some(&keys[1])); |         assert_eq!(message.get_account_key(1), Some(&keys[1])); | ||||||
| @@ -246,7 +253,7 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_is_writable_index() { |     fn test_is_writable_index() { | ||||||
|         let message = create_test_mapped_message().0; |         let message = check_test_loaded_message().0; | ||||||
| 
 | 
 | ||||||
|         assert!(message.is_writable_index(0)); |         assert!(message.is_writable_index(0)); | ||||||
|         assert!(!message.is_writable_index(1)); |         assert!(!message.is_writable_index(1)); | ||||||
| @@ -258,15 +265,15 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_is_writable() { |     fn test_is_writable() { | ||||||
|         let mut mapped_msg = create_test_mapped_message().0; |         let mut message = check_test_loaded_message().0; | ||||||
| 
 | 
 | ||||||
|         mapped_msg.message.account_keys[0] = sysvar::clock::id(); |         message.message.account_keys[0] = sysvar::clock::id(); | ||||||
|         assert!(mapped_msg.is_writable_index(0)); |         assert!(message.is_writable_index(0)); | ||||||
|         assert!(!mapped_msg.is_writable(0, /*demote_program_write_locks=*/ true)); |         assert!(!message.is_writable(0, /*demote_program_write_locks=*/ true)); | ||||||
| 
 | 
 | ||||||
|         mapped_msg.message.account_keys[0] = system_program::id(); |         message.message.account_keys[0] = system_program::id(); | ||||||
|         assert!(mapped_msg.is_writable_index(0)); |         assert!(message.is_writable_index(0)); | ||||||
|         assert!(!mapped_msg.is_writable(0, /*demote_program_write_locks=*/ true)); |         assert!(!message.is_writable(0, /*demote_program_write_locks=*/ true)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
| @@ -274,7 +281,7 @@ mod tests { | |||||||
|         let key0 = Pubkey::new_unique(); |         let key0 = Pubkey::new_unique(); | ||||||
|         let key1 = Pubkey::new_unique(); |         let key1 = Pubkey::new_unique(); | ||||||
|         let key2 = Pubkey::new_unique(); |         let key2 = Pubkey::new_unique(); | ||||||
|         let mapped_msg = MappedMessage { |         let message = LoadedMessage { | ||||||
|             message: v0::Message { |             message: v0::Message { | ||||||
|                 header: MessageHeader { |                 header: MessageHeader { | ||||||
|                     num_required_signatures: 1, |                     num_required_signatures: 1, | ||||||
| @@ -289,13 +296,13 @@ mod tests { | |||||||
|                 }], |                 }], | ||||||
|                 ..v0::Message::default() |                 ..v0::Message::default() | ||||||
|             }, |             }, | ||||||
|             mapped_addresses: MappedAddresses { |             loaded_addresses: LoadedAddresses { | ||||||
|                 writable: vec![key1, key2], |                 writable: vec![key1, key2], | ||||||
|                 readonly: vec![], |                 readonly: vec![], | ||||||
|             }, |             }, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         assert!(mapped_msg.is_writable_index(2)); |         assert!(message.is_writable_index(2)); | ||||||
|         assert!(!mapped_msg.is_writable(2, /*demote_program_write_locks=*/ true)); |         assert!(!message.is_writable(2, /*demote_program_write_locks=*/ true)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										374
									
								
								sdk/program/src/message/versions/v0/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								sdk/program/src/message/versions/v0/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,374 @@ | |||||||
|  | use crate::{ | ||||||
|  |     hash::Hash, | ||||||
|  |     instruction::CompiledInstruction, | ||||||
|  |     message::{MessageHeader, MESSAGE_VERSION_PREFIX}, | ||||||
|  |     pubkey::Pubkey, | ||||||
|  |     sanitize::{Sanitize, SanitizeError}, | ||||||
|  |     short_vec, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | mod loaded; | ||||||
|  |  | ||||||
|  | pub use loaded::*; | ||||||
|  |  | ||||||
|  | /// Address table lookups describe an on-chain address lookup table to use | ||||||
|  | /// for loading more readonly and writable accounts in a single tx. | ||||||
|  | #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct MessageAddressTableLookup { | ||||||
|  |     /// Address lookup table account key | ||||||
|  |     pub account_key: Pubkey, | ||||||
|  |     /// List of indexes used to load writable account addresses | ||||||
|  |     #[serde(with = "short_vec")] | ||||||
|  |     pub writable_indexes: Vec<u8>, | ||||||
|  |     /// List of indexes used to load readonly account addresses | ||||||
|  |     #[serde(with = "short_vec")] | ||||||
|  |     pub readonly_indexes: Vec<u8>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Transaction message format which supports succinct account loading with | ||||||
|  | /// on-chain address lookup tables. | ||||||
|  | #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct Message { | ||||||
|  |     /// The message header, identifying signed and read-only `account_keys` | ||||||
|  |     pub header: MessageHeader, | ||||||
|  |  | ||||||
|  |     /// List of accounts loaded by this transaction. | ||||||
|  |     #[serde(with = "short_vec")] | ||||||
|  |     pub account_keys: Vec<Pubkey>, | ||||||
|  |  | ||||||
|  |     /// The blockhash of a recent block. | ||||||
|  |     pub recent_blockhash: Hash, | ||||||
|  |  | ||||||
|  |     /// Instructions that invoke a designated program, are executed in sequence, | ||||||
|  |     /// and committed in one atomic transaction if all succeed. | ||||||
|  |     /// | ||||||
|  |     /// # Notes | ||||||
|  |     /// | ||||||
|  |     /// Account and program indexes will index into the list of addresses | ||||||
|  |     /// constructed from the concatenation of three key lists: | ||||||
|  |     ///   1) message `account_keys` | ||||||
|  |     ///   2) ordered list of keys loaded from `writable` lookup table indexes | ||||||
|  |     ///   3) ordered list of keys loaded from `readable` lookup table indexes | ||||||
|  |     #[serde(with = "short_vec")] | ||||||
|  |     pub instructions: Vec<CompiledInstruction>, | ||||||
|  |  | ||||||
|  |     /// List of address table lookups used to load additional accounts | ||||||
|  |     /// for this transaction. | ||||||
|  |     #[serde(with = "short_vec")] | ||||||
|  |     pub address_table_lookups: Vec<MessageAddressTableLookup>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Sanitize for Message { | ||||||
|  |     fn sanitize(&self) -> Result<(), SanitizeError> { | ||||||
|  |         // signing area and read-only non-signing area should not | ||||||
|  |         // overlap | ||||||
|  |         if usize::from(self.header.num_required_signatures) | ||||||
|  |             .saturating_add(usize::from(self.header.num_readonly_unsigned_accounts)) | ||||||
|  |             > self.account_keys.len() | ||||||
|  |         { | ||||||
|  |             return Err(SanitizeError::IndexOutOfBounds); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // there should be at least 1 RW fee-payer account. | ||||||
|  |         if self.header.num_readonly_signed_accounts >= self.header.num_required_signatures { | ||||||
|  |             return Err(SanitizeError::InvalidValue); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let mut num_loaded_accounts = self.account_keys.len(); | ||||||
|  |         for lookup in &self.address_table_lookups { | ||||||
|  |             let num_table_loaded_accounts = lookup | ||||||
|  |                 .writable_indexes | ||||||
|  |                 .len() | ||||||
|  |                 .saturating_add(lookup.readonly_indexes.len()); | ||||||
|  |  | ||||||
|  |             // each lookup table must be used to load at least one account | ||||||
|  |             if num_table_loaded_accounts == 0 { | ||||||
|  |                 return Err(SanitizeError::InvalidValue); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             num_loaded_accounts = num_loaded_accounts.saturating_add(num_table_loaded_accounts); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // the number of loaded accounts must be <= 256 since account indices are | ||||||
|  |         // encoded as `u8` | ||||||
|  |         if num_loaded_accounts > 256 { | ||||||
|  |             return Err(SanitizeError::IndexOutOfBounds); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for ci in &self.instructions { | ||||||
|  |             if usize::from(ci.program_id_index) >= num_loaded_accounts { | ||||||
|  |                 return Err(SanitizeError::IndexOutOfBounds); | ||||||
|  |             } | ||||||
|  |             // A program cannot be a payer. | ||||||
|  |             if ci.program_id_index == 0 { | ||||||
|  |                 return Err(SanitizeError::IndexOutOfBounds); | ||||||
|  |             } | ||||||
|  |             for ai in &ci.accounts { | ||||||
|  |                 if usize::from(*ai) >= num_loaded_accounts { | ||||||
|  |                     return Err(SanitizeError::IndexOutOfBounds); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Message { | ||||||
|  |     /// Serialize this message with a version #0 prefix using bincode encoding. | ||||||
|  |     pub fn serialize(&self) -> Vec<u8> { | ||||||
|  |         bincode::serialize(&(MESSAGE_VERSION_PREFIX, self)).unwrap() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use { | ||||||
|  |         super::*, | ||||||
|  |         crate::message::VersionedMessage, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_ok()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_instruction() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique(), Pubkey::new_unique()], | ||||||
|  |             instructions: vec![CompiledInstruction { | ||||||
|  |                 program_id_index: 1, | ||||||
|  |                 accounts: vec![0], | ||||||
|  |                 data: vec![] | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_ok()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_table_lookup() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             address_table_lookups: vec![MessageAddressTableLookup { | ||||||
|  |                 account_key: Pubkey::new_unique(), | ||||||
|  |                 writable_indexes: vec![1, 2, 3], | ||||||
|  |                 readonly_indexes: vec![0], | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_ok()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_table_lookup_and_ix() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             address_table_lookups: vec![MessageAddressTableLookup { | ||||||
|  |                 account_key: Pubkey::new_unique(), | ||||||
|  |                 writable_indexes: vec![1, 2, 3], | ||||||
|  |                 readonly_indexes: vec![0], | ||||||
|  |             }], | ||||||
|  |             instructions: vec![CompiledInstruction { | ||||||
|  |                 program_id_index: 4, | ||||||
|  |                 accounts: vec![0, 1, 2, 3], | ||||||
|  |                 data: vec![] | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_ok()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_without_signer() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader::default(), | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_err()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_without_writable_signer() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 num_readonly_signed_accounts: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_err()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_empty_table_lookup() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             address_table_lookups: vec![MessageAddressTableLookup { | ||||||
|  |                 account_key: Pubkey::new_unique(), | ||||||
|  |                 writable_indexes: vec![], | ||||||
|  |                 readonly_indexes: vec![], | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_err()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_max_account_keys() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(), | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_ok()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_too_many_account_keys() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: (0..=256).map(|_| Pubkey::new_unique()).collect(), | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_err()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_max_table_loaded_keys() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             address_table_lookups: vec![MessageAddressTableLookup { | ||||||
|  |                 account_key: Pubkey::new_unique(), | ||||||
|  |                 writable_indexes: (0..=254).step_by(2).collect(), | ||||||
|  |                 readonly_indexes: (1..=254).step_by(2).collect(), | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_ok()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_too_many_table_loaded_keys() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             address_table_lookups: vec![MessageAddressTableLookup { | ||||||
|  |                 account_key: Pubkey::new_unique(), | ||||||
|  |                 writable_indexes: (0..=255).step_by(2).collect(), | ||||||
|  |                 readonly_indexes: (1..=255).step_by(2).collect(), | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_err()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_invalid_ix_program_id() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             address_table_lookups: vec![MessageAddressTableLookup { | ||||||
|  |                 account_key: Pubkey::new_unique(), | ||||||
|  |                 writable_indexes: vec![0], | ||||||
|  |                 readonly_indexes: vec![], | ||||||
|  |             }], | ||||||
|  |             instructions: vec![CompiledInstruction { | ||||||
|  |                 program_id_index: 2, | ||||||
|  |                 accounts: vec![], | ||||||
|  |                 data: vec![] | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_err()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_sanitize_with_invalid_ix_account() { | ||||||
|  |         assert!(Message { | ||||||
|  |             header: MessageHeader { | ||||||
|  |                 num_required_signatures: 1, | ||||||
|  |                 ..MessageHeader::default() | ||||||
|  |             }, | ||||||
|  |             account_keys: vec![Pubkey::new_unique()], | ||||||
|  |             address_table_lookups: vec![MessageAddressTableLookup { | ||||||
|  |                 account_key: Pubkey::new_unique(), | ||||||
|  |                 writable_indexes: vec![], | ||||||
|  |                 readonly_indexes: vec![0], | ||||||
|  |             }], | ||||||
|  |             instructions: vec![CompiledInstruction { | ||||||
|  |                 program_id_index: 1, | ||||||
|  |                 accounts: vec![2], | ||||||
|  |                 data: vec![] | ||||||
|  |             }], | ||||||
|  |             ..Message::default() | ||||||
|  |         } | ||||||
|  |         .sanitize() | ||||||
|  |         .is_err()); | ||||||
|  |     } | ||||||
|  |     #[test] | ||||||
|  |     fn test_serialize() { | ||||||
|  |         let message = Message::default(); | ||||||
|  |         let versioned_msg = VersionedMessage::V0(message.clone()); | ||||||
|  |         assert_eq!(message.serialize(), versioned_msg.serialize()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -3,7 +3,10 @@ | |||||||
| use { | use { | ||||||
|     crate::{ |     crate::{ | ||||||
|         hash::Hash, |         hash::Hash, | ||||||
|         message::{v0, MappedAddresses, MappedMessage, SanitizedMessage, VersionedMessage}, |         message::{ | ||||||
|  |             v0::{self, LoadedAddresses}, | ||||||
|  |             SanitizedMessage, VersionedMessage, | ||||||
|  |         }, | ||||||
|         nonce::NONCED_TX_MARKER_IX_INDEX, |         nonce::NONCED_TX_MARKER_IX_INDEX, | ||||||
|         precompiles::verify_if_precompile, |         precompiles::verify_if_precompile, | ||||||
|         program_utils::limited_deserialize, |         program_utils::limited_deserialize, | ||||||
| @@ -37,21 +40,21 @@ pub struct TransactionAccountLocks<'a> { | |||||||
|  |  | ||||||
| impl SanitizedTransaction { | impl SanitizedTransaction { | ||||||
|     /// Create a sanitized transaction from an unsanitized transaction. |     /// Create a sanitized transaction from an unsanitized transaction. | ||||||
|     /// If the input transaction uses address maps, attempt to map the |     /// If the input transaction uses address tables, attempt to lookup | ||||||
|     /// transaction keys to full addresses. |     /// the address for each table index. | ||||||
|     pub fn try_create( |     pub fn try_create( | ||||||
|         tx: VersionedTransaction, |         tx: VersionedTransaction, | ||||||
|         message_hash: Hash, |         message_hash: Hash, | ||||||
|         is_simple_vote_tx: Option<bool>, |         is_simple_vote_tx: Option<bool>, | ||||||
|         address_mapper: impl Fn(&v0::Message) -> Result<MappedAddresses>, |         address_loader: impl Fn(&v0::Message) -> Result<LoadedAddresses>, | ||||||
|     ) -> Result<Self> { |     ) -> Result<Self> { | ||||||
|         tx.sanitize()?; |         tx.sanitize()?; | ||||||
|  |  | ||||||
|         let signatures = tx.signatures; |         let signatures = tx.signatures; | ||||||
|         let message = match tx.message { |         let message = match tx.message { | ||||||
|             VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(message), |             VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(message), | ||||||
|             VersionedMessage::V0(message) => SanitizedMessage::V0(MappedMessage { |             VersionedMessage::V0(message) => SanitizedMessage::V0(v0::LoadedMessage { | ||||||
|                 mapped_addresses: address_mapper(&message)?, |                 loaded_addresses: address_loader(&message)?, | ||||||
|                 message, |                 message, | ||||||
|             }), |             }), | ||||||
|         }; |         }; | ||||||
| @@ -125,9 +128,9 @@ impl SanitizedTransaction { | |||||||
|     pub fn to_versioned_transaction(&self) -> VersionedTransaction { |     pub fn to_versioned_transaction(&self) -> VersionedTransaction { | ||||||
|         let signatures = self.signatures.clone(); |         let signatures = self.signatures.clone(); | ||||||
|         match &self.message { |         match &self.message { | ||||||
|             SanitizedMessage::V0(mapped_msg) => VersionedTransaction { |             SanitizedMessage::V0(sanitized_msg) => VersionedTransaction { | ||||||
|                 signatures, |                 signatures, | ||||||
|                 message: VersionedMessage::V0(mapped_msg.message.clone()), |                 message: VersionedMessage::V0(sanitized_msg.message.clone()), | ||||||
|             }, |             }, | ||||||
|             SanitizedMessage::Legacy(message) => VersionedTransaction { |             SanitizedMessage::Legacy(message) => VersionedTransaction { | ||||||
|                 signatures, |                 signatures, | ||||||
| @@ -193,7 +196,7 @@ impl SanitizedTransaction { | |||||||
|     fn message_data(&self) -> Vec<u8> { |     fn message_data(&self) -> Vec<u8> { | ||||||
|         match &self.message { |         match &self.message { | ||||||
|             SanitizedMessage::Legacy(message) => message.serialize(), |             SanitizedMessage::Legacy(message) => message.serialize(), | ||||||
|             SanitizedMessage::V0(mapped_msg) => mapped_msg.message.serialize(), |             SanitizedMessage::V0(message) => message.serialize(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -76,7 +76,10 @@ mod test { | |||||||
|         solana_sdk::{ |         solana_sdk::{ | ||||||
|             hash::Hash, |             hash::Hash, | ||||||
|             instruction::CompiledInstruction, |             instruction::CompiledInstruction, | ||||||
|             message::{v0, MappedAddresses, MappedMessage, MessageHeader}, |             message::{ | ||||||
|  |                 v0::{self, LoadedAddresses}, | ||||||
|  |                 MessageHeader, | ||||||
|  |             }, | ||||||
|         }, |         }, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -125,7 +128,7 @@ mod test { | |||||||
|         let sanitized_message = SanitizedMessage::Legacy(message); |         let sanitized_message = SanitizedMessage::Legacy(message); | ||||||
|         assert_eq!(sanitized_message.extract_memos(), expected_memos); |         assert_eq!(sanitized_message.extract_memos(), expected_memos); | ||||||
|  |  | ||||||
|         let mapped_message = MappedMessage { |         let sanitized_message = SanitizedMessage::V0(v0::LoadedMessage { | ||||||
|             message: v0::Message { |             message: v0::Message { | ||||||
|                 header: MessageHeader { |                 header: MessageHeader { | ||||||
|                     num_required_signatures: 1, |                     num_required_signatures: 1, | ||||||
| @@ -136,12 +139,11 @@ mod test { | |||||||
|                 instructions: memo_instructions, |                 instructions: memo_instructions, | ||||||
|                 ..v0::Message::default() |                 ..v0::Message::default() | ||||||
|             }, |             }, | ||||||
|             mapped_addresses: MappedAddresses { |             loaded_addresses: LoadedAddresses { | ||||||
|                 writable: vec![], |                 writable: vec![], | ||||||
|                 readonly: vec![spl_memo_id_v1(), another_program_id, spl_memo_id_v3()], |                 readonly: vec![spl_memo_id_v1(), another_program_id, spl_memo_id_v3()], | ||||||
|             }, |             }, | ||||||
|         }; |         }); | ||||||
|         let sanitized_mapped_message = SanitizedMessage::V0(mapped_message); |         assert_eq!(sanitized_message.extract_memos(), expected_memos); | ||||||
|         assert_eq!(sanitized_mapped_message.extract_memos(), expected_memos); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user