Skip to main content

cbe_sdk/transaction/
mod.rs

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