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