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