From 1daad38b47d9e41827add682f6ba02ef9d680d38 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Fri, 16 Jul 2021 09:51:21 -0500 Subject: [PATCH 1/9] Version transaction message and add new message format --- sdk/program/src/message/mod.rs | 27 ++ .../src/{message.rs => message/original.rs} | 24 +- sdk/program/src/message/v0.rs | 383 ++++++++++++++++++ sdk/program/src/message/versions.rs | 263 ++++++++++++ 4 files changed, 675 insertions(+), 22 deletions(-) create mode 100644 sdk/program/src/message/mod.rs rename sdk/program/src/{message.rs => message/original.rs} (96%) create mode 100644 sdk/program/src/message/v0.rs create mode 100644 sdk/program/src/message/versions.rs diff --git a/sdk/program/src/message/mod.rs b/sdk/program/src/message/mod.rs new file mode 100644 index 00000000000000..fe16d69788fc79 --- /dev/null +++ b/sdk/program/src/message/mod.rs @@ -0,0 +1,27 @@ +//! A library for generating a message from a sequence of instructions + +mod original; +mod v0; +mod versions; + +pub use original::Message; + +pub const MESSAGE_HEADER_LENGTH: usize = 3; + +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] +#[serde(rename_all = "camelCase")] +pub struct MessageHeader { + /// The number of signatures required for this message to be considered valid. The + /// signatures must match the first `num_required_signatures` of `account_keys`. + /// NOTE: Serialization-related changes must be paired with the direct read at sigverify. + pub num_required_signatures: u8, + + /// The last num_readonly_signed_accounts of the signed keys are read-only accounts. Programs + /// may process multiple transactions that load read-only accounts within a single PoH entry, + /// but are not permitted to credit or debit lamports or modify account data. Transactions + /// targeting the same read-write account are evaluated sequentially. + pub num_readonly_signed_accounts: u8, + + /// The last num_readonly_unsigned_accounts of the unsigned keys are read-only accounts. + pub num_readonly_unsigned_accounts: u8, +} diff --git a/sdk/program/src/message.rs b/sdk/program/src/message/original.rs similarity index 96% rename from sdk/program/src/message.rs rename to sdk/program/src/message/original.rs index 535112487dd912..3325a9b84eeb1a 100644 --- a/sdk/program/src/message.rs +++ b/sdk/program/src/message/original.rs @@ -9,6 +9,7 @@ use crate::{ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, hash::Hash, instruction::{AccountMeta, CompiledInstruction, Instruction}, + message::MessageHeader, pubkey::Pubkey, short_vec, system_instruction, system_program, sysvar, }; @@ -163,27 +164,6 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec { .collect() } -pub const MESSAGE_HEADER_LENGTH: usize = 3; - -#[frozen_abi(digest = "BVC5RhetsNpheGipt5rUrkR6RDDUHtD5sCLK1UjymL4S")] -#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] -#[serde(rename_all = "camelCase")] -pub struct MessageHeader { - /// The number of signatures required for this message to be considered valid. The - /// signatures must match the first `num_required_signatures` of `account_keys`. - /// NOTE: Serialization-related changes must be paired with the direct read at sigverify. - pub num_required_signatures: u8, - - /// The last num_readonly_signed_accounts of the signed keys are read-only accounts. Programs - /// may process multiple transactions that load read-only accounts within a single PoH entry, - /// but are not permitted to credit or debit lamports or modify account data. Transactions - /// targeting the same read-write account are evaluated sequentially. - pub num_readonly_signed_accounts: u8, - - /// The last num_readonly_unsigned_accounts of the unsigned keys are read-only accounts. - pub num_readonly_unsigned_accounts: u8, -} - #[frozen_abi(digest = "2KnLEqfLcTBQqitE22Pp8JYkaqVVbAkGbCfdeHoyxcAU")] #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] #[serde(rename_all = "camelCase")] @@ -508,7 +488,7 @@ impl Message { #[cfg(test)] mod tests { use super::*; - use crate::{hash, instruction::AccountMeta}; + use crate::{hash, instruction::AccountMeta, message::MESSAGE_HEADER_LENGTH}; use std::collections::HashSet; #[test] diff --git a/sdk/program/src/message/v0.rs b/sdk/program/src/message/v0.rs new file mode 100644 index 00000000000000..e5ca462b8bdf8b --- /dev/null +++ b/sdk/program/src/message/v0.rs @@ -0,0 +1,383 @@ +#![allow(clippy::integer_arithmetic)] + +use super::MessageHeader; +use crate::{ + hash::Hash, + instruction::CompiledInstruction, + pubkey::Pubkey, + sanitize::{Sanitize, SanitizeError}, + short_vec, +}; + +/// Alternative version of `Message` that supports succinct account loading +/// through an on-chain address map. +#[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, + + /// List of address maps used to succinctly load additional accounts for + /// this transaction. + /// + /// # Notes + /// + /// The last `address_maps.len()` accounts of the read-only unsigned + /// accounts are loaded as address maps. + #[serde(with = "short_vec")] + pub address_maps: Vec, + + /// 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 indices will index into the list of addresses constructed from + /// the concatenation of `account_keys` and the `entries` of each address + /// map in sequential order. + #[serde(with = "short_vec")] + pub instructions: Vec, +} + +impl Sanitize for Message { + fn sanitize(&self) -> Result<(), SanitizeError> { + // signing area and read-only non-signing area should not + // overlap + if self.header.num_required_signatures as usize + + self.header.num_readonly_unsigned_accounts as usize + > 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. + if self.address_maps.len() > self.header.num_readonly_unsigned_accounts as usize { + return Err(SanitizeError::IndexOutOfBounds); + } + + // each map must load at least one entry + let mut num_loaded_accounts = self.account_keys.len(); + for map in &self.address_maps { + let num_loaded_map_entries = map + .read_only_entries + .len() + .saturating_add(map.writable_entries.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 ci.program_id_index as usize >= 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 *ai as usize >= num_loaded_accounts { + return Err(SanitizeError::IndexOutOfBounds); + } + } + } + + Ok(()) + } +} + +/// Address map specifies which entries to load and +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] +#[serde(rename_all = "camelCase")] +pub struct AddressMap { + /// List of map entries to load as read-only. + #[serde(with = "short_vec")] + pub read_only_entries: Vec, + /// List of map entries to load as read-write. + #[serde(with = "short_vec")] + pub writable_entries: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + + 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_maps: vec![AddressMap { + read_only_entries: vec![0], + writable_entries: vec![], + }], + ..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_maps: vec![ + AddressMap { + read_only_entries: vec![0], + writable_entries: vec![1], + }, + AddressMap { + read_only_entries: vec![1], + writable_entries: vec![0], + }, + ], + ..Message::default() + } + } + + #[test] + fn test_sanitize_account_indices() { + assert!(Message { + account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(), + address_maps: 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_maps: 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_maps: vec![ + AddressMap { + read_only_entries: (0..200).step_by(2).collect(), + writable_entries: (1..200).step_by(2).collect(), + }, + AddressMap { + read_only_entries: (0..53).step_by(2).collect(), + writable_entries: (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_maps: vec![ + AddressMap { + read_only_entries: (0..200).step_by(2).collect(), + writable_entries: (1..200).step_by(2).collect(), + }, + AddressMap { + read_only_entries: (0..52).step_by(2).collect(), + writable_entries: (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_maps: vec![], + ..simple_message() + } + .sanitize() + .is_ok()); + + assert!(Message { + account_keys: (0..257).map(|_| Pubkey::new_unique()).collect(), + address_maps: 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_maps: vec![ + AddressMap { + read_only_entries: (0..200).step_by(2).collect(), + writable_entries: (1..200).step_by(2).collect(), + }, + AddressMap { + read_only_entries: (0..53).step_by(2).collect(), + writable_entries: (1..53).step_by(2).collect(), + } + ], + ..two_map_message() + } + .sanitize() + .is_ok()); + + assert!(Message { + address_maps: vec![ + AddressMap { + read_only_entries: (0..200).step_by(2).collect(), + writable_entries: (1..200).step_by(2).collect(), + }, + AddressMap { + read_only_entries: (0..200).step_by(2).collect(), + writable_entries: (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_maps: vec![AddressMap { + read_only_entries: vec![], + writable_entries: vec![0], + }], + ..simple_message() + } + .sanitize() + .is_ok()); + + assert!(Message { + address_maps: vec![AddressMap { + read_only_entries: vec![0], + writable_entries: vec![], + }], + ..simple_message() + } + .sanitize() + .is_ok()); + + assert!(Message { + address_maps: vec![AddressMap { + read_only_entries: vec![], + writable_entries: vec![], + }], + ..simple_message() + } + .sanitize() + .is_err()); + } +} diff --git a/sdk/program/src/message/versions.rs b/sdk/program/src/message/versions.rs new file mode 100644 index 00000000000000..1625c929580e83 --- /dev/null +++ b/sdk/program/src/message/versions.rs @@ -0,0 +1,263 @@ +use { + crate::{ + hash::Hash, + instruction::CompiledInstruction, + message::{v0, Message, MessageHeader}, + pubkey::Pubkey, + sanitize::{Sanitize, SanitizeError}, + short_vec, + }, + serde::{ + de::{self, Deserializer, SeqAccess, Visitor}, + ser::{SerializeTuple, Serializer}, + {Deserialize, Serialize}, + }, + std::fmt, +}; + +/// Bit mask that indicates whether a serialized message is versioned. +const VERSION_PREFIX: u8 = 0x80; + +/// Message versions supported by the Solana runtime. +/// +/// # Serialization +/// +/// If the first bit is set, the remaining 7 bits will be used to determine +/// which message version is serialized starting from version `0`. If the first +/// is bit is not set, all bytes are used to encode the original `Message` +/// format. +#[frozen_abi(digest = "DeuuRstA25YgNwvzBmuBjK3jZobPA7Fz83DxnNi6cBX1")] +#[derive(Debug, PartialEq, Eq, Clone, AbiEnumVisitor, AbiExample)] +pub enum MessageVersions { + Original(Message), + V0(v0::Message), +} + +impl Sanitize for MessageVersions { + fn sanitize(&self) -> Result<(), SanitizeError> { + match self { + Self::Original(message) => message.sanitize(), + Self::V0(message) => message.sanitize(), + } + } +} + +impl Serialize for MessageVersions { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Original(message) => { + let mut seq = serializer.serialize_tuple(1)?; + seq.serialize_element(message)?; + seq.end() + } + Self::V0(message) => { + let mut seq = serializer.serialize_tuple(2)?; + seq.serialize_element(&VERSION_PREFIX)?; + seq.serialize_element(message)?; + seq.end() + } + } + } +} + +enum MessagePrefix { + Original(u8), + Versioned(u8), +} + +impl<'de> Deserialize<'de> for MessagePrefix { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PrefixVisitor; + + impl<'de> Visitor<'de> for PrefixVisitor { + type Value = MessagePrefix; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("message prefix byte") + } + + fn visit_u8(self, byte: u8) -> Result { + if byte & VERSION_PREFIX != 0 { + Ok(MessagePrefix::Versioned(byte ^ VERSION_PREFIX)) + } else { + Ok(MessagePrefix::Original(byte)) + } + } + } + + deserializer.deserialize_u8(PrefixVisitor) + } +} + +impl<'de> Deserialize<'de> for MessageVersions { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct MessageVisitor; + + impl<'de> Visitor<'de> for MessageVisitor { + type Value = MessageVersions; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("message bytes") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let prefix: MessagePrefix = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + + match prefix { + MessagePrefix::Original(num_required_signatures) => { + // The remaining fields of the original Message struct after the first byte. + #[derive(Serialize, Deserialize)] + struct RemainingMessage { + pub num_readonly_signed_accounts: u8, + pub num_readonly_unsigned_accounts: u8, + #[serde(with = "short_vec")] + pub account_keys: Vec, + pub recent_blockhash: Hash, + #[serde(with = "short_vec")] + pub instructions: Vec, + } + + let rm: RemainingMessage = seq.next_element()?.ok_or_else(|| { + // will never happen since tuple length is always 2 + de::Error::invalid_length(1, &self) + })?; + + Ok(MessageVersions::Original(Message { + header: MessageHeader { + num_required_signatures, + num_readonly_signed_accounts: rm.num_readonly_signed_accounts, + num_readonly_unsigned_accounts: rm.num_readonly_unsigned_accounts, + }, + account_keys: rm.account_keys, + recent_blockhash: rm.recent_blockhash, + instructions: rm.instructions, + })) + } + MessagePrefix::Versioned(version) => { + if version == 0 { + Ok(MessageVersions::V0(seq.next_element()?.ok_or_else( + || { + // will never happen since tuple length is always 2 + de::Error::invalid_length(1, &self) + }, + )?)) + } else { + Err(de::Error::invalid_value( + de::Unexpected::Unsigned(version as u64), + &"supported versions: [0]", + )) + } + } + } + } + } + + deserializer.deserialize_tuple(2, MessageVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + instruction::{AccountMeta, Instruction}, + message::v0::AddressMap, + }; + + #[test] + fn test_original_message_serialization() { + let program_id0 = Pubkey::new_unique(); + let program_id1 = Pubkey::new_unique(); + let id0 = Pubkey::new_unique(); + let id1 = Pubkey::new_unique(); + let id2 = Pubkey::new_unique(); + let id3 = Pubkey::new_unique(); + let instructions = vec![ + Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]), + Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]), + Instruction::new_with_bincode( + program_id1, + &0, + vec![AccountMeta::new_readonly(id2, false)], + ), + Instruction::new_with_bincode( + program_id1, + &0, + vec![AccountMeta::new_readonly(id3, true)], + ), + ]; + + let mut message = Message::new(&instructions, Some(&id1)); + message.recent_blockhash = Hash::new_unique(); + + let bytes1 = bincode::serialize(&message).unwrap(); + let bytes2 = bincode::serialize(&MessageVersions::Original(message.clone())).unwrap(); + + assert_eq!(bytes1, bytes2); + + let message1: Message = bincode::deserialize(&bytes1).unwrap(); + let message2: MessageVersions = bincode::deserialize(&bytes2).unwrap(); + + if let MessageVersions::Original(message2) = message2 { + assert_eq!(message, message1); + assert_eq!(message1, message2); + } else { + panic!("should deserialize to original message"); + } + } + + #[test] + fn test_versioned_message_serialization() { + let message = v0::Message { + header: MessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 2, + }, + recent_blockhash: Hash::new_unique(), + account_keys: vec![ + Pubkey::new_unique(), + Pubkey::new_unique(), + Pubkey::new_unique(), + ], + address_maps: vec![ + AddressMap { + read_only_entries: vec![0], + writable_entries: vec![1], + }, + AddressMap { + read_only_entries: vec![1], + writable_entries: vec![0], + }, + ], + instructions: vec![CompiledInstruction { + program_id_index: 1, + accounts: vec![0], + data: vec![], + }], + }; + + let bytes = bincode::serialize(&MessageVersions::V0(message.clone())).unwrap(); + let message_from_bytes: MessageVersions = bincode::deserialize(&bytes).unwrap(); + + if let MessageVersions::V0(message_from_bytes) = message_from_bytes { + assert_eq!(message, message_from_bytes); + } else { + panic!("should deserialize to versioned message"); + } + } +} From 7bb3a8aa3e31ac0f0774600d71490d2d9fda9a9b Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Wed, 21 Jul 2021 09:17:19 -0500 Subject: [PATCH 2/9] Update abi digest due to message path change --- gossip/src/cluster_info.rs | 2 +- sdk/src/transaction.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gossip/src/cluster_info.rs b/gossip/src/cluster_info.rs index e4725d485e997f..6011d427a214c5 100644 --- a/gossip/src/cluster_info.rs +++ b/gossip/src/cluster_info.rs @@ -252,7 +252,7 @@ pub fn make_accounts_hashes_message( pub(crate) type Ping = ping_pong::Ping<[u8; GOSSIP_PING_TOKEN_SIZE]>; // TODO These messages should go through the gpu pipeline for spam filtering -#[frozen_abi(digest = "4khbdefBamDC8XpdahkW4bzkGX6N5c8PcHp3kBXJGg46")] +#[frozen_abi(digest = "E9L7gLXScuPpap3vUqTYe7KTa58noJq7aghj4QLiKoRi")] #[derive(Serialize, Deserialize, Debug, AbiEnumVisitor, AbiExample)] #[allow(clippy::large_enum_variant)] pub(crate) enum Protocol { diff --git a/sdk/src/transaction.rs b/sdk/src/transaction.rs index b38b74d5781079..2f3a157c2dba06 100644 --- a/sdk/src/transaction.rs +++ b/sdk/src/transaction.rs @@ -110,7 +110,7 @@ impl From for TransactionError { } /// An atomic transaction -#[frozen_abi(digest = "AAeVxvWiiotwxDLxKLxsfgkA6ndW74nVbaAEb6cwJYqR")] +#[frozen_abi(digest = "AJho2K4eY1U3LuMJS9HuUqJBiLY4AHx4ivxXfA2KrrcC")] #[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize, AbiExample)] pub struct Transaction { /// A set of digital signatures of a serialized [`Message`], signed by the From 51921fa9d72c86265b0494f49e905ba10a3350b1 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Wed, 21 Jul 2021 22:13:22 -0500 Subject: [PATCH 3/9] Update v0.rs Fix comment --- sdk/program/src/message/v0.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/program/src/message/v0.rs b/sdk/program/src/message/v0.rs index e5ca462b8bdf8b..3b3978ee31b8a0 100644 --- a/sdk/program/src/message/v0.rs +++ b/sdk/program/src/message/v0.rs @@ -107,7 +107,8 @@ impl Sanitize for Message { } } -/// Address map specifies which entries to load and +/// Address map specifies read-only and writable entries +/// to load for the transaction. #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] #[serde(rename_all = "camelCase")] pub struct AddressMap { From a5ef863c3fac7db4db8bb731382266424047d466 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Wed, 21 Jul 2021 22:15:08 -0500 Subject: [PATCH 4/9] Update original.rs --- sdk/program/src/message/original.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/program/src/message/original.rs b/sdk/program/src/message/original.rs index 3325a9b84eeb1a..934ce332f054f1 100644 --- a/sdk/program/src/message/original.rs +++ b/sdk/program/src/message/original.rs @@ -164,6 +164,8 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec { .collect() } +// NOTE: Serialization-related changes must be paired with the custom serialization +// for versioned messages in the `RemainingMessage` struct. #[frozen_abi(digest = "2KnLEqfLcTBQqitE22Pp8JYkaqVVbAkGbCfdeHoyxcAU")] #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] #[serde(rename_all = "camelCase")] From 955456e75f81d48f9ebf2adf77056520dcf45610 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Sat, 7 Aug 2021 10:04:34 -0700 Subject: [PATCH 5/9] Update message versions name and address map indexes field name --- sdk/program/src/message/original.rs | 2 +- sdk/program/src/message/v0.rs | 158 ++++++++++++++-------------- sdk/program/src/message/versions.rs | 62 ++++++----- 3 files changed, 114 insertions(+), 108 deletions(-) diff --git a/sdk/program/src/message/original.rs b/sdk/program/src/message/original.rs index 934ce332f054f1..8e2094a269fb45 100644 --- a/sdk/program/src/message/original.rs +++ b/sdk/program/src/message/original.rs @@ -19,7 +19,7 @@ use std::{convert::TryFrom, str::FromStr}; lazy_static! { // Copied keys over since direct references create cyclical dependency. - static ref BUILTIN_PROGRAMS_KEYS: [Pubkey; 10] = { + pub static ref BUILTIN_PROGRAMS_KEYS: [Pubkey; 10] = { let parse = |s| Pubkey::from_str(s).unwrap(); [ parse("Config1111111111111111111111111111111111111"), diff --git a/sdk/program/src/message/v0.rs b/sdk/program/src/message/v0.rs index 3b3978ee31b8a0..f945233c30f7cc 100644 --- a/sdk/program/src/message/v0.rs +++ b/sdk/program/src/message/v0.rs @@ -1,16 +1,27 @@ #![allow(clippy::integer_arithmetic)] -use super::MessageHeader; use crate::{ hash::Hash, instruction::CompiledInstruction, + message::MessageHeader, pubkey::Pubkey, sanitize::{Sanitize, SanitizeError}, short_vec, }; -/// Alternative version of `Message` that supports succinct account loading -/// through an on-chain address map. +/// 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, + #[serde(with = "short_vec")] + pub readonly: Vec, +} + +/// 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 { @@ -21,15 +32,15 @@ pub struct Message { #[serde(with = "short_vec")] pub account_keys: Vec, - /// List of address maps used to succinctly load additional accounts for - /// this transaction. + /// List of address map indexes used to succinctly load additional accounts + /// for this transaction. /// /// # Notes /// - /// The last `address_maps.len()` accounts of the read-only unsigned + /// The last `address_map_indexes.len()` accounts of the read-only unsigned /// accounts are loaded as address maps. #[serde(with = "short_vec")] - pub address_maps: Vec, + pub address_map_indexes: Vec, /// The blockhash of a recent block. pub recent_blockhash: Hash, @@ -39,9 +50,10 @@ pub struct Message { /// /// # Notes /// - /// Account indices will index into the list of addresses constructed from - /// the concatenation of `account_keys` and the `entries` of each address - /// map in sequential order. + /// 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, } @@ -63,17 +75,18 @@ impl Sanitize for Message { } // there cannot be more address maps than read-only unsigned accounts. - if self.address_maps.len() > self.header.num_readonly_unsigned_accounts as usize { + let num_address_map_indexes = self.address_map_indexes.len(); + if num_address_map_indexes > self.header.num_readonly_unsigned_accounts as usize { return Err(SanitizeError::IndexOutOfBounds); } // each map must load at least one entry let mut num_loaded_accounts = self.account_keys.len(); - for map in &self.address_maps { - let num_loaded_map_entries = map - .read_only_entries + for indexes in &self.address_map_indexes { + let num_loaded_map_entries = indexes + .writable .len() - .saturating_add(map.writable_entries.len()); + .saturating_add(indexes.readonly.len()); if num_loaded_map_entries == 0 { return Err(SanitizeError::InvalidValue); @@ -107,19 +120,6 @@ impl Sanitize for Message { } } -/// Address map specifies read-only and writable entries -/// to load for the transaction. -#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] -#[serde(rename_all = "camelCase")] -pub struct AddressMap { - /// List of map entries to load as read-only. - #[serde(with = "short_vec")] - pub read_only_entries: Vec, - /// List of map entries to load as read-write. - #[serde(with = "short_vec")] - pub writable_entries: Vec, -} - #[cfg(test)] mod tests { use super::*; @@ -132,9 +132,9 @@ mod tests { num_readonly_unsigned_accounts: 1, }, account_keys: vec![Pubkey::new_unique(), Pubkey::new_unique()], - address_maps: vec![AddressMap { - read_only_entries: vec![0], - writable_entries: vec![], + address_map_indexes: vec![AddressMapIndexes { + writable: vec![], + readonly: vec![0], }], ..Message::default() } @@ -152,14 +152,14 @@ mod tests { Pubkey::new_unique(), Pubkey::new_unique(), ], - address_maps: vec![ - AddressMap { - read_only_entries: vec![0], - writable_entries: vec![1], + address_map_indexes: vec![ + AddressMapIndexes { + writable: vec![1], + readonly: vec![0], }, - AddressMap { - read_only_entries: vec![1], - writable_entries: vec![0], + AddressMapIndexes { + writable: vec![0], + readonly: vec![1], }, ], ..Message::default() @@ -170,7 +170,7 @@ mod tests { fn test_sanitize_account_indices() { assert!(Message { account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(), - address_maps: vec![], + address_map_indexes: vec![], instructions: vec![CompiledInstruction { program_id_index: 1, accounts: vec![u8::MAX], @@ -183,7 +183,7 @@ mod tests { assert!(Message { account_keys: (0..u8::MAX).map(|_| Pubkey::new_unique()).collect(), - address_maps: vec![], + address_map_indexes: vec![], instructions: vec![CompiledInstruction { program_id_index: 1, accounts: vec![u8::MAX], @@ -219,14 +219,14 @@ mod tests { .is_err()); assert!(Message { - address_maps: vec![ - AddressMap { - read_only_entries: (0..200).step_by(2).collect(), - writable_entries: (1..200).step_by(2).collect(), + address_map_indexes: vec![ + AddressMapIndexes { + writable: (0..200).step_by(2).collect(), + readonly: (1..200).step_by(2).collect(), }, - AddressMap { - read_only_entries: (0..53).step_by(2).collect(), - writable_entries: (1..53).step_by(2).collect(), + AddressMapIndexes { + writable: (0..53).step_by(2).collect(), + readonly: (1..53).step_by(2).collect(), }, ], instructions: vec![CompiledInstruction { @@ -240,14 +240,14 @@ mod tests { .is_ok()); assert!(Message { - address_maps: vec![ - AddressMap { - read_only_entries: (0..200).step_by(2).collect(), - writable_entries: (1..200).step_by(2).collect(), + address_map_indexes: vec![ + AddressMapIndexes { + writable: (0..200).step_by(2).collect(), + readonly: (1..200).step_by(2).collect(), }, - AddressMap { - read_only_entries: (0..52).step_by(2).collect(), - writable_entries: (1..52).step_by(2).collect(), + AddressMapIndexes { + writable: (0..52).step_by(2).collect(), + readonly: (1..52).step_by(2).collect(), }, ], instructions: vec![CompiledInstruction { @@ -265,7 +265,7 @@ mod tests { fn test_sanitize_excessive_loaded_accounts() { assert!(Message { account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(), - address_maps: vec![], + address_map_indexes: vec![], ..simple_message() } .sanitize() @@ -273,7 +273,7 @@ mod tests { assert!(Message { account_keys: (0..257).map(|_| Pubkey::new_unique()).collect(), - address_maps: vec![], + address_map_indexes: vec![], ..simple_message() } .sanitize() @@ -294,14 +294,14 @@ mod tests { .is_err()); assert!(Message { - address_maps: vec![ - AddressMap { - read_only_entries: (0..200).step_by(2).collect(), - writable_entries: (1..200).step_by(2).collect(), + address_map_indexes: vec![ + AddressMapIndexes { + writable: (0..200).step_by(2).collect(), + readonly: (1..200).step_by(2).collect(), }, - AddressMap { - read_only_entries: (0..53).step_by(2).collect(), - writable_entries: (1..53).step_by(2).collect(), + AddressMapIndexes { + writable: (0..53).step_by(2).collect(), + readonly: (1..53).step_by(2).collect(), } ], ..two_map_message() @@ -310,14 +310,14 @@ mod tests { .is_ok()); assert!(Message { - address_maps: vec![ - AddressMap { - read_only_entries: (0..200).step_by(2).collect(), - writable_entries: (1..200).step_by(2).collect(), + address_map_indexes: vec![ + AddressMapIndexes { + writable: (0..200).step_by(2).collect(), + readonly: (1..200).step_by(2).collect(), }, - AddressMap { - read_only_entries: (0..200).step_by(2).collect(), - writable_entries: (1..200).step_by(2).collect(), + AddressMapIndexes { + writable: (0..200).step_by(2).collect(), + readonly: (1..200).step_by(2).collect(), } ], ..two_map_message() @@ -352,9 +352,9 @@ mod tests { #[test] fn test_sanitize_address_map() { assert!(Message { - address_maps: vec![AddressMap { - read_only_entries: vec![], - writable_entries: vec![0], + address_map_indexes: vec![AddressMapIndexes { + writable: vec![0], + readonly: vec![], }], ..simple_message() } @@ -362,9 +362,9 @@ mod tests { .is_ok()); assert!(Message { - address_maps: vec![AddressMap { - read_only_entries: vec![0], - writable_entries: vec![], + address_map_indexes: vec![AddressMapIndexes { + writable: vec![], + readonly: vec![0], }], ..simple_message() } @@ -372,9 +372,9 @@ mod tests { .is_ok()); assert!(Message { - address_maps: vec![AddressMap { - read_only_entries: vec![], - writable_entries: vec![], + address_map_indexes: vec![AddressMapIndexes { + writable: vec![], + readonly: vec![], }], ..simple_message() } diff --git a/sdk/program/src/message/versions.rs b/sdk/program/src/message/versions.rs index 1625c929580e83..e8a44cacea4dc8 100644 --- a/sdk/program/src/message/versions.rs +++ b/sdk/program/src/message/versions.rs @@ -16,7 +16,7 @@ use { }; /// Bit mask that indicates whether a serialized message is versioned. -const VERSION_PREFIX: u8 = 0x80; +pub const MESSAGE_VERSION_PREFIX: u8 = 0x80; /// Message versions supported by the Solana runtime. /// @@ -26,14 +26,20 @@ const VERSION_PREFIX: u8 = 0x80; /// which message version is serialized starting from version `0`. If the first /// is bit is not set, all bytes are used to encode the original `Message` /// format. -#[frozen_abi(digest = "DeuuRstA25YgNwvzBmuBjK3jZobPA7Fz83DxnNi6cBX1")] +#[frozen_abi(digest = "5hAeXsHoMFfGREfh8RqJYmAjRcs88sT1YxrpJJq3Ne1f")] #[derive(Debug, PartialEq, Eq, Clone, AbiEnumVisitor, AbiExample)] -pub enum MessageVersions { +pub enum VersionedMessage { Original(Message), V0(v0::Message), } -impl Sanitize for MessageVersions { +impl Default for VersionedMessage { + fn default() -> Self { + Self::Original(Message::default()) + } +} + +impl Sanitize for VersionedMessage { fn sanitize(&self) -> Result<(), SanitizeError> { match self { Self::Original(message) => message.sanitize(), @@ -42,7 +48,7 @@ impl Sanitize for MessageVersions { } } -impl Serialize for MessageVersions { +impl Serialize for VersionedMessage { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -55,7 +61,7 @@ impl Serialize for MessageVersions { } Self::V0(message) => { let mut seq = serializer.serialize_tuple(2)?; - seq.serialize_element(&VERSION_PREFIX)?; + seq.serialize_element(&MESSAGE_VERSION_PREFIX)?; seq.serialize_element(message)?; seq.end() } @@ -83,8 +89,8 @@ impl<'de> Deserialize<'de> for MessagePrefix { } fn visit_u8(self, byte: u8) -> Result { - if byte & VERSION_PREFIX != 0 { - Ok(MessagePrefix::Versioned(byte ^ VERSION_PREFIX)) + if byte & MESSAGE_VERSION_PREFIX != 0 { + Ok(MessagePrefix::Versioned(byte & !MESSAGE_VERSION_PREFIX)) } else { Ok(MessagePrefix::Original(byte)) } @@ -95,21 +101,21 @@ impl<'de> Deserialize<'de> for MessagePrefix { } } -impl<'de> Deserialize<'de> for MessageVersions { - fn deserialize(deserializer: D) -> Result +impl<'de> Deserialize<'de> for VersionedMessage { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct MessageVisitor; impl<'de> Visitor<'de> for MessageVisitor { - type Value = MessageVersions; + type Value = VersionedMessage; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("message bytes") } - fn visit_seq(self, mut seq: A) -> Result + fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { @@ -136,7 +142,7 @@ impl<'de> Deserialize<'de> for MessageVersions { de::Error::invalid_length(1, &self) })?; - Ok(MessageVersions::Original(Message { + Ok(VersionedMessage::Original(Message { header: MessageHeader { num_required_signatures, num_readonly_signed_accounts: rm.num_readonly_signed_accounts, @@ -149,7 +155,7 @@ impl<'de> Deserialize<'de> for MessageVersions { } MessagePrefix::Versioned(version) => { if version == 0 { - Ok(MessageVersions::V0(seq.next_element()?.ok_or_else( + Ok(VersionedMessage::V0(seq.next_element()?.ok_or_else( || { // will never happen since tuple length is always 2 de::Error::invalid_length(1, &self) @@ -175,7 +181,7 @@ mod tests { use super::*; use crate::{ instruction::{AccountMeta, Instruction}, - message::v0::AddressMap, + message::v0::AddressMapIndexes, }; #[test] @@ -205,14 +211,14 @@ mod tests { message.recent_blockhash = Hash::new_unique(); let bytes1 = bincode::serialize(&message).unwrap(); - let bytes2 = bincode::serialize(&MessageVersions::Original(message.clone())).unwrap(); + let bytes2 = bincode::serialize(&VersionedMessage::Original(message.clone())).unwrap(); assert_eq!(bytes1, bytes2); let message1: Message = bincode::deserialize(&bytes1).unwrap(); - let message2: MessageVersions = bincode::deserialize(&bytes2).unwrap(); + let message2: VersionedMessage = bincode::deserialize(&bytes2).unwrap(); - if let MessageVersions::Original(message2) = message2 { + if let VersionedMessage::Original(message2) = message2 { assert_eq!(message, message1); assert_eq!(message1, message2); } else { @@ -234,14 +240,14 @@ mod tests { Pubkey::new_unique(), Pubkey::new_unique(), ], - address_maps: vec![ - AddressMap { - read_only_entries: vec![0], - writable_entries: vec![1], + address_map_indexes: vec![ + AddressMapIndexes { + writable: vec![1], + readonly: vec![0], }, - AddressMap { - read_only_entries: vec![1], - writable_entries: vec![0], + AddressMapIndexes { + writable: vec![0], + readonly: vec![1], }, ], instructions: vec![CompiledInstruction { @@ -251,10 +257,10 @@ mod tests { }], }; - let bytes = bincode::serialize(&MessageVersions::V0(message.clone())).unwrap(); - let message_from_bytes: MessageVersions = bincode::deserialize(&bytes).unwrap(); + let bytes = bincode::serialize(&VersionedMessage::V0(message.clone())).unwrap(); + let message_from_bytes: VersionedMessage = bincode::deserialize(&bytes).unwrap(); - if let MessageVersions::V0(message_from_bytes) = message_from_bytes { + if let VersionedMessage::V0(message_from_bytes) = message_from_bytes { assert_eq!(message, message_from_bytes); } else { panic!("should deserialize to versioned message"); From 2e3f4264a6a28aa80ca2e0ecb40f9a46207a3375 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Mon, 9 Aug 2021 11:12:49 -0700 Subject: [PATCH 6/9] s/original/legacy --- .../src/message/{original.rs => legacy.rs} | 0 sdk/program/src/message/mod.rs | 4 +- sdk/program/src/message/versions.rs | 42 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) rename sdk/program/src/message/{original.rs => legacy.rs} (100%) diff --git a/sdk/program/src/message/original.rs b/sdk/program/src/message/legacy.rs similarity index 100% rename from sdk/program/src/message/original.rs rename to sdk/program/src/message/legacy.rs diff --git a/sdk/program/src/message/mod.rs b/sdk/program/src/message/mod.rs index fe16d69788fc79..015eb19b15f110 100644 --- a/sdk/program/src/message/mod.rs +++ b/sdk/program/src/message/mod.rs @@ -1,10 +1,10 @@ //! A library for generating a message from a sequence of instructions -mod original; +mod legacy; mod v0; mod versions; -pub use original::Message; +pub use legacy::Message; pub const MESSAGE_HEADER_LENGTH: usize = 3; diff --git a/sdk/program/src/message/versions.rs b/sdk/program/src/message/versions.rs index e8a44cacea4dc8..3db1ed18d3c51c 100644 --- a/sdk/program/src/message/versions.rs +++ b/sdk/program/src/message/versions.rs @@ -24,25 +24,25 @@ pub const MESSAGE_VERSION_PREFIX: u8 = 0x80; /// /// If the first bit is set, the remaining 7 bits will be used to determine /// which message version is serialized starting from version `0`. If the first -/// is bit is not set, all bytes are used to encode the original `Message` +/// is bit is not set, all bytes are used to encode the legacy `Message` /// format. #[frozen_abi(digest = "5hAeXsHoMFfGREfh8RqJYmAjRcs88sT1YxrpJJq3Ne1f")] #[derive(Debug, PartialEq, Eq, Clone, AbiEnumVisitor, AbiExample)] pub enum VersionedMessage { - Original(Message), + Legacy(Message), V0(v0::Message), } impl Default for VersionedMessage { fn default() -> Self { - Self::Original(Message::default()) + Self::Legacy(Message::default()) } } impl Sanitize for VersionedMessage { fn sanitize(&self) -> Result<(), SanitizeError> { match self { - Self::Original(message) => message.sanitize(), + Self::Legacy(message) => message.sanitize(), Self::V0(message) => message.sanitize(), } } @@ -54,7 +54,7 @@ impl Serialize for VersionedMessage { S: Serializer, { match self { - Self::Original(message) => { + Self::Legacy(message) => { let mut seq = serializer.serialize_tuple(1)?; seq.serialize_element(message)?; seq.end() @@ -70,7 +70,7 @@ impl Serialize for VersionedMessage { } enum MessagePrefix { - Original(u8), + Legacy(u8), Versioned(u8), } @@ -92,7 +92,7 @@ impl<'de> Deserialize<'de> for MessagePrefix { if byte & MESSAGE_VERSION_PREFIX != 0 { Ok(MessagePrefix::Versioned(byte & !MESSAGE_VERSION_PREFIX)) } else { - Ok(MessagePrefix::Original(byte)) + Ok(MessagePrefix::Legacy(byte)) } } } @@ -124,10 +124,10 @@ impl<'de> Deserialize<'de> for VersionedMessage { .ok_or_else(|| de::Error::invalid_length(0, &self))?; match prefix { - MessagePrefix::Original(num_required_signatures) => { - // The remaining fields of the original Message struct after the first byte. + MessagePrefix::Legacy(num_required_signatures) => { + // The remaining fields of the legacy Message struct after the first byte. #[derive(Serialize, Deserialize)] - struct RemainingMessage { + struct RemainingLegacyMessage { pub num_readonly_signed_accounts: u8, pub num_readonly_unsigned_accounts: u8, #[serde(with = "short_vec")] @@ -137,20 +137,20 @@ impl<'de> Deserialize<'de> for VersionedMessage { pub instructions: Vec, } - let rm: RemainingMessage = seq.next_element()?.ok_or_else(|| { + let message: RemainingLegacyMessage = seq.next_element()?.ok_or_else(|| { // will never happen since tuple length is always 2 de::Error::invalid_length(1, &self) })?; - Ok(VersionedMessage::Original(Message { + Ok(VersionedMessage::Legacy(Message { header: MessageHeader { num_required_signatures, - num_readonly_signed_accounts: rm.num_readonly_signed_accounts, - num_readonly_unsigned_accounts: rm.num_readonly_unsigned_accounts, + num_readonly_signed_accounts: message.num_readonly_signed_accounts, + num_readonly_unsigned_accounts: message.num_readonly_unsigned_accounts, }, - account_keys: rm.account_keys, - recent_blockhash: rm.recent_blockhash, - instructions: rm.instructions, + account_keys: message.account_keys, + recent_blockhash: message.recent_blockhash, + instructions: message.instructions, })) } MessagePrefix::Versioned(version) => { @@ -185,7 +185,7 @@ mod tests { }; #[test] - fn test_original_message_serialization() { + fn test_legacy_message_serialization() { let program_id0 = Pubkey::new_unique(); let program_id1 = Pubkey::new_unique(); let id0 = Pubkey::new_unique(); @@ -211,18 +211,18 @@ mod tests { message.recent_blockhash = Hash::new_unique(); let bytes1 = bincode::serialize(&message).unwrap(); - let bytes2 = bincode::serialize(&VersionedMessage::Original(message.clone())).unwrap(); + let bytes2 = bincode::serialize(&VersionedMessage::Legacy(message.clone())).unwrap(); assert_eq!(bytes1, bytes2); let message1: Message = bincode::deserialize(&bytes1).unwrap(); let message2: VersionedMessage = bincode::deserialize(&bytes2).unwrap(); - if let VersionedMessage::Original(message2) = message2 { + if let VersionedMessage::Legacy(message2) = message2 { assert_eq!(message, message1); assert_eq!(message1, message2); } else { - panic!("should deserialize to original message"); + panic!("should deserialize to legacy message"); } } From 5fefb81d5162d04c22d747fbeb5276f431aed192 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Mon, 9 Aug 2021 11:14:13 -0700 Subject: [PATCH 7/9] update comment --- sdk/program/src/message/legacy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/program/src/message/legacy.rs b/sdk/program/src/message/legacy.rs index 8e2094a269fb45..22bdbc44eef3e8 100644 --- a/sdk/program/src/message/legacy.rs +++ b/sdk/program/src/message/legacy.rs @@ -165,7 +165,7 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec { } // NOTE: Serialization-related changes must be paired with the custom serialization -// for versioned messages in the `RemainingMessage` struct. +// for versioned messages in the `RemainingLegacyMessage` struct. #[frozen_abi(digest = "2KnLEqfLcTBQqitE22Pp8JYkaqVVbAkGbCfdeHoyxcAU")] #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)] #[serde(rename_all = "camelCase")] From fa2df7ebf99c9d6bbc19894223620f816ce69454 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Mon, 9 Aug 2021 11:38:13 -0700 Subject: [PATCH 8/9] cargo fmt --- sdk/program/src/message/versions.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sdk/program/src/message/versions.rs b/sdk/program/src/message/versions.rs index 3db1ed18d3c51c..f12bf10a23e041 100644 --- a/sdk/program/src/message/versions.rs +++ b/sdk/program/src/message/versions.rs @@ -137,16 +137,18 @@ impl<'de> Deserialize<'de> for VersionedMessage { pub instructions: Vec, } - let message: RemainingLegacyMessage = seq.next_element()?.ok_or_else(|| { - // will never happen since tuple length is always 2 - de::Error::invalid_length(1, &self) - })?; + let message: RemainingLegacyMessage = + seq.next_element()?.ok_or_else(|| { + // will never happen since tuple length is always 2 + de::Error::invalid_length(1, &self) + })?; Ok(VersionedMessage::Legacy(Message { header: MessageHeader { num_required_signatures, num_readonly_signed_accounts: message.num_readonly_signed_accounts, - num_readonly_unsigned_accounts: message.num_readonly_unsigned_accounts, + num_readonly_unsigned_accounts: message + .num_readonly_unsigned_accounts, }, account_keys: message.account_keys, recent_blockhash: message.recent_blockhash, From 5ad1a3dc4d9649efb1382fa607d858898f4b373b Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Mon, 9 Aug 2021 14:59:28 -0700 Subject: [PATCH 9/9] Update abi digest due to legacy rename --- gossip/src/cluster_info.rs | 2 +- sdk/program/src/message/versions.rs | 2 +- sdk/src/transaction.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gossip/src/cluster_info.rs b/gossip/src/cluster_info.rs index 6011d427a214c5..c93a10ce7bff0b 100644 --- a/gossip/src/cluster_info.rs +++ b/gossip/src/cluster_info.rs @@ -252,7 +252,7 @@ pub fn make_accounts_hashes_message( pub(crate) type Ping = ping_pong::Ping<[u8; GOSSIP_PING_TOKEN_SIZE]>; // TODO These messages should go through the gpu pipeline for spam filtering -#[frozen_abi(digest = "E9L7gLXScuPpap3vUqTYe7KTa58noJq7aghj4QLiKoRi")] +#[frozen_abi(digest = "AqKhoLDkFr85WPiZnXG4bcRwHU4qSSyDZ3MQZLk3cnJf")] #[derive(Serialize, Deserialize, Debug, AbiEnumVisitor, AbiExample)] #[allow(clippy::large_enum_variant)] pub(crate) enum Protocol { diff --git a/sdk/program/src/message/versions.rs b/sdk/program/src/message/versions.rs index f12bf10a23e041..3e5e581ff9c0c1 100644 --- a/sdk/program/src/message/versions.rs +++ b/sdk/program/src/message/versions.rs @@ -26,7 +26,7 @@ pub const MESSAGE_VERSION_PREFIX: u8 = 0x80; /// 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` /// format. -#[frozen_abi(digest = "5hAeXsHoMFfGREfh8RqJYmAjRcs88sT1YxrpJJq3Ne1f")] +#[frozen_abi(digest = "C4MZ7qztFJHUp1bVcuh7Gn43PQExadzEGyEb8UMn9unz")] #[derive(Debug, PartialEq, Eq, Clone, AbiEnumVisitor, AbiExample)] pub enum VersionedMessage { Legacy(Message), diff --git a/sdk/src/transaction.rs b/sdk/src/transaction.rs index 2f3a157c2dba06..42bfe5931b1215 100644 --- a/sdk/src/transaction.rs +++ b/sdk/src/transaction.rs @@ -110,7 +110,7 @@ impl From for TransactionError { } /// An atomic transaction -#[frozen_abi(digest = "AJho2K4eY1U3LuMJS9HuUqJBiLY4AHx4ivxXfA2KrrcC")] +#[frozen_abi(digest = "FZtncnS1Xk8ghHfKiXE5oGiUbw2wJhmfXQuNgQR3K6Mc")] #[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize, AbiExample)] pub struct Transaction { /// A set of digital signatures of a serialized [`Message`], signed by the