lib.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. use bincode::{deserialize, serialize};
  2. use libc::{c_int, size_t};
  3. use rand_chacha::ChaChaRng;
  4. use rand_core::SeedableRng;
  5. use solana_ed25519_dalek::{SignatureError, KEYPAIR_LENGTH, PUBLIC_KEY_LENGTH};
  6. use solana_sdk::hash::Hash;
  7. use solana_sdk::instruction::CompiledInstruction as CompiledInstructionNative;
  8. use solana_sdk::message::Message as MessageNative;
  9. use solana_sdk::message::MessageHeader as MessageHeaderNative;
  10. use solana_sdk::pubkey::Pubkey;
  11. use solana_sdk::signature::Signature as SignatureNative;
  12. use solana_sdk::signature::{Keypair as KeypairNative, KeypairUtil};
  13. use solana_sdk::transaction::Transaction as TransactionNative;
  14. use std::convert::TryInto;
  15. use std::ffi::CString;
  16. use std::os::raw::c_char;
  17. use std::vec::Vec;
  18. use std::{fmt, mem, ptr, slice};
  19. #[repr(C)]
  20. #[derive(Debug)]
  21. pub struct Transaction {
  22. /// A set of digital signatures of `account_keys`, `program_ids`, `recent_blockhash`, and `instructions`, signed by the first
  23. /// signatures_len keys of account_keys
  24. pub signatures: CVec<Signature>,
  25. /// The message to sign.
  26. pub message: Message,
  27. }
  28. impl Transaction {
  29. pub fn from_native(mut t: TransactionNative) -> Self {
  30. t.signatures.shrink_to_fit();
  31. Self {
  32. signatures: CVec::from_native(
  33. t.signatures
  34. .into_iter()
  35. .map(Signature::from_native)
  36. .collect(),
  37. ),
  38. message: Message::from_native(t.message),
  39. }
  40. }
  41. pub unsafe fn into_native(self) -> TransactionNative {
  42. TransactionNative {
  43. signatures: CVec::into_native(self.signatures)
  44. .iter()
  45. .map(|s| s.new_native())
  46. .collect(),
  47. message: self.message.into_native(),
  48. }
  49. }
  50. #[allow(clippy::should_implement_trait)]
  51. pub unsafe fn clone(&self) -> Self {
  52. Self {
  53. signatures: self.signatures.clone(),
  54. message: self.message.clone(),
  55. }
  56. }
  57. }
  58. #[repr(C)]
  59. #[derive(Debug)]
  60. pub struct Message {
  61. /// The message header, identifying signed and credit-only `account_keys`
  62. pub header: MessageHeader,
  63. /// All the account keys used by this transaction
  64. pub account_keys: CVec<Pubkey>,
  65. /// The id of a recent ledger entry.
  66. pub recent_blockhash: Hash,
  67. /// Programs that will be executed in sequence and committed in one atomic transaction if all
  68. /// succeed.
  69. pub instructions: CVec<CompiledInstruction>,
  70. }
  71. impl Message {
  72. pub fn from_native(m: MessageNative) -> Self {
  73. Self {
  74. header: MessageHeader::from_native(m.header),
  75. account_keys: CVec::from_native(m.account_keys),
  76. recent_blockhash: m.recent_blockhash,
  77. instructions: CVec::from_native(
  78. m.instructions
  79. .into_iter()
  80. .map(CompiledInstruction::from_native)
  81. .collect(),
  82. ),
  83. }
  84. }
  85. pub unsafe fn into_native(self) -> MessageNative {
  86. MessageNative {
  87. header: self.header.into_native(),
  88. account_keys: CVec::into_native(self.account_keys),
  89. recent_blockhash: self.recent_blockhash,
  90. instructions: CVec::into_native(self.instructions)
  91. .into_iter()
  92. .map(|i| i.into_native())
  93. .collect(),
  94. }
  95. }
  96. #[allow(clippy::should_implement_trait)]
  97. pub unsafe fn clone(&self) -> Self {
  98. Self {
  99. header: self.header.clone(),
  100. account_keys: self.account_keys.clone(),
  101. recent_blockhash: self.recent_blockhash,
  102. instructions: self.instructions.clone(),
  103. }
  104. }
  105. }
  106. /// An instruction to execute a program
  107. #[repr(C)]
  108. #[derive(Debug)]
  109. pub struct CompiledInstruction {
  110. /// Index into the transaction keys array indicating the program account that executes this instruction
  111. pub program_id_index: u8,
  112. /// Ordered indices into the transaction keys array indicating which accounts to pass to the program
  113. pub accounts: CVec<u8>,
  114. /// The program input data
  115. pub data: CVec<u8>,
  116. }
  117. impl CompiledInstruction {
  118. pub fn from_native(c: CompiledInstructionNative) -> Self {
  119. Self {
  120. program_id_index: c.program_id_index,
  121. accounts: CVec::from_native(c.accounts),
  122. data: CVec::from_native(c.data),
  123. }
  124. }
  125. pub unsafe fn into_native(self) -> CompiledInstructionNative {
  126. CompiledInstructionNative {
  127. program_id_index: self.program_id_index,
  128. accounts: CVec::into_native(self.accounts),
  129. data: CVec::into_native(self.data),
  130. }
  131. }
  132. #[allow(clippy::should_implement_trait)]
  133. pub unsafe fn clone(&self) -> Self {
  134. Self {
  135. program_id_index: self.program_id_index,
  136. accounts: self.accounts.clone(),
  137. data: self.data.clone(),
  138. }
  139. }
  140. }
  141. #[repr(C)]
  142. #[derive(Default, Debug, Clone)]
  143. pub struct MessageHeader {
  144. /// The number of signatures required for this message to be considered valid. The
  145. /// signatures must match the first `num_required_signatures` of `account_keys`.
  146. pub num_required_signatures: u8,
  147. /// The last num_credit_only_signed_accounts of the signed keys are credit-only accounts.
  148. /// Programs may process multiple transactions that add lamports to the same credit-only
  149. /// account within a single PoH entry, but are not permitted to debit lamports or modify
  150. /// account data. Transactions targeting the same debit account are evaluated sequentially.
  151. pub num_credit_only_signed_accounts: u8,
  152. /// The last num_credit_only_unsigned_accounts of the unsigned keys are credit-only accounts.
  153. pub num_credit_only_unsigned_accounts: u8,
  154. }
  155. impl MessageHeader {
  156. pub fn from_native(h: MessageHeaderNative) -> Self {
  157. Self {
  158. num_required_signatures: h.num_required_signatures,
  159. num_credit_only_signed_accounts: h.num_credit_only_signed_accounts,
  160. num_credit_only_unsigned_accounts: h.num_credit_only_unsigned_accounts,
  161. }
  162. }
  163. pub fn into_native(self) -> MessageHeaderNative {
  164. MessageHeaderNative {
  165. num_required_signatures: self.num_required_signatures,
  166. num_credit_only_signed_accounts: self.num_credit_only_signed_accounts,
  167. num_credit_only_unsigned_accounts: self.num_credit_only_unsigned_accounts,
  168. }
  169. }
  170. }
  171. #[repr(transparent)]
  172. #[derive(Clone, Copy)]
  173. pub struct Signature([u8; 64]);
  174. impl fmt::Debug for Signature {
  175. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  176. write!(f, "{}", bs58::encode(&self.0[..]).into_string())
  177. }
  178. }
  179. impl fmt::Display for Signature {
  180. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  181. write!(f, "{}", bs58::encode(&self.0[..]).into_string())
  182. }
  183. }
  184. impl Signature {
  185. pub fn from_native(s: SignatureNative) -> Self {
  186. Self(s.into())
  187. }
  188. pub fn new_native(&self) -> SignatureNative {
  189. SignatureNative::new(&self.0[..])
  190. }
  191. }
  192. #[repr(transparent)]
  193. #[derive(Clone)]
  194. pub struct Keypair([u8; KEYPAIR_LENGTH]);
  195. impl Keypair {
  196. pub fn from_native(k: &KeypairNative) -> Self {
  197. Self(k.to_bytes())
  198. }
  199. pub fn new_native(&self) -> Result<KeypairNative, SignatureError> {
  200. KeypairNative::from_bytes(&self.0[..])
  201. }
  202. }
  203. /// A representation of a Rust Vec that can be passed to C. Should not be modified or copied by C code.
  204. #[repr(C)]
  205. #[derive(Debug)]
  206. pub struct CVec<T> {
  207. data: *mut T,
  208. len: size_t,
  209. capacity: size_t,
  210. }
  211. impl<T> CVec<T> {
  212. pub fn from_native(mut v: Vec<T>) -> Self {
  213. let out = Self {
  214. data: v.as_mut_ptr(),
  215. len: v.len(),
  216. capacity: v.capacity(),
  217. };
  218. mem::forget(v);
  219. out
  220. }
  221. pub unsafe fn into_native(self) -> Vec<T> {
  222. Vec::from_raw_parts(self.data, self.len, self.capacity)
  223. }
  224. }
  225. impl<T: Clone> CVec<T> {
  226. #[allow(clippy::should_implement_trait)]
  227. pub unsafe fn clone(&self) -> Self {
  228. let native = Vec::from_raw_parts(self.data, self.len, self.capacity);
  229. let mut new: Vec<T> = Vec::with_capacity(native.capacity());
  230. <Vec<T> as Clone>::clone_from(&mut new, &native);
  231. mem::forget(native);
  232. Self::from_native(new)
  233. }
  234. }
  235. impl CVec<CompiledInstruction> {
  236. #[allow(clippy::should_implement_trait)]
  237. pub unsafe fn clone(&self) -> Self {
  238. let native = Vec::from_raw_parts(self.data, self.len, self.capacity);
  239. let mut new: Vec<CompiledInstruction> = Vec::with_capacity(native.capacity());
  240. for elem in &native {
  241. new.push(elem.clone());
  242. }
  243. mem::forget(native);
  244. Self::from_native(new)
  245. }
  246. }
  247. #[no_mangle]
  248. pub unsafe extern "C" fn free_transaction(tx: *mut Transaction) {
  249. Box::from_raw(tx);
  250. }
  251. #[no_mangle]
  252. pub unsafe extern "C" fn free_message(m: *mut Message) {
  253. Box::from_raw(m);
  254. }
  255. #[no_mangle]
  256. pub unsafe extern "C" fn free_message_header(mh: *mut MessageHeader) {
  257. Box::from_raw(mh);
  258. }
  259. #[no_mangle]
  260. pub unsafe extern "C" fn free_signature(s: *mut Signature) {
  261. Box::from_raw(s);
  262. }
  263. #[no_mangle]
  264. pub unsafe extern "C" fn free_compiled_instruction(i: *mut CompiledInstruction) {
  265. Box::from_raw(i);
  266. }
  267. #[no_mangle]
  268. pub unsafe extern "C" fn free_c_string(s: *mut c_char) {
  269. CString::from_raw(s);
  270. }
  271. #[no_mangle]
  272. pub unsafe extern "C" fn new_unsigned_transaction(message: *mut Message) -> *mut Transaction {
  273. let message = Box::from_raw(message);
  274. let tx = Box::new(Transaction::from_native(TransactionNative::new_unsigned(
  275. message.into_native(),
  276. )));
  277. Box::into_raw(tx)
  278. }
  279. /// Generate a `Keypair` from a uint8_t[32] seed. Assumes the pointer is to an array of length 32.
  280. ///
  281. /// # Undefined Behavior
  282. ///
  283. /// Causes UB if `seed` is not a pointer to an array of length 32 or if `seed` is `NULL`
  284. #[no_mangle]
  285. pub unsafe extern "C" fn generate_keypair(seed: *const u8) -> *mut Keypair {
  286. let seed = <&[u8] as TryInto<&[u8; PUBLIC_KEY_LENGTH]>>::try_into(slice::from_raw_parts(
  287. seed,
  288. PUBLIC_KEY_LENGTH,
  289. ))
  290. .unwrap(); // Guaranteed not to panic
  291. let mut rng = ChaChaRng::from_seed(*seed);
  292. let keypair = KeypairNative::generate(&mut rng);
  293. let keypair = Box::new(Keypair::from_native(&keypair));
  294. Box::into_raw(keypair)
  295. }
  296. /// Get a pubkey from a keypair. Returns `NULL` if the conversion causes an error
  297. ///
  298. /// # Undefined Behavior
  299. ///
  300. /// Causes UB if `keypair` is `NULL` or if `keypair` in not a pointer to a valid `Keypair`
  301. #[no_mangle]
  302. pub unsafe extern "C" fn get_keypair_pubkey(keypair: *const Keypair) -> *mut Pubkey {
  303. let keypair = if let Ok(k) = (*keypair).new_native() {
  304. k
  305. } else {
  306. return ptr::null_mut();
  307. };
  308. let pubkey = Box::new(keypair.pubkey());
  309. Box::into_raw(pubkey)
  310. }
  311. /// Serialize a `Transaction` and save a pointer to the resulting byte array to `serialized`, and its
  312. /// length to `len`. Returns `0` for success, other for failure. Consumes and frees the `Transaction`.
  313. ///
  314. /// # Undefined Behavior
  315. ///
  316. /// Causes UB if any of the input pointers is `NULL`, or if `tx` is not a valid `Transaction`
  317. #[no_mangle]
  318. pub unsafe extern "C" fn serialize_transaction(
  319. tx: *mut Transaction,
  320. serialized: *mut *const u8,
  321. len: *mut size_t,
  322. ) -> c_int {
  323. let tx = Box::from_raw(tx);
  324. let tx = tx.into_native();
  325. let mut serialized_tx = if let Ok(s) = serialize(&tx) {
  326. s
  327. } else {
  328. return 1;
  329. };
  330. serialized_tx.shrink_to_fit();
  331. *serialized = serialized_tx.as_mut_ptr();
  332. *len = serialized_tx.len();
  333. mem::forget(serialized_tx);
  334. 0
  335. }
  336. /// Deserialize an array of bytes into a `Transaction`. Returns `NULL` if deserialization fails
  337. ///
  338. /// # Undefined Behavior
  339. ///
  340. /// Causes UB if `bytes` is `NULL`, or if `bytes` does not point to a valid array of length `len`
  341. #[no_mangle]
  342. pub unsafe extern "C" fn deserialize_transaction(
  343. bytes: *const u8,
  344. len: size_t,
  345. ) -> *mut Transaction {
  346. let slice = slice::from_raw_parts(bytes, len);
  347. let tx = if let Ok(t) = deserialize::<TransactionNative>(slice) {
  348. t
  349. } else {
  350. return ptr::null_mut();
  351. };
  352. let tx = Box::new(Transaction::from_native(tx));
  353. Box::into_raw(tx)
  354. }
  355. /// Sign a transaction with a subset of the required `Keypair`s. If the `recent_blockhash` supplied
  356. /// does not match that of the `Transaction`, the supplied one will be used, and any existing
  357. /// signatures will be discarded. Returns `0` for success, other for failure.
  358. ///
  359. /// # Undefined Behavior
  360. ///
  361. /// Causes UB if any of the pointers is `NULL`, or if `keypairs` does not point to a valid array of
  362. /// `Keypairs` of length `num_keypairs`
  363. #[no_mangle]
  364. pub unsafe extern "C" fn transaction_partial_sign(
  365. tx: *mut Transaction,
  366. keypairs: *const Keypair,
  367. num_keypairs: size_t,
  368. recent_blockhash: *const Hash,
  369. ) -> c_int {
  370. let mut tx = Box::from_raw(tx);
  371. let mut tx_native = tx.clone().into_native();
  372. let keypairs = slice::from_raw_parts(keypairs, num_keypairs);
  373. let keypairs: Vec<Result<_, _>> = keypairs.iter().map(|k| k.new_native()).collect();
  374. let keypairs: Vec<KeypairNative> = if keypairs.iter().all(|k| k.is_ok()) {
  375. keypairs
  376. .into_iter()
  377. .map(|k| k.expect("This shouldn't ever happen"))
  378. .collect()
  379. } else {
  380. return 1;
  381. };
  382. let keypairs_ref: Vec<&KeypairNative> = keypairs.iter().collect();
  383. let positions = if let Ok(v) = tx_native.get_signing_keypair_positions(&keypairs_ref[..]) {
  384. v
  385. } else {
  386. return 2;
  387. };
  388. let positions: Vec<usize> = if positions.iter().all(|pos| pos.is_some()) {
  389. positions
  390. .iter()
  391. .map(|pos| pos.expect("This shouldn't ever happen"))
  392. .collect()
  393. } else {
  394. return 3;
  395. };
  396. tx_native.partial_sign_unchecked(&keypairs_ref[..], positions, *recent_blockhash);
  397. *tx = Transaction::from_native(tx_native);
  398. Box::into_raw(tx);
  399. 0
  400. }
  401. /// Get the printable c-string of a Pubkey. The returned c-string must be freed with `free_c_string()`
  402. /// Returns `NULL` if the conversion fails.
  403. ///
  404. /// # Undefined Behavior
  405. ///
  406. /// Causes UB if `pubkey` is `NULL`, or if the returned c-string is freed by any method other than
  407. /// calling `free_c_string()`
  408. #[no_mangle]
  409. pub unsafe extern "C" fn get_pubkey_string(pubkey: *const Pubkey) -> *mut c_char {
  410. if let Ok(s) = CString::new(format!("{}", *pubkey)) {
  411. s.into_raw()
  412. } else {
  413. ptr::null_mut()
  414. }
  415. }
  416. #[cfg(test)]
  417. mod tests {
  418. use crate::*;
  419. use bincode::serialize;
  420. use rand_chacha::ChaChaRng;
  421. use rand_core::SeedableRng;
  422. use solana_sdk::signature::{Keypair as KeypairNative, KeypairUtil};
  423. use solana_sdk::system_transaction;
  424. #[test]
  425. fn test_generate_keypair() {
  426. let seed = [1u8; 32];
  427. let mut rng = ChaChaRng::from_seed(seed);
  428. let keypair = KeypairNative::generate(&mut rng);
  429. let c_keypair = unsafe { Box::from_raw(generate_keypair(seed.as_ptr())) };
  430. assert_eq!(c_keypair.new_native(), Ok(keypair));
  431. }
  432. #[test]
  433. fn test_get_pubkey() {
  434. let seed = [1u8; 32];
  435. unsafe {
  436. let keypair = generate_keypair(seed.as_ptr());
  437. let pubkey = Box::from_raw(get_keypair_pubkey(keypair));
  438. let keypair = Box::from_raw(keypair);
  439. let keypair = keypair.new_native().unwrap();
  440. assert_eq!(keypair.pubkey(), *pubkey);
  441. };
  442. }
  443. #[test]
  444. fn test_serialize_transaction() {
  445. let key = KeypairNative::new();
  446. let to = Pubkey::new_rand();
  447. let blockhash = Hash::default();
  448. let tx = system_transaction::transfer_now(&key, &to, 50, blockhash);
  449. let serialized = serialize(&tx).unwrap();
  450. let tx = Box::new(Transaction::from_native(tx));
  451. let tx = Box::into_raw(tx);
  452. let mut res: *const u8 = ptr::null_mut();
  453. let mut len: size_t = 0;
  454. let slice;
  455. unsafe {
  456. assert_eq!(0, serialize_transaction(tx, &mut res, &mut len));
  457. slice = slice::from_raw_parts(res, len);
  458. }
  459. assert_eq!(serialized, slice);
  460. }
  461. #[test]
  462. fn test_deserialize_transaction() {
  463. let key = KeypairNative::new();
  464. let to = Pubkey::new_rand();
  465. let blockhash = Hash::default();
  466. let tx = system_transaction::transfer_now(&key, &to, 50, blockhash);
  467. let serialized = serialize(&tx).unwrap();
  468. let deserialized;
  469. unsafe {
  470. let ret = deserialize_transaction(serialized.as_ptr(), serialized.len());
  471. assert_ne!(ret, ptr::null_mut());
  472. let ret = Box::from_raw(ret);
  473. deserialized = ret.into_native();
  474. }
  475. assert_eq!(deserialized, tx);
  476. }
  477. #[test]
  478. fn test_deserialize_transaction_bad() {
  479. let serialized_bad = vec![0u8; 3];
  480. let deserialized;
  481. unsafe {
  482. deserialized = deserialize_transaction(serialized_bad.as_ptr(), serialized_bad.len());
  483. }
  484. assert_eq!(deserialized, ptr::null_mut());
  485. }
  486. #[test]
  487. fn test_get_pubkey_string() {
  488. let pubkey = Pubkey::new_rand();
  489. let str_native = format!("{}", pubkey);
  490. let str_c;
  491. unsafe {
  492. str_c = CString::from_raw(get_pubkey_string(&pubkey));
  493. }
  494. let str_c = str_c.into_string().unwrap();
  495. assert_eq!(str_native, str_c);
  496. }
  497. #[test]
  498. fn test_transaction_partial_sign() {
  499. let key_native = KeypairNative::new();
  500. let to = Pubkey::new_rand();
  501. let blockhash = Hash::default();
  502. let mut tx_native = system_transaction::transfer_now(&key_native, &to, 50, blockhash);
  503. let tx = Box::into_raw(Box::new(Transaction::from_native(tx_native.clone())));
  504. let key = Keypair::from_native(&key_native);
  505. let tx2;
  506. unsafe {
  507. assert_eq!(0, transaction_partial_sign(tx, &key, 1, &blockhash));
  508. tx2 = Box::from_raw(tx).into_native();
  509. }
  510. tx_native.partial_sign(&[&key_native], blockhash);
  511. assert_eq!(tx_native, tx2);
  512. }
  513. }