solana_sdk/transaction/mod.rs
1//! Atomically-committed sequences of instructions.
2//!
3//! While [`Instruction`]s are the basic unit of computation in Safecoin, they are
4//! submitted by clients in [`Transaction`]s containing one or more
5//! instructions, and signed by one or more [`Signer`]s. Safecoin executes the
6//! instructions in a transaction in order, and only commits any changes if all
7//! instructions terminate without producing an error or exception.
8//!
9//! Transactions do not directly contain their instructions but instead include
10//! a [`Message`], a precompiled representation of a sequence of instructions.
11//! `Message`'s constructors handle the complex task of reordering the
12//! individual lists of accounts required by each instruction into a single flat
13//! list of deduplicated accounts required by the Safecoin runtime. The
14//! `Transaction` type has constructors that build the `Message` so that clients
15//! don't need to interact with them directly.
16//!
17//! Prior to submission to the network, transactions must be signed by one or or
18//! more keypairs, and this signing is typically performed by an abstract
19//! [`Signer`], which may be a [`Keypair`] but may also be other types of
20//! signers including remote wallets, such as Ledger devices, as represented by
21//! the [`RemoteKeypair`] type in the [`safecoin-remote-wallet`] crate.
22//!
23//! [`Signer`]: crate::signer::Signer
24//! [`Keypair`]: crate::signer::keypair::Keypair
25//! [`safecoin-remote-wallet`]: https://docs.rs/safecoin-remote-wallet/latest/
26//! [`RemoteKeypair`]: https://docs.rs/safecoin-remote-wallet/latest/safecoin_remote_wallet/remote_keypair/struct.RemoteKeypair.html
27//!
28//! Every transaction must be signed by a fee-paying account, the account from
29//! which the cost of executing the transaction is withdrawn. Other required
30//! signatures are determined by the requirements of the programs being executed
31//! by each instruction, and are conventionally specified by that program's
32//! documentation.
33//!
34//! When signing a transaction, a recent blockhash must be provided (which can
35//! be retrieved with [`RpcClient::get_latest_blockhash`]). This allows
36//! validators to drop old but unexecuted transactions; and to distinguish
37//! between accidentally duplicated transactions and intentionally duplicated
38//! transactions — any identical transactions will not be executed more
39//! than once, so updating the blockhash between submitting otherwise identical
40//! transactions makes them unique. If a client must sign a transaction long
41//! before submitting it to the network, then it can use the _[durable
42//! transaction nonce]_ mechanism instead of a recent blockhash to ensure unique
43//! transactions.
44//!
45//! [`RpcClient::get_latest_blockhash`]: https://docs.rs/safecoin-client/latest/safecoin_client/rpc_client/struct.RpcClient.html#method.get_latest_blockhash
46//! [durable transaction nonce]: https://docs.solana.com/implemented-proposals/durable-tx-nonces
47//!
48//! # Examples
49//!
50//! This example uses the [`safecoin_client`] and [`anyhow`] crates.
51//!
52//! [`safecoin_client`]: https://docs.rs/safecoin-client
53//! [`anyhow`]: https://docs.rs/anyhow
54//!
55//! ```
56//! # use solana_sdk::example_mocks::safecoin_client;
57//! use anyhow::Result;
58//! use borsh::{BorshSerialize, BorshDeserialize};
59//! use safecoin_client::rpc_client::RpcClient;
60//! use solana_sdk::{
61//! instruction::Instruction,
62//! message::Message,
63//! pubkey::Pubkey,
64//! signature::{Keypair, Signer},
65//! transaction::Transaction,
66//! };
67//!
68//! // A custom program instruction. This would typically be defined in
69//! // another crate so it can be shared between the on-chain program and
70//! // the client.
71//! #[derive(BorshSerialize, BorshDeserialize)]
72//! enum BankInstruction {
73//! Initialize,
74//! Deposit { lamports: u64 },
75//! Withdraw { lamports: u64 },
76//! }
77//!
78//! fn send_initialize_tx(
79//! client: &RpcClient,
80//! program_id: Pubkey,
81//! payer: &Keypair
82//! ) -> Result<()> {
83//!
84//! let bank_instruction = BankInstruction::Initialize;
85//!
86//! let instruction = Instruction::new_with_borsh(
87//! program_id,
88//! &bank_instruction,
89//! vec![],
90//! );
91//!
92//! let blockhash = client.get_latest_blockhash()?;
93//! let mut tx = Transaction::new_signed_with_payer(
94//! &[instruction],
95//! Some(&payer.pubkey()),
96//! &[payer],
97//! blockhash,
98//! );
99//! client.send_and_confirm_transaction(&tx)?;
100//!
101//! Ok(())
102//! }
103//! #
104//! # let client = RpcClient::new(String::new());
105//! # let program_id = Pubkey::new_unique();
106//! # let payer = Keypair::new();
107//! # send_initialize_tx(&client, program_id, &payer)?;
108//! #
109//! # Ok::<(), anyhow::Error>(())
110//! ```
111
112#![cfg(feature = "full")]
113
114use {
115 crate::{
116 hash::Hash,
117 instruction::{CompiledInstruction, Instruction},
118 message::Message,
119 nonce::NONCED_TX_MARKER_IX_INDEX,
120 precompiles::verify_if_precompile,
121 program_utils::limited_deserialize,
122 pubkey::Pubkey,
123 sanitize::{Sanitize, SanitizeError},
124 short_vec,
125 signature::{Signature, SignerError},
126 signers::Signers,
127 wasm_bindgen,
128 },
129 serde::Serialize,
130 solana_program::{system_instruction::SystemInstruction, system_program},
131 solana_sdk::feature_set,
132 std::{result, sync::Arc},
133};
134
135mod error;
136mod sanitized;
137mod versioned;
138
139pub use {error::*, sanitized::*, versioned::*};
140
141#[derive(PartialEq, Eq, Clone, Copy, Debug)]
142pub enum TransactionVerificationMode {
143 HashOnly,
144 HashAndVerifyPrecompiles,
145 FullVerification,
146}
147
148pub type Result<T> = result::Result<T, TransactionError>;
149
150/// An atomically-commited sequence of instructions.
151///
152/// While [`Instruction`]s are the basic unit of computation in Safecoin,
153/// they are submitted by clients in [`Transaction`]s containing one or
154/// more instructions, and signed by one or more [`Signer`]s.
155///
156/// [`Signer`]: crate::signer::Signer
157///
158/// See the [module documentation] for more details about transactions.
159///
160/// [module documentation]: self
161///
162/// Some constructors accept an optional `payer`, the account responsible for
163/// paying the cost of executing a transaction. In most cases, callers should
164/// specify the payer explicitly in these constructors. In some cases though,
165/// the caller is not _required_ to specify the payer, but is still allowed to:
166/// in the [`Message`] structure, the first account is always the fee-payer, so
167/// if the caller has knowledge that the first account of the constructed
168/// transaction's `Message` is both a signer and the expected fee-payer, then
169/// redundantly specifying the fee-payer is not strictly required.
170#[wasm_bindgen]
171#[frozen_abi(digest = "FZtncnS1Xk8ghHfKiXE5oGiUbw2wJhmfXQuNgQR3K6Mc")]
172#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize, AbiExample)]
173pub struct Transaction {
174 /// A set of signatures of a serialized [`Message`], signed by the first
175 /// keys of the `Message`'s [`account_keys`], where the number of signatures
176 /// is equal to [`num_required_signatures`] of the `Message`'s
177 /// [`MessageHeader`].
178 ///
179 /// [`account_keys`]: Message::account_keys
180 /// [`MessageHeader`]: crate::message::MessageHeader
181 /// [`num_required_signatures`]: crate::message::MessageHeader::num_required_signatures
182 // NOTE: Serialization-related changes must be paired with the direct read at sigverify.
183 #[wasm_bindgen(skip)]
184 #[serde(with = "short_vec")]
185 pub signatures: Vec<Signature>,
186
187 /// The message to sign.
188 #[wasm_bindgen(skip)]
189 pub message: Message,
190}
191
192impl Sanitize for Transaction {
193 fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
194 if self.message.header.num_required_signatures as usize > self.signatures.len() {
195 return Err(SanitizeError::IndexOutOfBounds);
196 }
197 if self.signatures.len() > self.message.account_keys.len() {
198 return Err(SanitizeError::IndexOutOfBounds);
199 }
200 self.message.sanitize()
201 }
202}
203
204impl Transaction {
205 /// Create an unsigned transaction from a [`Message`].
206 ///
207 /// # Examples
208 ///
209 /// This example uses the [`safecoin_client`] and [`anyhow`] crates.
210 ///
211 /// [`safecoin_client`]: https://docs.rs/safecoin-client
212 /// [`anyhow`]: https://docs.rs/anyhow
213 ///
214 /// ```
215 /// # use solana_sdk::example_mocks::safecoin_client;
216 /// use anyhow::Result;
217 /// use borsh::{BorshSerialize, BorshDeserialize};
218 /// use safecoin_client::rpc_client::RpcClient;
219 /// use solana_sdk::{
220 /// instruction::Instruction,
221 /// message::Message,
222 /// pubkey::Pubkey,
223 /// signature::{Keypair, Signer},
224 /// transaction::Transaction,
225 /// };
226 ///
227 /// // A custom program instruction. This would typically be defined in
228 /// // another crate so it can be shared between the on-chain program and
229 /// // the client.
230 /// #[derive(BorshSerialize, BorshDeserialize)]
231 /// enum BankInstruction {
232 /// Initialize,
233 /// Deposit { lamports: u64 },
234 /// Withdraw { lamports: u64 },
235 /// }
236 ///
237 /// fn send_initialize_tx(
238 /// client: &RpcClient,
239 /// program_id: Pubkey,
240 /// payer: &Keypair
241 /// ) -> Result<()> {
242 ///
243 /// let bank_instruction = BankInstruction::Initialize;
244 ///
245 /// let instruction = Instruction::new_with_borsh(
246 /// program_id,
247 /// &bank_instruction,
248 /// vec![],
249 /// );
250 ///
251 /// let message = Message::new(
252 /// &[instruction],
253 /// Some(&payer.pubkey()),
254 /// );
255 ///
256 /// let mut tx = Transaction::new_unsigned(message);
257 /// let blockhash = client.get_latest_blockhash()?;
258 /// tx.sign(&[payer], blockhash);
259 /// client.send_and_confirm_transaction(&tx)?;
260 ///
261 /// Ok(())
262 /// }
263 /// #
264 /// # let client = RpcClient::new(String::new());
265 /// # let program_id = Pubkey::new_unique();
266 /// # let payer = Keypair::new();
267 /// # send_initialize_tx(&client, program_id, &payer)?;
268 /// #
269 /// # Ok::<(), anyhow::Error>(())
270 /// ```
271 pub fn new_unsigned(message: Message) -> Self {
272 Self {
273 signatures: vec![Signature::default(); message.header.num_required_signatures as usize],
274 message,
275 }
276 }
277
278 /// Create a fully-signed transaction from a [`Message`].
279 ///
280 /// # Panics
281 ///
282 /// Panics when signing fails. See [`Transaction::try_sign`] and
283 /// [`Transaction::try_partial_sign`] for a full description of failure
284 /// scenarios.
285 ///
286 /// # Examples
287 ///
288 /// This example uses the [`safecoin_client`] and [`anyhow`] crates.
289 ///
290 /// [`safecoin_client`]: https://docs.rs/safecoin-client
291 /// [`anyhow`]: https://docs.rs/anyhow
292 ///
293 /// ```
294 /// # use solana_sdk::example_mocks::safecoin_client;
295 /// use anyhow::Result;
296 /// use borsh::{BorshSerialize, BorshDeserialize};
297 /// use safecoin_client::rpc_client::RpcClient;
298 /// use solana_sdk::{
299 /// instruction::Instruction,
300 /// message::Message,
301 /// pubkey::Pubkey,
302 /// signature::{Keypair, Signer},
303 /// transaction::Transaction,
304 /// };
305 ///
306 /// // A custom program instruction. This would typically be defined in
307 /// // another crate so it can be shared between the on-chain program and
308 /// // the client.
309 /// #[derive(BorshSerialize, BorshDeserialize)]
310 /// enum BankInstruction {
311 /// Initialize,
312 /// Deposit { lamports: u64 },
313 /// Withdraw { lamports: u64 },
314 /// }
315 ///
316 /// fn send_initialize_tx(
317 /// client: &RpcClient,
318 /// program_id: Pubkey,
319 /// payer: &Keypair
320 /// ) -> Result<()> {
321 ///
322 /// let bank_instruction = BankInstruction::Initialize;
323 ///
324 /// let instruction = Instruction::new_with_borsh(
325 /// program_id,
326 /// &bank_instruction,
327 /// vec![],
328 /// );
329 ///
330 /// let message = Message::new(
331 /// &[instruction],
332 /// Some(&payer.pubkey()),
333 /// );
334 ///
335 /// let blockhash = client.get_latest_blockhash()?;
336 /// let mut tx = Transaction::new(&[payer], message, blockhash);
337 /// client.send_and_confirm_transaction(&tx)?;
338 ///
339 /// Ok(())
340 /// }
341 /// #
342 /// # let client = RpcClient::new(String::new());
343 /// # let program_id = Pubkey::new_unique();
344 /// # let payer = Keypair::new();
345 /// # send_initialize_tx(&client, program_id, &payer)?;
346 /// #
347 /// # Ok::<(), anyhow::Error>(())
348 /// ```
349 pub fn new<T: Signers>(
350 from_keypairs: &T,
351 message: Message,
352 recent_blockhash: Hash,
353 ) -> Transaction {
354 let mut tx = Self::new_unsigned(message);
355 tx.sign(from_keypairs, recent_blockhash);
356 tx
357 }
358
359 /// Create an unsigned transaction from a list of [`Instruction`]s.
360 ///
361 /// `payer` is the account responsible for paying the cost of executing the
362 /// transaction. It is typically provided, but is optional in some cases.
363 /// See the [`Transaction`] docs for more.
364 ///
365 /// # Examples
366 ///
367 /// This example uses the [`safecoin_client`] and [`anyhow`] crates.
368 ///
369 /// [`safecoin_client`]: https://docs.rs/safecoin-client
370 /// [`anyhow`]: https://docs.rs/anyhow
371 ///
372 /// ```
373 /// # use solana_sdk::example_mocks::safecoin_client;
374 /// use anyhow::Result;
375 /// use borsh::{BorshSerialize, BorshDeserialize};
376 /// use safecoin_client::rpc_client::RpcClient;
377 /// use solana_sdk::{
378 /// instruction::Instruction,
379 /// message::Message,
380 /// pubkey::Pubkey,
381 /// signature::{Keypair, Signer},
382 /// transaction::Transaction,
383 /// };
384 ///
385 /// // A custom program instruction. This would typically be defined in
386 /// // another crate so it can be shared between the on-chain program and
387 /// // the client.
388 /// #[derive(BorshSerialize, BorshDeserialize)]
389 /// enum BankInstruction {
390 /// Initialize,
391 /// Deposit { lamports: u64 },
392 /// Withdraw { lamports: u64 },
393 /// }
394 ///
395 /// fn send_initialize_tx(
396 /// client: &RpcClient,
397 /// program_id: Pubkey,
398 /// payer: &Keypair
399 /// ) -> Result<()> {
400 ///
401 /// let bank_instruction = BankInstruction::Initialize;
402 ///
403 /// let instruction = Instruction::new_with_borsh(
404 /// program_id,
405 /// &bank_instruction,
406 /// vec![],
407 /// );
408 ///
409 /// let mut tx = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
410 /// let blockhash = client.get_latest_blockhash()?;
411 /// tx.sign(&[payer], blockhash);
412 /// client.send_and_confirm_transaction(&tx)?;
413 ///
414 /// Ok(())
415 /// }
416 /// #
417 /// # let client = RpcClient::new(String::new());
418 /// # let program_id = Pubkey::new_unique();
419 /// # let payer = Keypair::new();
420 /// # send_initialize_tx(&client, program_id, &payer)?;
421 /// #
422 /// # Ok::<(), anyhow::Error>(())
423 /// ```
424 pub fn new_with_payer(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
425 let message = Message::new(instructions, payer);
426 Self::new_unsigned(message)
427 }
428
429 /// Create a fully-signed transaction from a list of [`Instruction`]s.
430 ///
431 /// `payer` is the account responsible for paying the cost of executing the
432 /// transaction. It is typically provided, but is optional in some cases.
433 /// See the [`Transaction`] docs for more.
434 ///
435 /// # Panics
436 ///
437 /// Panics when signing fails. See [`Transaction::try_sign`] and
438 /// [`Transaction::try_partial_sign`] for a full description of failure
439 /// scenarios.
440 ///
441 /// # Examples
442 ///
443 /// This example uses the [`safecoin_client`] and [`anyhow`] crates.
444 ///
445 /// [`safecoin_client`]: https://docs.rs/safecoin-client
446 /// [`anyhow`]: https://docs.rs/anyhow
447 ///
448 /// ```
449 /// # use solana_sdk::example_mocks::safecoin_client;
450 /// use anyhow::Result;
451 /// use borsh::{BorshSerialize, BorshDeserialize};
452 /// use safecoin_client::rpc_client::RpcClient;
453 /// use solana_sdk::{
454 /// instruction::Instruction,
455 /// message::Message,
456 /// pubkey::Pubkey,
457 /// signature::{Keypair, Signer},
458 /// transaction::Transaction,
459 /// };
460 ///
461 /// // A custom program instruction. This would typically be defined in
462 /// // another crate so it can be shared between the on-chain program and
463 /// // the client.
464 /// #[derive(BorshSerialize, BorshDeserialize)]
465 /// enum BankInstruction {
466 /// Initialize,
467 /// Deposit { lamports: u64 },
468 /// Withdraw { lamports: u64 },
469 /// }
470 ///
471 /// fn send_initialize_tx(
472 /// client: &RpcClient,
473 /// program_id: Pubkey,
474 /// payer: &Keypair
475 /// ) -> Result<()> {
476 ///
477 /// let bank_instruction = BankInstruction::Initialize;
478 ///
479 /// let instruction = Instruction::new_with_borsh(
480 /// program_id,
481 /// &bank_instruction,
482 /// vec![],
483 /// );
484 ///
485 /// let blockhash = client.get_latest_blockhash()?;
486 /// let mut tx = Transaction::new_signed_with_payer(
487 /// &[instruction],
488 /// Some(&payer.pubkey()),
489 /// &[payer],
490 /// blockhash,
491 /// );
492 /// client.send_and_confirm_transaction(&tx)?;
493 ///
494 /// Ok(())
495 /// }
496 /// #
497 /// # let client = RpcClient::new(String::new());
498 /// # let program_id = Pubkey::new_unique();
499 /// # let payer = Keypair::new();
500 /// # send_initialize_tx(&client, program_id, &payer)?;
501 /// #
502 /// # Ok::<(), anyhow::Error>(())
503 /// ```
504 pub fn new_signed_with_payer<T: Signers>(
505 instructions: &[Instruction],
506 payer: Option<&Pubkey>,
507 signing_keypairs: &T,
508 recent_blockhash: Hash,
509 ) -> Self {
510 let message = Message::new(instructions, payer);
511 Self::new(signing_keypairs, message, recent_blockhash)
512 }
513
514 /// Create a fully-signed transaction from pre-compiled instructions.
515 ///
516 /// # Arguments
517 ///
518 /// * `from_keypairs` - The keys used to sign the transaction.
519 /// * `keys` - The keys for the transaction. These are the program state
520 /// instances or lamport recipient keys.
521 /// * `recent_blockhash` - The PoH hash.
522 /// * `program_ids` - The keys that identify programs used in the `instruction` vector.
523 /// * `instructions` - Instructions that will be executed atomically.
524 ///
525 /// # Panics
526 ///
527 /// Panics when signing fails. See [`Transaction::try_sign`] and for a full
528 /// description of failure conditions.
529 pub fn new_with_compiled_instructions<T: Signers>(
530 from_keypairs: &T,
531 keys: &[Pubkey],
532 recent_blockhash: Hash,
533 program_ids: Vec<Pubkey>,
534 instructions: Vec<CompiledInstruction>,
535 ) -> Self {
536 let mut account_keys = from_keypairs.pubkeys();
537 let from_keypairs_len = account_keys.len();
538 account_keys.extend_from_slice(keys);
539 account_keys.extend(&program_ids);
540 let message = Message::new_with_compiled_instructions(
541 from_keypairs_len as u8,
542 0,
543 program_ids.len() as u8,
544 account_keys,
545 Hash::default(),
546 instructions,
547 );
548 Transaction::new(from_keypairs, message, recent_blockhash)
549 }
550
551 /// Get the data for an instruction at the given index.
552 ///
553 /// The `instruction_index` corresponds to the [`instructions`] vector of
554 /// the `Transaction`'s [`Message`] value.
555 ///
556 /// [`instructions`]: Message::instructions
557 ///
558 /// # Panics
559 ///
560 /// Panics if `instruction_index` is greater than or equal to the number of
561 /// instructions in the transaction.
562 pub fn data(&self, instruction_index: usize) -> &[u8] {
563 &self.message.instructions[instruction_index].data
564 }
565
566 fn key_index(&self, instruction_index: usize, accounts_index: usize) -> Option<usize> {
567 self.message
568 .instructions
569 .get(instruction_index)
570 .and_then(|instruction| instruction.accounts.get(accounts_index))
571 .map(|&account_keys_index| account_keys_index as usize)
572 }
573
574 /// Get the `Pubkey` of an account required by one of the instructions in
575 /// the transaction.
576 ///
577 /// The `instruction_index` corresponds to the [`instructions`] vector of
578 /// the `Transaction`'s [`Message`] value; and the `account_index` to the
579 /// [`accounts`] vector of the message's [`CompiledInstruction`]s.
580 ///
581 /// [`instructions`]: Message::instructions
582 /// [`accounts`]: CompiledInstruction::accounts
583 /// [`CompiledInstruction`]: CompiledInstruction
584 ///
585 /// Returns `None` if `instruction_index` is greater than or equal to the
586 /// number of instructions in the transaction; or if `accounts_index` is
587 /// greater than or equal to the number of accounts in the instruction.
588 pub fn key(&self, instruction_index: usize, accounts_index: usize) -> Option<&Pubkey> {
589 self.key_index(instruction_index, accounts_index)
590 .and_then(|account_keys_index| self.message.account_keys.get(account_keys_index))
591 }
592
593 /// Get the `Pubkey` of a signing account required by one of the
594 /// instructions in the transaction.
595 ///
596 /// The transaction does not need to be signed for this function to return a
597 /// signing account's pubkey.
598 ///
599 /// Returns `None` if the indexed account is not required to sign the
600 /// transaction. Returns `None` if the [`signatures`] field does not contain
601 /// enough elements to hold a signature for the indexed account (this should
602 /// only be possible if `Transaction` has been manually constructed).
603 ///
604 /// [`signatures`]: Transaction::signatures
605 ///
606 /// Returns `None` if `instruction_index` is greater than or equal to the
607 /// number of instructions in the transaction; or if `accounts_index` is
608 /// greater than or equal to the number of accounts in the instruction.
609 pub fn signer_key(&self, instruction_index: usize, accounts_index: usize) -> Option<&Pubkey> {
610 match self.key_index(instruction_index, accounts_index) {
611 None => None,
612 Some(signature_index) => {
613 if signature_index >= self.signatures.len() {
614 return None;
615 }
616 self.message.account_keys.get(signature_index)
617 }
618 }
619 }
620
621 /// Return the message containing all data that should be signed.
622 pub fn message(&self) -> &Message {
623 &self.message
624 }
625
626 /// Return the serialized message data to sign.
627 pub fn message_data(&self) -> Vec<u8> {
628 self.message().serialize()
629 }
630
631 /// Sign the transaction.
632 ///
633 /// This method fully signs a transaction with all required signers, which
634 /// must be present in the `keypairs` slice. To sign with only some of the
635 /// required signers, use [`Transaction::partial_sign`].
636 ///
637 /// If `recent_blockhash` is different than recorded in the transaction message's
638 /// [`recent_blockhash`] field, then the message's `recent_blockhash` will be updated
639 /// to the provided `recent_blockhash`, and any prior signatures will be cleared.
640 ///
641 /// [`recent_blockhash`]: Message::recent_blockhash
642 ///
643 /// # Panics
644 ///
645 /// Panics when signing fails. Use [`Transaction::try_sign`] to handle the
646 /// error. See the documentation for [`Transaction::try_sign`] for a full description of
647 /// failure conditions.
648 ///
649 /// # Examples
650 ///
651 /// This example uses the [`safecoin_client`] and [`anyhow`] crates.
652 ///
653 /// [`safecoin_client`]: https://docs.rs/safecoin-client
654 /// [`anyhow`]: https://docs.rs/anyhow
655 ///
656 /// ```
657 /// # use solana_sdk::example_mocks::safecoin_client;
658 /// use anyhow::Result;
659 /// use borsh::{BorshSerialize, BorshDeserialize};
660 /// use safecoin_client::rpc_client::RpcClient;
661 /// use solana_sdk::{
662 /// instruction::Instruction,
663 /// message::Message,
664 /// pubkey::Pubkey,
665 /// signature::{Keypair, Signer},
666 /// transaction::Transaction,
667 /// };
668 ///
669 /// // A custom program instruction. This would typically be defined in
670 /// // another crate so it can be shared between the on-chain program and
671 /// // the client.
672 /// #[derive(BorshSerialize, BorshDeserialize)]
673 /// enum BankInstruction {
674 /// Initialize,
675 /// Deposit { lamports: u64 },
676 /// Withdraw { lamports: u64 },
677 /// }
678 ///
679 /// fn send_initialize_tx(
680 /// client: &RpcClient,
681 /// program_id: Pubkey,
682 /// payer: &Keypair
683 /// ) -> Result<()> {
684 ///
685 /// let bank_instruction = BankInstruction::Initialize;
686 ///
687 /// let instruction = Instruction::new_with_borsh(
688 /// program_id,
689 /// &bank_instruction,
690 /// vec![],
691 /// );
692 ///
693 /// let mut tx = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
694 /// let blockhash = client.get_latest_blockhash()?;
695 /// tx.sign(&[payer], blockhash);
696 /// client.send_and_confirm_transaction(&tx)?;
697 ///
698 /// Ok(())
699 /// }
700 /// #
701 /// # let client = RpcClient::new(String::new());
702 /// # let program_id = Pubkey::new_unique();
703 /// # let payer = Keypair::new();
704 /// # send_initialize_tx(&client, program_id, &payer)?;
705 /// #
706 /// # Ok::<(), anyhow::Error>(())
707 /// ```
708 pub fn sign<T: Signers>(&mut self, keypairs: &T, recent_blockhash: Hash) {
709 if let Err(e) = self.try_sign(keypairs, recent_blockhash) {
710 panic!("Transaction::sign failed with error {:?}", e);
711 }
712 }
713
714 /// Sign the transaction with a subset of required keys.
715 ///
716 /// Unlike [`Transaction::sign`], this method does not require all keypairs
717 /// to be provided, allowing a transaction to be signed in multiple steps.
718 ///
719 /// It is permitted to sign a transaction with the same keypair multiple
720 /// times.
721 ///
722 /// If `recent_blockhash` is different than recorded in the transaction message's
723 /// [`recent_blockhash`] field, then the message's `recent_blockhash` will be updated
724 /// to the provided `recent_blockhash`, and any prior signatures will be cleared.
725 ///
726 /// [`recent_blockhash`]: Message::recent_blockhash
727 ///
728 /// # Panics
729 ///
730 /// Panics when signing fails. Use [`Transaction::try_partial_sign`] to
731 /// handle the error. See the documentation for
732 /// [`Transaction::try_partial_sign`] for a full description of failure
733 /// conditions.
734 pub fn partial_sign<T: Signers>(&mut self, keypairs: &T, recent_blockhash: Hash) {
735 if let Err(e) = self.try_partial_sign(keypairs, recent_blockhash) {
736 panic!("Transaction::partial_sign failed with error {:?}", e);
737 }
738 }
739
740 /// Sign the transaction with a subset of required keys.
741 ///
742 /// This places each of the signatures created from `keypairs` in the
743 /// corresponding position, as specified in the `positions` vector, in the
744 /// transactions [`signatures`] field. It does not verify that the signature
745 /// positions are correct.
746 ///
747 /// [`signatures`]: Transaction::signatures
748 ///
749 /// # Panics
750 ///
751 /// Panics if signing fails. Use [`Transaction::try_partial_sign_unchecked`]
752 /// to handle the error.
753 pub fn partial_sign_unchecked<T: Signers>(
754 &mut self,
755 keypairs: &T,
756 positions: Vec<usize>,
757 recent_blockhash: Hash,
758 ) {
759 if let Err(e) = self.try_partial_sign_unchecked(keypairs, positions, recent_blockhash) {
760 panic!(
761 "Transaction::partial_sign_unchecked failed with error {:?}",
762 e
763 );
764 }
765 }
766
767 /// Sign the transaction, returning any errors.
768 ///
769 /// This method fully signs a transaction with all required signers, which
770 /// must be present in the `keypairs` slice. To sign with only some of the
771 /// required signers, use [`Transaction::try_partial_sign`].
772 ///
773 /// If `recent_blockhash` is different than recorded in the transaction message's
774 /// [`recent_blockhash`] field, then the message's `recent_blockhash` will be updated
775 /// to the provided `recent_blockhash`, and any prior signatures will be cleared.
776 ///
777 /// [`recent_blockhash`]: Message::recent_blockhash
778 ///
779 /// # Errors
780 ///
781 /// Signing will fail if some required signers are not provided in
782 /// `keypairs`; or, if the transaction has previously been partially signed,
783 /// some of the remaining required signers are not provided in `keypairs`.
784 /// In other words, the transaction must be fully signed as a result of
785 /// calling this function. The error is [`SignerError::NotEnoughSigners`].
786 ///
787 /// Signing will fail for any of the reasons described in the documentation
788 /// for [`Transaction::try_partial_sign`].
789 ///
790 /// # Examples
791 ///
792 /// This example uses the [`safecoin_client`] and [`anyhow`] crates.
793 ///
794 /// [`safecoin_client`]: https://docs.rs/safecoin-client
795 /// [`anyhow`]: https://docs.rs/anyhow
796 ///
797 /// ```
798 /// # use solana_sdk::example_mocks::safecoin_client;
799 /// use anyhow::Result;
800 /// use borsh::{BorshSerialize, BorshDeserialize};
801 /// use safecoin_client::rpc_client::RpcClient;
802 /// use solana_sdk::{
803 /// instruction::Instruction,
804 /// message::Message,
805 /// pubkey::Pubkey,
806 /// signature::{Keypair, Signer},
807 /// transaction::Transaction,
808 /// };
809 ///
810 /// // A custom program instruction. This would typically be defined in
811 /// // another crate so it can be shared between the on-chain program and
812 /// // the client.
813 /// #[derive(BorshSerialize, BorshDeserialize)]
814 /// enum BankInstruction {
815 /// Initialize,
816 /// Deposit { lamports: u64 },
817 /// Withdraw { lamports: u64 },
818 /// }
819 ///
820 /// fn send_initialize_tx(
821 /// client: &RpcClient,
822 /// program_id: Pubkey,
823 /// payer: &Keypair
824 /// ) -> Result<()> {
825 ///
826 /// let bank_instruction = BankInstruction::Initialize;
827 ///
828 /// let instruction = Instruction::new_with_borsh(
829 /// program_id,
830 /// &bank_instruction,
831 /// vec![],
832 /// );
833 ///
834 /// let mut tx = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
835 /// let blockhash = client.get_latest_blockhash()?;
836 /// tx.try_sign(&[payer], blockhash)?;
837 /// client.send_and_confirm_transaction(&tx)?;
838 ///
839 /// Ok(())
840 /// }
841 /// #
842 /// # let client = RpcClient::new(String::new());
843 /// # let program_id = Pubkey::new_unique();
844 /// # let payer = Keypair::new();
845 /// # send_initialize_tx(&client, program_id, &payer)?;
846 /// #
847 /// # Ok::<(), anyhow::Error>(())
848 /// ```
849 pub fn try_sign<T: Signers>(
850 &mut self,
851 keypairs: &T,
852 recent_blockhash: Hash,
853 ) -> result::Result<(), SignerError> {
854 self.try_partial_sign(keypairs, recent_blockhash)?;
855
856 if !self.is_signed() {
857 Err(SignerError::NotEnoughSigners)
858 } else {
859 Ok(())
860 }
861 }
862
863 /// Sign the transaction with a subset of required keys, returning any errors.
864 ///
865 /// Unlike [`Transaction::try_sign`], this method does not require all
866 /// keypairs to be provided, allowing a transaction to be signed in multiple
867 /// steps.
868 ///
869 /// It is permitted to sign a transaction with the same keypair multiple
870 /// times.
871 ///
872 /// If `recent_blockhash` is different than recorded in the transaction message's
873 /// [`recent_blockhash`] field, then the message's `recent_blockhash` will be updated
874 /// to the provided `recent_blockhash`, and any prior signatures will be cleared.
875 ///
876 /// [`recent_blockhash`]: Message::recent_blockhash
877 ///
878 /// # Errors
879 ///
880 /// Signing will fail if
881 ///
882 /// - The transaction's [`Message`] is malformed such that the number of
883 /// required signatures recorded in its header
884 /// ([`num_required_signatures`]) is greater than the length of its
885 /// account keys ([`account_keys`]). The error is
886 /// [`SignerError::TransactionError`] where the interior
887 /// [`TransactionError`] is [`TransactionError::InvalidAccountIndex`].
888 /// - Any of the provided signers in `keypairs` is not a required signer of
889 /// the message. The error is [`SignerError::KeypairPubkeyMismatch`].
890 /// - Any of the signers is a [`Presigner`], and its provided signature is
891 /// incorrect. The error is [`SignerError::PresignerError`] where the
892 /// interior [`PresignerError`] is
893 /// [`PresignerError::VerificationFailure`].
894 /// - The signer is a [`RemoteKeypair`] and
895 /// - It does not understand the input provided ([`SignerError::InvalidInput`]).
896 /// - The device cannot be found ([`SignerError::NoDeviceFound`]).
897 /// - The user cancels the signing ([`SignerError::UserCancel`]).
898 /// - An error was encountered connecting ([`SignerError::Connection`]).
899 /// - Some device-specific protocol error occurs ([`SignerError::Protocol`]).
900 /// - Some other error occurs ([`SignerError::Custom`]).
901 ///
902 /// See the documentation for the [`safecoin-remote-wallet`] crate for details
903 /// on the operation of [`RemoteKeypair`] signers.
904 ///
905 /// [`num_required_signatures`]: crate::message::MessageHeader::num_required_signatures
906 /// [`account_keys`]: Message::account_keys
907 /// [`Presigner`]: crate::signer::presigner::Presigner
908 /// [`PresignerError`]: crate::signer::presigner::PresignerError
909 /// [`PresignerError::VerificationFailure`]: crate::signer::presigner::PresignerError::VerificationFailure
910 /// [`safecoin-remote-wallet`]: https://docs.rs/safecoin-remote-wallet/latest/
911 /// [`RemoteKeypair`]: https://docs.rs/safecoin-remote-wallet/latest/safecoin_remote_wallet/remote_keypair/struct.RemoteKeypair.html
912 pub fn try_partial_sign<T: Signers>(
913 &mut self,
914 keypairs: &T,
915 recent_blockhash: Hash,
916 ) -> result::Result<(), SignerError> {
917 let positions = self.get_signing_keypair_positions(&keypairs.pubkeys())?;
918 if positions.iter().any(|pos| pos.is_none()) {
919 return Err(SignerError::KeypairPubkeyMismatch);
920 }
921 let positions: Vec<usize> = positions.iter().map(|pos| pos.unwrap()).collect();
922 self.try_partial_sign_unchecked(keypairs, positions, recent_blockhash)
923 }
924
925 /// Sign the transaction with a subset of required keys, returning any
926 /// errors.
927 ///
928 /// This places each of the signatures created from `keypairs` in the
929 /// corresponding position, as specified in the `positions` vector, in the
930 /// transactions [`signatures`] field. It does not verify that the signature
931 /// positions are correct.
932 ///
933 /// [`signatures`]: Transaction::signatures
934 ///
935 /// # Errors
936 ///
937 /// Returns an error if signing fails.
938 pub fn try_partial_sign_unchecked<T: Signers>(
939 &mut self,
940 keypairs: &T,
941 positions: Vec<usize>,
942 recent_blockhash: Hash,
943 ) -> result::Result<(), SignerError> {
944 // if you change the blockhash, you're re-signing...
945 if recent_blockhash != self.message.recent_blockhash {
946 self.message.recent_blockhash = recent_blockhash;
947 self.signatures
948 .iter_mut()
949 .for_each(|signature| *signature = Signature::default());
950 }
951
952 let signatures = keypairs.try_sign_message(&self.message_data())?;
953 for i in 0..positions.len() {
954 self.signatures[positions[i]] = signatures[i];
955 }
956 Ok(())
957 }
958
959 /// Returns a signature that is not valid for signing this transaction.
960 pub fn get_invalid_signature() -> Signature {
961 Signature::default()
962 }
963
964 /// Verifies that all signers have signed the message.
965 ///
966 /// # Errors
967 ///
968 /// Returns [`TransactionError::SignatureFailure`] on error.
969 pub fn verify(&self) -> Result<()> {
970 let message_bytes = self.message_data();
971 if !self
972 ._verify_with_results(&message_bytes)
973 .iter()
974 .all(|verify_result| *verify_result)
975 {
976 Err(TransactionError::SignatureFailure)
977 } else {
978 Ok(())
979 }
980 }
981
982 /// Verify the transaction and hash its message.
983 ///
984 /// # Errors
985 ///
986 /// Returns [`TransactionError::SignatureFailure`] on error.
987 pub fn verify_and_hash_message(&self) -> Result<Hash> {
988 let message_bytes = self.message_data();
989 if !self
990 ._verify_with_results(&message_bytes)
991 .iter()
992 .all(|verify_result| *verify_result)
993 {
994 Err(TransactionError::SignatureFailure)
995 } else {
996 Ok(Message::hash_raw_message(&message_bytes))
997 }
998 }
999
1000 /// Verifies that all signers have signed the message.
1001 ///
1002 /// Returns a vector with the length of required signatures, where each
1003 /// element is either `true` if that signer has signed, or `false` if not.
1004 pub fn verify_with_results(&self) -> Vec<bool> {
1005 self._verify_with_results(&self.message_data())
1006 }
1007
1008 pub(crate) fn _verify_with_results(&self, message_bytes: &[u8]) -> Vec<bool> {
1009 self.signatures
1010 .iter()
1011 .zip(&self.message.account_keys)
1012 .map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
1013 .collect()
1014 }
1015
1016 /// Verify the precompiled programs in this transaction.
1017 pub fn verify_precompiles(&self, feature_set: &Arc<feature_set::FeatureSet>) -> Result<()> {
1018 for instruction in &self.message().instructions {
1019 // The Transaction may not be sanitized at this point
1020 if instruction.program_id_index as usize >= self.message().account_keys.len() {
1021 return Err(TransactionError::AccountNotFound);
1022 }
1023 let program_id = &self.message().account_keys[instruction.program_id_index as usize];
1024
1025 verify_if_precompile(
1026 program_id,
1027 instruction,
1028 &self.message().instructions,
1029 feature_set,
1030 )
1031 .map_err(|_| TransactionError::InvalidAccountIndex)?;
1032 }
1033 Ok(())
1034 }
1035
1036 /// Get the positions of the pubkeys in `account_keys` associated with signing keypairs.
1037 ///
1038 /// [`account_keys`]: Message::account_keys
1039 pub fn get_signing_keypair_positions(&self, pubkeys: &[Pubkey]) -> Result<Vec<Option<usize>>> {
1040 if self.message.account_keys.len() < self.message.header.num_required_signatures as usize {
1041 return Err(TransactionError::InvalidAccountIndex);
1042 }
1043 let signed_keys =
1044 &self.message.account_keys[0..self.message.header.num_required_signatures as usize];
1045
1046 Ok(pubkeys
1047 .iter()
1048 .map(|pubkey| signed_keys.iter().position(|x| x == pubkey))
1049 .collect())
1050 }
1051
1052 /// Replace all the signatures and pubkeys.
1053 pub fn replace_signatures(&mut self, signers: &[(Pubkey, Signature)]) -> Result<()> {
1054 let num_required_signatures = self.message.header.num_required_signatures as usize;
1055 if signers.len() != num_required_signatures
1056 || self.signatures.len() != num_required_signatures
1057 || self.message.account_keys.len() < num_required_signatures
1058 {
1059 return Err(TransactionError::InvalidAccountIndex);
1060 }
1061
1062 signers
1063 .iter()
1064 .enumerate()
1065 .for_each(|(i, (pubkey, signature))| {
1066 self.signatures[i] = *signature;
1067 self.message.account_keys[i] = *pubkey;
1068 });
1069
1070 self.verify()
1071 }
1072
1073 pub fn is_signed(&self) -> bool {
1074 self.signatures
1075 .iter()
1076 .all(|signature| *signature != Signature::default())
1077 }
1078}
1079
1080pub fn uses_durable_nonce(tx: &Transaction) -> Option<&CompiledInstruction> {
1081 let message = tx.message();
1082 message
1083 .instructions
1084 .get(NONCED_TX_MARKER_IX_INDEX as usize)
1085 .filter(|instruction| {
1086 // Is system program
1087 matches!(
1088 message.account_keys.get(instruction.program_id_index as usize),
1089 Some(program_id) if system_program::check_id(program_id)
1090 )
1091 // Is a nonce advance instruction
1092 && matches!(
1093 limited_deserialize(&instruction.data),
1094 Ok(SystemInstruction::AdvanceNonceAccount)
1095 )
1096 // Nonce account is writable
1097 && matches!(
1098 instruction.accounts.first(),
1099 Some(index) if message.is_writable(*index as usize)
1100 )
1101 })
1102}
1103
1104#[deprecated]
1105pub fn get_nonce_pubkey_from_instruction<'a>(
1106 ix: &CompiledInstruction,
1107 tx: &'a Transaction,
1108) -> Option<&'a Pubkey> {
1109 ix.accounts.first().and_then(|idx| {
1110 let idx = *idx as usize;
1111 tx.message().account_keys.get(idx)
1112 })
1113}
1114
1115#[cfg(test)]
1116mod tests {
1117 #![allow(deprecated)]
1118
1119 use {
1120 super::*,
1121 crate::{
1122 hash::hash,
1123 instruction::AccountMeta,
1124 signature::{Keypair, Presigner, Signer},
1125 system_instruction, sysvar,
1126 },
1127 bincode::{deserialize, serialize, serialized_size},
1128 std::mem::size_of,
1129 };
1130
1131 fn get_program_id(tx: &Transaction, instruction_index: usize) -> &Pubkey {
1132 let message = tx.message();
1133 let instruction = &message.instructions[instruction_index];
1134 instruction.program_id(&message.account_keys)
1135 }
1136
1137 #[test]
1138 fn test_refs() {
1139 let key = Keypair::new();
1140 let key1 = solana_sdk::pubkey::new_rand();
1141 let key2 = solana_sdk::pubkey::new_rand();
1142 let prog1 = solana_sdk::pubkey::new_rand();
1143 let prog2 = solana_sdk::pubkey::new_rand();
1144 let instructions = vec![
1145 CompiledInstruction::new(3, &(), vec![0, 1]),
1146 CompiledInstruction::new(4, &(), vec![0, 2]),
1147 ];
1148 let tx = Transaction::new_with_compiled_instructions(
1149 &[&key],
1150 &[key1, key2],
1151 Hash::default(),
1152 vec![prog1, prog2],
1153 instructions,
1154 );
1155 assert!(tx.sanitize().is_ok());
1156
1157 assert_eq!(tx.key(0, 0), Some(&key.pubkey()));
1158 assert_eq!(tx.signer_key(0, 0), Some(&key.pubkey()));
1159
1160 assert_eq!(tx.key(1, 0), Some(&key.pubkey()));
1161 assert_eq!(tx.signer_key(1, 0), Some(&key.pubkey()));
1162
1163 assert_eq!(tx.key(0, 1), Some(&key1));
1164 assert_eq!(tx.signer_key(0, 1), None);
1165
1166 assert_eq!(tx.key(1, 1), Some(&key2));
1167 assert_eq!(tx.signer_key(1, 1), None);
1168
1169 assert_eq!(tx.key(2, 0), None);
1170 assert_eq!(tx.signer_key(2, 0), None);
1171
1172 assert_eq!(tx.key(0, 2), None);
1173 assert_eq!(tx.signer_key(0, 2), None);
1174
1175 assert_eq!(*get_program_id(&tx, 0), prog1);
1176 assert_eq!(*get_program_id(&tx, 1), prog2);
1177 }
1178
1179 #[test]
1180 fn test_refs_invalid_program_id() {
1181 let key = Keypair::new();
1182 let instructions = vec![CompiledInstruction::new(1, &(), vec![])];
1183 let tx = Transaction::new_with_compiled_instructions(
1184 &[&key],
1185 &[],
1186 Hash::default(),
1187 vec![],
1188 instructions,
1189 );
1190 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1191 }
1192 #[test]
1193 fn test_refs_invalid_account() {
1194 let key = Keypair::new();
1195 let instructions = vec![CompiledInstruction::new(1, &(), vec![2])];
1196 let tx = Transaction::new_with_compiled_instructions(
1197 &[&key],
1198 &[],
1199 Hash::default(),
1200 vec![Pubkey::default()],
1201 instructions,
1202 );
1203 assert_eq!(*get_program_id(&tx, 0), Pubkey::default());
1204 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1205 }
1206
1207 #[test]
1208 fn test_sanitize_txs() {
1209 let key = Keypair::new();
1210 let id0 = Pubkey::default();
1211 let program_id = solana_sdk::pubkey::new_rand();
1212 let ix = Instruction::new_with_bincode(
1213 program_id,
1214 &0,
1215 vec![
1216 AccountMeta::new(key.pubkey(), true),
1217 AccountMeta::new(id0, true),
1218 ],
1219 );
1220 let mut tx = Transaction::new_with_payer(&[ix], Some(&key.pubkey()));
1221 let o = tx.clone();
1222 assert_eq!(tx.sanitize(), Ok(()));
1223 assert_eq!(tx.message.account_keys.len(), 3);
1224
1225 tx = o.clone();
1226 tx.message.header.num_required_signatures = 3;
1227 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1228
1229 tx = o.clone();
1230 tx.message.header.num_readonly_signed_accounts = 4;
1231 tx.message.header.num_readonly_unsigned_accounts = 0;
1232 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1233
1234 tx = o.clone();
1235 tx.message.header.num_readonly_signed_accounts = 2;
1236 tx.message.header.num_readonly_unsigned_accounts = 2;
1237 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1238
1239 tx = o.clone();
1240 tx.message.header.num_readonly_signed_accounts = 0;
1241 tx.message.header.num_readonly_unsigned_accounts = 4;
1242 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1243
1244 tx = o.clone();
1245 tx.message.instructions[0].program_id_index = 3;
1246 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1247
1248 tx = o.clone();
1249 tx.message.instructions[0].accounts[0] = 3;
1250 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1251
1252 tx = o.clone();
1253 tx.message.instructions[0].program_id_index = 0;
1254 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1255
1256 tx = o.clone();
1257 tx.message.header.num_readonly_signed_accounts = 2;
1258 tx.message.header.num_readonly_unsigned_accounts = 3;
1259 tx.message.account_keys.resize(4, Pubkey::default());
1260 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1261
1262 tx = o;
1263 tx.message.header.num_readonly_signed_accounts = 2;
1264 tx.message.header.num_required_signatures = 1;
1265 assert_eq!(tx.sanitize(), Err(SanitizeError::IndexOutOfBounds));
1266 }
1267
1268 fn create_sample_transaction() -> Transaction {
1269 let keypair = Keypair::from_bytes(&[
1270 48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 255, 101, 36, 24, 124, 23,
1271 167, 21, 132, 204, 155, 5, 185, 58, 121, 75, 156, 227, 116, 193, 215, 38, 142, 22, 8,
1272 14, 229, 239, 119, 93, 5, 218, 161, 35, 3, 33, 0, 36, 100, 158, 252, 33, 161, 97, 185,
1273 62, 89, 99,
1274 ])
1275 .unwrap();
1276 let to = Pubkey::from([
1277 1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4,
1278 1, 1, 1,
1279 ]);
1280
1281 let program_id = Pubkey::from([
1282 2, 2, 2, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4,
1283 2, 2, 2,
1284 ]);
1285 let account_metas = vec![
1286 AccountMeta::new(keypair.pubkey(), true),
1287 AccountMeta::new(to, false),
1288 ];
1289 let instruction =
1290 Instruction::new_with_bincode(program_id, &(1u8, 2u8, 3u8), account_metas);
1291 let message = Message::new(&[instruction], Some(&keypair.pubkey()));
1292 Transaction::new(&[&keypair], message, Hash::default())
1293 }
1294
1295 #[test]
1296 fn test_transaction_serialize() {
1297 let tx = create_sample_transaction();
1298 let ser = serialize(&tx).unwrap();
1299 let deser = deserialize(&ser).unwrap();
1300 assert_eq!(tx, deser);
1301 }
1302
1303 /// Detect changes to the serialized size of payment transactions, which affects TPS.
1304 #[test]
1305 fn test_transaction_minimum_serialized_size() {
1306 let alice_keypair = Keypair::new();
1307 let alice_pubkey = alice_keypair.pubkey();
1308 let bob_pubkey = solana_sdk::pubkey::new_rand();
1309 let ix = system_instruction::transfer(&alice_pubkey, &bob_pubkey, 42);
1310
1311 let expected_data_size = size_of::<u32>() + size_of::<u64>();
1312 assert_eq!(expected_data_size, 12);
1313 assert_eq!(
1314 ix.data.len(),
1315 expected_data_size,
1316 "unexpected system instruction size"
1317 );
1318
1319 let expected_instruction_size = 1 + 1 + ix.accounts.len() + 1 + expected_data_size;
1320 assert_eq!(expected_instruction_size, 17);
1321
1322 let message = Message::new(&[ix], Some(&alice_pubkey));
1323 assert_eq!(
1324 serialized_size(&message.instructions[0]).unwrap() as usize,
1325 expected_instruction_size,
1326 "unexpected Instruction::serialized_size"
1327 );
1328
1329 let tx = Transaction::new(&[&alice_keypair], message, Hash::default());
1330
1331 let len_size = 1;
1332 let num_required_sigs_size = 1;
1333 let num_readonly_accounts_size = 2;
1334 let blockhash_size = size_of::<Hash>();
1335 let expected_transaction_size = len_size
1336 + (tx.signatures.len() * size_of::<Signature>())
1337 + num_required_sigs_size
1338 + num_readonly_accounts_size
1339 + len_size
1340 + (tx.message.account_keys.len() * size_of::<Pubkey>())
1341 + blockhash_size
1342 + len_size
1343 + expected_instruction_size;
1344 assert_eq!(expected_transaction_size, 215);
1345
1346 assert_eq!(
1347 serialized_size(&tx).unwrap() as usize,
1348 expected_transaction_size,
1349 "unexpected serialized transaction size"
1350 );
1351 }
1352
1353 /// Detect binary changes in the serialized transaction data, which could have a downstream
1354 /// affect on SDKs and applications
1355 #[test]
1356 fn test_sdk_serialize() {
1357 assert_eq!(
1358 serialize(&create_sample_transaction()).unwrap(),
1359 vec![
1360 1, 71, 59, 9, 187, 190, 129, 150, 165, 21, 33, 158, 72, 87, 110, 144, 120, 79, 238,
1361 132, 134, 105, 39, 102, 116, 209, 29, 229, 154, 36, 105, 44, 172, 118, 131, 22,
1362 124, 131, 179, 142, 176, 27, 117, 160, 89, 102, 224, 204, 1, 252, 141, 2, 136, 0,
1363 37, 218, 225, 129, 92, 154, 250, 59, 97, 178, 10, 1, 0, 1, 3, 156, 227, 116, 193,
1364 215, 38, 142, 22, 8, 14, 229, 239, 119, 93, 5, 218, 161, 35, 3, 33, 0, 36, 100,
1365 158, 252, 33, 161, 97, 185, 62, 89, 99, 1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9,
1366 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1, 2, 2, 2, 4, 5, 6, 7, 8, 9, 1,
1367 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2, 0, 0, 0, 0, 0, 0,
1368 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
1369 2, 0, 1, 3, 1, 2, 3
1370 ]
1371 );
1372 }
1373
1374 #[test]
1375 #[should_panic]
1376 fn test_transaction_missing_key() {
1377 let keypair = Keypair::new();
1378 let message = Message::new(&[], None);
1379 Transaction::new_unsigned(message).sign(&[&keypair], Hash::default());
1380 }
1381
1382 #[test]
1383 #[should_panic]
1384 fn test_partial_sign_mismatched_key() {
1385 let keypair = Keypair::new();
1386 let fee_payer = solana_sdk::pubkey::new_rand();
1387 let ix = Instruction::new_with_bincode(
1388 Pubkey::default(),
1389 &0,
1390 vec![AccountMeta::new(fee_payer, true)],
1391 );
1392 let message = Message::new(&[ix], Some(&fee_payer));
1393 Transaction::new_unsigned(message).partial_sign(&[&keypair], Hash::default());
1394 }
1395
1396 #[test]
1397 fn test_partial_sign() {
1398 let keypair0 = Keypair::new();
1399 let keypair1 = Keypair::new();
1400 let keypair2 = Keypair::new();
1401 let ix = Instruction::new_with_bincode(
1402 Pubkey::default(),
1403 &0,
1404 vec![
1405 AccountMeta::new(keypair0.pubkey(), true),
1406 AccountMeta::new(keypair1.pubkey(), true),
1407 AccountMeta::new(keypair2.pubkey(), true),
1408 ],
1409 );
1410 let message = Message::new(&[ix], Some(&keypair0.pubkey()));
1411 let mut tx = Transaction::new_unsigned(message);
1412
1413 tx.partial_sign(&[&keypair0, &keypair2], Hash::default());
1414 assert!(!tx.is_signed());
1415 tx.partial_sign(&[&keypair1], Hash::default());
1416 assert!(tx.is_signed());
1417
1418 let hash = hash(&[1]);
1419 tx.partial_sign(&[&keypair1], hash);
1420 assert!(!tx.is_signed());
1421 tx.partial_sign(&[&keypair0, &keypair2], hash);
1422 assert!(tx.is_signed());
1423 }
1424
1425 #[test]
1426 #[should_panic]
1427 fn test_transaction_missing_keypair() {
1428 let program_id = Pubkey::default();
1429 let keypair0 = Keypair::new();
1430 let id0 = keypair0.pubkey();
1431 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
1432 let message = Message::new(&[ix], Some(&id0));
1433 Transaction::new_unsigned(message).sign(&Vec::<&Keypair>::new(), Hash::default());
1434 }
1435
1436 #[test]
1437 #[should_panic]
1438 fn test_transaction_wrong_key() {
1439 let program_id = Pubkey::default();
1440 let keypair0 = Keypair::new();
1441 let wrong_id = Pubkey::default();
1442 let ix =
1443 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(wrong_id, true)]);
1444 let message = Message::new(&[ix], Some(&wrong_id));
1445 Transaction::new_unsigned(message).sign(&[&keypair0], Hash::default());
1446 }
1447
1448 #[test]
1449 fn test_transaction_correct_key() {
1450 let program_id = Pubkey::default();
1451 let keypair0 = Keypair::new();
1452 let id0 = keypair0.pubkey();
1453 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
1454 let message = Message::new(&[ix], Some(&id0));
1455 let mut tx = Transaction::new_unsigned(message);
1456 tx.sign(&[&keypair0], Hash::default());
1457 assert_eq!(
1458 tx.message.instructions[0],
1459 CompiledInstruction::new(1, &0, vec![0])
1460 );
1461 assert!(tx.is_signed());
1462 }
1463
1464 #[test]
1465 fn test_transaction_instruction_with_duplicate_keys() {
1466 let program_id = Pubkey::default();
1467 let keypair0 = Keypair::new();
1468 let id0 = keypair0.pubkey();
1469 let id1 = solana_sdk::pubkey::new_rand();
1470 let ix = Instruction::new_with_bincode(
1471 program_id,
1472 &0,
1473 vec![
1474 AccountMeta::new(id0, true),
1475 AccountMeta::new(id1, false),
1476 AccountMeta::new(id0, false),
1477 AccountMeta::new(id1, false),
1478 ],
1479 );
1480 let message = Message::new(&[ix], Some(&id0));
1481 let mut tx = Transaction::new_unsigned(message);
1482 tx.sign(&[&keypair0], Hash::default());
1483 assert_eq!(
1484 tx.message.instructions[0],
1485 CompiledInstruction::new(2, &0, vec![0, 1, 0, 1])
1486 );
1487 assert!(tx.is_signed());
1488 }
1489
1490 #[test]
1491 fn test_try_sign_dyn_keypairs() {
1492 let program_id = Pubkey::default();
1493 let keypair = Keypair::new();
1494 let pubkey = keypair.pubkey();
1495 let presigner_keypair = Keypair::new();
1496 let presigner_pubkey = presigner_keypair.pubkey();
1497
1498 let ix = Instruction::new_with_bincode(
1499 program_id,
1500 &0,
1501 vec![
1502 AccountMeta::new(pubkey, true),
1503 AccountMeta::new(presigner_pubkey, true),
1504 ],
1505 );
1506 let message = Message::new(&[ix], Some(&pubkey));
1507 let mut tx = Transaction::new_unsigned(message);
1508
1509 let presigner_sig = presigner_keypair.sign_message(&tx.message_data());
1510 let presigner = Presigner::new(&presigner_pubkey, &presigner_sig);
1511
1512 let signers: Vec<&dyn Signer> = vec![&keypair, &presigner];
1513
1514 let res = tx.try_sign(&signers, Hash::default());
1515 assert_eq!(res, Ok(()));
1516 assert_eq!(tx.signatures[0], keypair.sign_message(&tx.message_data()));
1517 assert_eq!(tx.signatures[1], presigner_sig);
1518
1519 // Wrong key should error, not panic
1520 let another_pubkey = solana_sdk::pubkey::new_rand();
1521 let ix = Instruction::new_with_bincode(
1522 program_id,
1523 &0,
1524 vec![
1525 AccountMeta::new(another_pubkey, true),
1526 AccountMeta::new(presigner_pubkey, true),
1527 ],
1528 );
1529 let message = Message::new(&[ix], Some(&another_pubkey));
1530 let mut tx = Transaction::new_unsigned(message);
1531
1532 let res = tx.try_sign(&signers, Hash::default());
1533 assert!(res.is_err());
1534 assert_eq!(
1535 tx.signatures,
1536 vec![Signature::default(), Signature::default()]
1537 );
1538 }
1539
1540 fn nonced_transfer_tx() -> (Pubkey, Pubkey, Transaction) {
1541 let from_keypair = Keypair::new();
1542 let from_pubkey = from_keypair.pubkey();
1543 let nonce_keypair = Keypair::new();
1544 let nonce_pubkey = nonce_keypair.pubkey();
1545 let instructions = [
1546 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
1547 system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
1548 ];
1549 let message = Message::new(&instructions, Some(&nonce_pubkey));
1550 let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
1551 (from_pubkey, nonce_pubkey, tx)
1552 }
1553
1554 #[test]
1555 fn tx_uses_nonce_ok() {
1556 let (_, _, tx) = nonced_transfer_tx();
1557 assert!(uses_durable_nonce(&tx).is_some());
1558 }
1559
1560 #[test]
1561 fn tx_uses_nonce_empty_ix_fail() {
1562 assert!(uses_durable_nonce(&Transaction::default()).is_none());
1563 }
1564
1565 #[test]
1566 fn tx_uses_nonce_bad_prog_id_idx_fail() {
1567 let (_, _, mut tx) = nonced_transfer_tx();
1568 tx.message.instructions.get_mut(0).unwrap().program_id_index = 255u8;
1569 assert!(uses_durable_nonce(&tx).is_none());
1570 }
1571
1572 #[test]
1573 fn tx_uses_nonce_first_prog_id_not_nonce_fail() {
1574 let from_keypair = Keypair::new();
1575 let from_pubkey = from_keypair.pubkey();
1576 let nonce_keypair = Keypair::new();
1577 let nonce_pubkey = nonce_keypair.pubkey();
1578 let instructions = [
1579 system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
1580 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
1581 ];
1582 let message = Message::new(&instructions, Some(&from_pubkey));
1583 let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
1584 assert!(uses_durable_nonce(&tx).is_none());
1585 }
1586
1587 #[test]
1588 fn tx_uses_ro_nonce_account() {
1589 let from_keypair = Keypair::new();
1590 let from_pubkey = from_keypair.pubkey();
1591 let nonce_keypair = Keypair::new();
1592 let nonce_pubkey = nonce_keypair.pubkey();
1593 let account_metas = vec![
1594 AccountMeta::new_readonly(nonce_pubkey, false),
1595 #[allow(deprecated)]
1596 AccountMeta::new_readonly(sysvar::recent_blockhashes::id(), false),
1597 AccountMeta::new_readonly(nonce_pubkey, true),
1598 ];
1599 let nonce_instruction = Instruction::new_with_bincode(
1600 system_program::id(),
1601 &system_instruction::SystemInstruction::AdvanceNonceAccount,
1602 account_metas,
1603 );
1604 let tx = Transaction::new_signed_with_payer(
1605 &[nonce_instruction],
1606 Some(&from_pubkey),
1607 &[&from_keypair, &nonce_keypair],
1608 Hash::default(),
1609 );
1610 assert!(uses_durable_nonce(&tx).is_none());
1611 }
1612
1613 #[test]
1614 fn tx_uses_nonce_wrong_first_nonce_ix_fail() {
1615 let from_keypair = Keypair::new();
1616 let from_pubkey = from_keypair.pubkey();
1617 let nonce_keypair = Keypair::new();
1618 let nonce_pubkey = nonce_keypair.pubkey();
1619 let instructions = [
1620 system_instruction::withdraw_nonce_account(
1621 &nonce_pubkey,
1622 &nonce_pubkey,
1623 &from_pubkey,
1624 42,
1625 ),
1626 system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
1627 ];
1628 let message = Message::new(&instructions, Some(&nonce_pubkey));
1629 let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
1630 assert!(uses_durable_nonce(&tx).is_none());
1631 }
1632
1633 #[test]
1634 fn get_nonce_pub_from_ix_ok() {
1635 let (_, nonce_pubkey, tx) = nonced_transfer_tx();
1636 let nonce_ix = uses_durable_nonce(&tx).unwrap();
1637 assert_eq!(
1638 get_nonce_pubkey_from_instruction(nonce_ix, &tx),
1639 Some(&nonce_pubkey),
1640 );
1641 }
1642
1643 #[test]
1644 fn get_nonce_pub_from_ix_no_accounts_fail() {
1645 let (_, _, tx) = nonced_transfer_tx();
1646 let nonce_ix = uses_durable_nonce(&tx).unwrap();
1647 let mut nonce_ix = nonce_ix.clone();
1648 nonce_ix.accounts.clear();
1649 assert_eq!(get_nonce_pubkey_from_instruction(&nonce_ix, &tx), None,);
1650 }
1651
1652 #[test]
1653 fn get_nonce_pub_from_ix_bad_acc_idx_fail() {
1654 let (_, _, tx) = nonced_transfer_tx();
1655 let nonce_ix = uses_durable_nonce(&tx).unwrap();
1656 let mut nonce_ix = nonce_ix.clone();
1657 nonce_ix.accounts[0] = 255u8;
1658 assert_eq!(get_nonce_pubkey_from_instruction(&nonce_ix, &tx), None,);
1659 }
1660
1661 #[test]
1662 fn tx_keypair_pubkey_mismatch() {
1663 let from_keypair = Keypair::new();
1664 let from_pubkey = from_keypair.pubkey();
1665 let to_pubkey = Pubkey::new_unique();
1666 let instructions = [system_instruction::transfer(&from_pubkey, &to_pubkey, 42)];
1667 let mut tx = Transaction::new_with_payer(&instructions, Some(&from_pubkey));
1668 let unused_keypair = Keypair::new();
1669 let err = tx
1670 .try_partial_sign(&[&from_keypair, &unused_keypair], Hash::default())
1671 .unwrap_err();
1672 assert_eq!(err, SignerError::KeypairPubkeyMismatch);
1673 }
1674}