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