lib.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #![cfg_attr(
  2. not(feature = "agave-unstable-api"),
  3. deprecated(
  4. since = "3.1.0",
  5. note = "This crate has been marked for formal inclusion in the Agave Unstable API. From \
  6. v4.0.0 onward, the `agave-unstable-api` crate feature must be specified to \
  7. acknowledge use of an interface that may break without warning."
  8. )
  9. )]
  10. #![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
  11. pub use self::legacy::{LegacyVersion1, LegacyVersion2};
  12. use {
  13. rand::{thread_rng, Rng},
  14. serde::{Deserialize, Serialize},
  15. solana_sanitize::Sanitize,
  16. solana_serde_varint as serde_varint,
  17. std::{convert::TryInto, fmt},
  18. };
  19. #[cfg_attr(feature = "frozen-abi", macro_use)]
  20. #[cfg(feature = "frozen-abi")]
  21. extern crate solana_frozen_abi_macro;
  22. mod legacy;
  23. #[derive(Debug, Eq, PartialEq)]
  24. pub enum ClientId {
  25. SolanaLabs,
  26. JitoLabs,
  27. Frankendancer,
  28. Agave,
  29. AgavePaladin,
  30. Firedancer,
  31. AgaveBam,
  32. Sig,
  33. // If new variants are added, update From<u16> and TryFrom<ClientId>.
  34. Unknown(u16),
  35. }
  36. #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
  37. #[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
  38. pub struct Version {
  39. #[serde(with = "serde_varint")]
  40. pub major: u16,
  41. #[serde(with = "serde_varint")]
  42. pub minor: u16,
  43. #[serde(with = "serde_varint")]
  44. pub patch: u16,
  45. pub commit: u32, // first 4 bytes of the sha1 commit hash
  46. pub feature_set: u32, // first 4 bytes of the FeatureSet identifier
  47. #[serde(with = "serde_varint")]
  48. client: u16,
  49. }
  50. impl Version {
  51. pub fn as_semver_version(&self) -> semver::Version {
  52. semver::Version::new(self.major as u64, self.minor as u64, self.patch as u64)
  53. }
  54. pub fn client(&self) -> ClientId {
  55. ClientId::from(self.client)
  56. }
  57. }
  58. fn compute_commit(sha1: Option<&'static str>) -> Option<u32> {
  59. u32::from_str_radix(sha1?.get(..8)?, /*radix:*/ 16).ok()
  60. }
  61. impl Default for Version {
  62. fn default() -> Self {
  63. let feature_set =
  64. u32::from_le_bytes(agave_feature_set::ID.as_ref()[..4].try_into().unwrap());
  65. Self {
  66. major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
  67. minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
  68. patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
  69. commit: compute_commit(option_env!("CI_COMMIT"))
  70. .or(compute_commit(option_env!("AGAVE_GIT_COMMIT_HASH")))
  71. .unwrap_or_else(|| thread_rng().gen::<u32>()),
  72. feature_set,
  73. // Other client implementations need to modify this line.
  74. client: u16::try_from(ClientId::Agave).unwrap(),
  75. }
  76. }
  77. }
  78. impl fmt::Display for Version {
  79. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  80. write!(f, "{}.{}.{}", self.major, self.minor, self.patch,)
  81. }
  82. }
  83. impl fmt::Debug for Version {
  84. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  85. write!(
  86. f,
  87. "{}.{}.{} (src:{:08x}; feat:{}, client:{:?})",
  88. self.major,
  89. self.minor,
  90. self.patch,
  91. self.commit,
  92. self.feature_set,
  93. self.client(),
  94. )
  95. }
  96. }
  97. impl Sanitize for Version {}
  98. impl From<u16> for ClientId {
  99. fn from(client: u16) -> Self {
  100. match client {
  101. 0u16 => Self::SolanaLabs,
  102. 1u16 => Self::JitoLabs,
  103. 2u16 => Self::Frankendancer,
  104. 3u16 => Self::Agave,
  105. 4u16 => Self::AgavePaladin,
  106. 5u16 => Self::Firedancer,
  107. 6u16 => Self::AgaveBam,
  108. 7u16 => Self::Sig,
  109. _ => Self::Unknown(client),
  110. }
  111. }
  112. }
  113. impl TryFrom<ClientId> for u16 {
  114. type Error = String;
  115. fn try_from(client: ClientId) -> Result<Self, Self::Error> {
  116. match client {
  117. ClientId::SolanaLabs => Ok(0u16),
  118. ClientId::JitoLabs => Ok(1u16),
  119. ClientId::Frankendancer => Ok(2u16),
  120. ClientId::Agave => Ok(3u16),
  121. ClientId::AgavePaladin => Ok(4u16),
  122. ClientId::Firedancer => Ok(5u16),
  123. ClientId::AgaveBam => Ok(6u16),
  124. ClientId::Sig => Ok(7u16),
  125. ClientId::Unknown(client @ 0u16..=7u16) => Err(format!("Invalid client: {client}")),
  126. ClientId::Unknown(client) => Ok(client),
  127. }
  128. }
  129. }
  130. #[macro_export]
  131. macro_rules! semver {
  132. () => {
  133. &*format!("{}", $crate::Version::default())
  134. };
  135. }
  136. #[macro_export]
  137. macro_rules! version {
  138. () => {
  139. &*format!("{:?}", $crate::Version::default())
  140. };
  141. }
  142. #[cfg(test)]
  143. mod test {
  144. use super::*;
  145. #[test]
  146. fn test_compute_commit() {
  147. assert_eq!(compute_commit(None), None);
  148. assert_eq!(compute_commit(Some("1234567890")), Some(0x1234_5678));
  149. assert_eq!(compute_commit(Some("HEAD")), None);
  150. assert_eq!(compute_commit(Some("garbagein")), None);
  151. }
  152. #[test]
  153. fn test_client_id() {
  154. assert_eq!(ClientId::from(0u16), ClientId::SolanaLabs);
  155. assert_eq!(ClientId::from(1u16), ClientId::JitoLabs);
  156. assert_eq!(ClientId::from(2u16), ClientId::Frankendancer);
  157. assert_eq!(ClientId::from(3u16), ClientId::Agave);
  158. assert_eq!(ClientId::from(4u16), ClientId::AgavePaladin);
  159. assert_eq!(ClientId::from(5u16), ClientId::Firedancer);
  160. assert_eq!(ClientId::from(6u16), ClientId::AgaveBam);
  161. assert_eq!(ClientId::from(7u16), ClientId::Sig);
  162. for client in 8u16..=u16::MAX {
  163. assert_eq!(ClientId::from(client), ClientId::Unknown(client));
  164. }
  165. assert_eq!(u16::try_from(ClientId::SolanaLabs), Ok(0u16));
  166. assert_eq!(u16::try_from(ClientId::JitoLabs), Ok(1u16));
  167. assert_eq!(u16::try_from(ClientId::Frankendancer), Ok(2u16));
  168. assert_eq!(u16::try_from(ClientId::Agave), Ok(3u16));
  169. assert_eq!(u16::try_from(ClientId::AgavePaladin), Ok(4u16));
  170. assert_eq!(u16::try_from(ClientId::Firedancer), Ok(5u16));
  171. assert_eq!(u16::try_from(ClientId::AgaveBam), Ok(6u16));
  172. assert_eq!(u16::try_from(ClientId::Sig), Ok(7u16));
  173. for client in 0..=7u16 {
  174. assert_eq!(
  175. u16::try_from(ClientId::Unknown(client)),
  176. Err(format!("Invalid client: {client}"))
  177. );
  178. }
  179. for client in 8u16..=u16::MAX {
  180. assert_eq!(u16::try_from(ClientId::Unknown(client)), Ok(client));
  181. }
  182. }
  183. }