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