Support sending versioned txs in rpc client (#27933)
* Support sending versioned txs in rpc client * Revert adding instructions_mut function
This commit is contained in:
parent
e15a5c010e
commit
00b0a92ec4
|
@ -18,7 +18,9 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
http_sender::HttpSender,
|
http_sender::HttpSender,
|
||||||
mock_sender::MockSender,
|
mock_sender::MockSender,
|
||||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClientConfig},
|
rpc_client::{
|
||||||
|
GetConfirmedSignaturesForAddress2Config, RpcClientConfig, SerializableTransaction,
|
||||||
|
},
|
||||||
rpc_sender::*,
|
rpc_sender::*,
|
||||||
},
|
},
|
||||||
bincode::serialize,
|
bincode::serialize,
|
||||||
|
@ -48,7 +50,7 @@ use {
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
transaction::{self, uses_durable_nonce, Transaction},
|
transaction::{self},
|
||||||
},
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
|
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
|
||||||
|
@ -658,7 +660,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn send_and_confirm_transaction(
|
pub async fn send_and_confirm_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
const SEND_RETRIES: usize = 1;
|
const SEND_RETRIES: usize = 1;
|
||||||
const GET_STATUS_RETRIES: usize = usize::MAX;
|
const GET_STATUS_RETRIES: usize = usize::MAX;
|
||||||
|
@ -666,13 +668,13 @@ impl RpcClient {
|
||||||
'sending: for _ in 0..SEND_RETRIES {
|
'sending: for _ in 0..SEND_RETRIES {
|
||||||
let signature = self.send_transaction(transaction).await?;
|
let signature = self.send_transaction(transaction).await?;
|
||||||
|
|
||||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
let recent_blockhash = if transaction.uses_durable_nonce() {
|
||||||
let (recent_blockhash, ..) = self
|
let (recent_blockhash, ..) = self
|
||||||
.get_latest_blockhash_with_commitment(CommitmentConfig::processed())
|
.get_latest_blockhash_with_commitment(CommitmentConfig::processed())
|
||||||
.await?;
|
.await?;
|
||||||
recent_blockhash
|
recent_blockhash
|
||||||
} else {
|
} else {
|
||||||
transaction.message.recent_blockhash
|
*transaction.get_recent_blockhash()
|
||||||
};
|
};
|
||||||
|
|
||||||
for status_retry in 0..GET_STATUS_RETRIES {
|
for status_retry in 0..GET_STATUS_RETRIES {
|
||||||
|
@ -711,7 +713,7 @@ impl RpcClient {
|
||||||
#[cfg(feature = "spinner")]
|
#[cfg(feature = "spinner")]
|
||||||
pub async fn send_and_confirm_transaction_with_spinner(
|
pub async fn send_and_confirm_transaction_with_spinner(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
self.send_and_confirm_transaction_with_spinner_and_commitment(
|
self.send_and_confirm_transaction_with_spinner_and_commitment(
|
||||||
transaction,
|
transaction,
|
||||||
|
@ -723,7 +725,7 @@ impl RpcClient {
|
||||||
#[cfg(feature = "spinner")]
|
#[cfg(feature = "spinner")]
|
||||||
pub async fn send_and_confirm_transaction_with_spinner_and_commitment(
|
pub async fn send_and_confirm_transaction_with_spinner_and_commitment(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
commitment: CommitmentConfig,
|
commitment: CommitmentConfig,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
self.send_and_confirm_transaction_with_spinner_and_config(
|
self.send_and_confirm_transaction_with_spinner_and_config(
|
||||||
|
@ -740,16 +742,16 @@ impl RpcClient {
|
||||||
#[cfg(feature = "spinner")]
|
#[cfg(feature = "spinner")]
|
||||||
pub async fn send_and_confirm_transaction_with_spinner_and_config(
|
pub async fn send_and_confirm_transaction_with_spinner_and_config(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
commitment: CommitmentConfig,
|
commitment: CommitmentConfig,
|
||||||
config: RpcSendTransactionConfig,
|
config: RpcSendTransactionConfig,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
let recent_blockhash = if transaction.uses_durable_nonce() {
|
||||||
self.get_latest_blockhash_with_commitment(CommitmentConfig::processed())
|
self.get_latest_blockhash_with_commitment(CommitmentConfig::processed())
|
||||||
.await?
|
.await?
|
||||||
.0
|
.0
|
||||||
} else {
|
} else {
|
||||||
transaction.message.recent_blockhash
|
*transaction.get_recent_blockhash()
|
||||||
};
|
};
|
||||||
let signature = self
|
let signature = self
|
||||||
.send_transaction_with_config(transaction, config)
|
.send_transaction_with_config(transaction, config)
|
||||||
|
@ -829,7 +831,10 @@ impl RpcClient {
|
||||||
/// # })?;
|
/// # })?;
|
||||||
/// # Ok::<(), Error>(())
|
/// # Ok::<(), Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn send_transaction(&self, transaction: &Transaction) -> ClientResult<Signature> {
|
pub async fn send_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: &impl SerializableTransaction,
|
||||||
|
) -> ClientResult<Signature> {
|
||||||
self.send_transaction_with_config(
|
self.send_transaction_with_config(
|
||||||
transaction,
|
transaction,
|
||||||
RpcSendTransactionConfig {
|
RpcSendTransactionConfig {
|
||||||
|
@ -927,7 +932,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn send_transaction_with_config(
|
pub async fn send_transaction_with_config(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
config: RpcSendTransactionConfig,
|
config: RpcSendTransactionConfig,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
let encoding = if let Some(encoding) = config.encoding {
|
let encoding = if let Some(encoding) = config.encoding {
|
||||||
|
@ -944,7 +949,7 @@ impl RpcClient {
|
||||||
preflight_commitment: Some(preflight_commitment.commitment),
|
preflight_commitment: Some(preflight_commitment.commitment),
|
||||||
..config
|
..config
|
||||||
};
|
};
|
||||||
let serialized_encoded = serialize_and_encode::<Transaction>(transaction, encoding)?;
|
let serialized_encoded = serialize_and_encode(transaction, encoding)?;
|
||||||
let signature_base58_str: String = match self
|
let signature_base58_str: String = match self
|
||||||
.send(
|
.send(
|
||||||
RpcRequest::SendTransaction,
|
RpcRequest::SendTransaction,
|
||||||
|
@ -984,14 +989,15 @@ impl RpcClient {
|
||||||
// should not be passed along to confirmation methods. The transaction may or may
|
// should not be passed along to confirmation methods. The transaction may or may
|
||||||
// not have been submitted to the cluster, so callers should verify the success of
|
// not have been submitted to the cluster, so callers should verify the success of
|
||||||
// the correct transaction signature independently.
|
// the correct transaction signature independently.
|
||||||
if signature != transaction.signatures[0] {
|
if signature != *transaction.get_signature() {
|
||||||
Err(RpcError::RpcRequestError(format!(
|
Err(RpcError::RpcRequestError(format!(
|
||||||
"RPC node returned mismatched signature {:?}, expected {:?}",
|
"RPC node returned mismatched signature {:?}, expected {:?}",
|
||||||
signature, transaction.signatures[0]
|
signature,
|
||||||
|
transaction.get_signature()
|
||||||
))
|
))
|
||||||
.into())
|
.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(transaction.signatures[0])
|
Ok(*transaction.get_signature())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1290,7 +1296,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn simulate_transaction(
|
pub async fn simulate_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||||
self.simulate_transaction_with_config(
|
self.simulate_transaction_with_config(
|
||||||
transaction,
|
transaction,
|
||||||
|
@ -1377,7 +1383,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn simulate_transaction_with_config(
|
pub async fn simulate_transaction_with_config(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
config: RpcSimulateTransactionConfig,
|
config: RpcSimulateTransactionConfig,
|
||||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||||
let encoding = if let Some(encoding) = config.encoding {
|
let encoding = if let Some(encoding) = config.encoding {
|
||||||
|
@ -1392,7 +1398,7 @@ impl RpcClient {
|
||||||
commitment: Some(commitment),
|
commitment: Some(commitment),
|
||||||
..config
|
..config
|
||||||
};
|
};
|
||||||
let serialized_encoded = serialize_and_encode::<Transaction>(transaction, encoding)?;
|
let serialized_encoded = serialize_and_encode(transaction, encoding)?;
|
||||||
self.send(
|
self.send(
|
||||||
RpcRequest::SimulateTransaction,
|
RpcRequest::SimulateTransaction,
|
||||||
json!([serialized_encoded, config]),
|
json!([serialized_encoded, config]),
|
||||||
|
|
|
@ -21,6 +21,7 @@ use {
|
||||||
nonblocking::{self, rpc_client::get_rpc_request_str},
|
nonblocking::{self, rpc_client::get_rpc_request_str},
|
||||||
rpc_sender::*,
|
rpc_sender::*,
|
||||||
},
|
},
|
||||||
|
serde::Serialize,
|
||||||
serde_json::Value,
|
serde_json::Value,
|
||||||
solana_account_decoder::{
|
solana_account_decoder::{
|
||||||
parse_token::{UiTokenAccount, UiTokenAmount},
|
parse_token::{UiTokenAccount, UiTokenAmount},
|
||||||
|
@ -43,7 +44,7 @@ use {
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
transaction::{self, Transaction},
|
transaction::{self, uses_durable_nonce, Transaction, VersionedTransaction},
|
||||||
},
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
|
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
|
||||||
|
@ -67,6 +68,36 @@ impl RpcClientConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait used to add support for versioned transactions to RPC APIs while
|
||||||
|
/// retaining backwards compatibility
|
||||||
|
pub trait SerializableTransaction: Serialize {
|
||||||
|
fn get_signature(&self) -> &Signature;
|
||||||
|
fn get_recent_blockhash(&self) -> &Hash;
|
||||||
|
fn uses_durable_nonce(&self) -> bool;
|
||||||
|
}
|
||||||
|
impl SerializableTransaction for Transaction {
|
||||||
|
fn get_signature(&self) -> &Signature {
|
||||||
|
&self.signatures[0]
|
||||||
|
}
|
||||||
|
fn get_recent_blockhash(&self) -> &Hash {
|
||||||
|
&self.message.recent_blockhash
|
||||||
|
}
|
||||||
|
fn uses_durable_nonce(&self) -> bool {
|
||||||
|
uses_durable_nonce(self).is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SerializableTransaction for VersionedTransaction {
|
||||||
|
fn get_signature(&self) -> &Signature {
|
||||||
|
&self.signatures[0]
|
||||||
|
}
|
||||||
|
fn get_recent_blockhash(&self) -> &Hash {
|
||||||
|
self.message.recent_blockhash()
|
||||||
|
}
|
||||||
|
fn uses_durable_nonce(&self) -> bool {
|
||||||
|
self.uses_durable_nonce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct GetConfirmedSignaturesForAddress2Config {
|
pub struct GetConfirmedSignaturesForAddress2Config {
|
||||||
pub before: Option<Signature>,
|
pub before: Option<Signature>,
|
||||||
|
@ -629,7 +660,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn send_and_confirm_transaction(
|
pub fn send_and_confirm_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
self.invoke((self.rpc_client.as_ref()).send_and_confirm_transaction(transaction))
|
self.invoke((self.rpc_client.as_ref()).send_and_confirm_transaction(transaction))
|
||||||
}
|
}
|
||||||
|
@ -637,7 +668,7 @@ impl RpcClient {
|
||||||
#[cfg(feature = "spinner")]
|
#[cfg(feature = "spinner")]
|
||||||
pub fn send_and_confirm_transaction_with_spinner(
|
pub fn send_and_confirm_transaction_with_spinner(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
self.invoke(
|
self.invoke(
|
||||||
(self.rpc_client.as_ref()).send_and_confirm_transaction_with_spinner(transaction),
|
(self.rpc_client.as_ref()).send_and_confirm_transaction_with_spinner(transaction),
|
||||||
|
@ -647,7 +678,7 @@ impl RpcClient {
|
||||||
#[cfg(feature = "spinner")]
|
#[cfg(feature = "spinner")]
|
||||||
pub fn send_and_confirm_transaction_with_spinner_and_commitment(
|
pub fn send_and_confirm_transaction_with_spinner_and_commitment(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
commitment: CommitmentConfig,
|
commitment: CommitmentConfig,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
self.invoke(
|
self.invoke(
|
||||||
|
@ -659,7 +690,7 @@ impl RpcClient {
|
||||||
#[cfg(feature = "spinner")]
|
#[cfg(feature = "spinner")]
|
||||||
pub fn send_and_confirm_transaction_with_spinner_and_config(
|
pub fn send_and_confirm_transaction_with_spinner_and_config(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
commitment: CommitmentConfig,
|
commitment: CommitmentConfig,
|
||||||
config: RpcSendTransactionConfig,
|
config: RpcSendTransactionConfig,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
|
@ -740,7 +771,10 @@ impl RpcClient {
|
||||||
/// let signature = rpc_client.send_transaction(&tx)?;
|
/// let signature = rpc_client.send_transaction(&tx)?;
|
||||||
/// # Ok::<(), Error>(())
|
/// # Ok::<(), Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult<Signature> {
|
pub fn send_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: &impl SerializableTransaction,
|
||||||
|
) -> ClientResult<Signature> {
|
||||||
self.invoke((self.rpc_client.as_ref()).send_transaction(transaction))
|
self.invoke((self.rpc_client.as_ref()).send_transaction(transaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,7 +859,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn send_transaction_with_config(
|
pub fn send_transaction_with_config(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
config: RpcSendTransactionConfig,
|
config: RpcSendTransactionConfig,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
self.invoke((self.rpc_client.as_ref()).send_transaction_with_config(transaction, config))
|
self.invoke((self.rpc_client.as_ref()).send_transaction_with_config(transaction, config))
|
||||||
|
@ -1025,7 +1059,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn simulate_transaction(
|
pub fn simulate_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||||
self.invoke((self.rpc_client.as_ref()).simulate_transaction(transaction))
|
self.invoke((self.rpc_client.as_ref()).simulate_transaction(transaction))
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1136,7 @@ impl RpcClient {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn simulate_transaction_with_config(
|
pub fn simulate_transaction_with_config(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &impl SerializableTransaction,
|
||||||
config: RpcSimulateTransactionConfig,
|
config: RpcSimulateTransactionConfig,
|
||||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||||
self.invoke(
|
self.invoke(
|
||||||
|
|
|
@ -20,6 +20,12 @@ use {
|
||||||
mod sanitized;
|
mod sanitized;
|
||||||
|
|
||||||
pub use sanitized::*;
|
pub use sanitized::*;
|
||||||
|
use {
|
||||||
|
crate::program_utils::limited_deserialize,
|
||||||
|
solana_program::{
|
||||||
|
nonce::NONCED_TX_MARKER_IX_INDEX, system_instruction::SystemInstruction, system_program,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/// Type that serializes to the string "legacy"
|
/// Type that serializes to the string "legacy"
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
@ -181,6 +187,35 @@ impl VersionedTransaction {
|
||||||
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
|
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if transaction begins with a valid advance nonce
|
||||||
|
/// instruction. Since dynamically loaded addresses can't have write locks
|
||||||
|
/// demoted without loading addresses, this shouldn't be used in the
|
||||||
|
/// runtime.
|
||||||
|
pub fn uses_durable_nonce(&self) -> bool {
|
||||||
|
let message = &self.message;
|
||||||
|
message
|
||||||
|
.instructions()
|
||||||
|
.get(NONCED_TX_MARKER_IX_INDEX as usize)
|
||||||
|
.filter(|instruction| {
|
||||||
|
// Is system program
|
||||||
|
matches!(
|
||||||
|
message.static_account_keys().get(instruction.program_id_index as usize),
|
||||||
|
Some(program_id) if system_program::check_id(program_id)
|
||||||
|
)
|
||||||
|
// Is a nonce advance instruction
|
||||||
|
&& matches!(
|
||||||
|
limited_deserialize(&instruction.data),
|
||||||
|
Ok(SystemInstruction::AdvanceNonceAccount)
|
||||||
|
)
|
||||||
|
// Nonce account is writable
|
||||||
|
&& matches!(
|
||||||
|
instruction.accounts.first(),
|
||||||
|
Some(index) if message.is_maybe_writable(*index as usize)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -190,6 +225,7 @@ mod tests {
|
||||||
crate::{
|
crate::{
|
||||||
message::Message as LegacyMessage,
|
message::Message as LegacyMessage,
|
||||||
signer::{keypair::Keypair, Signer},
|
signer::{keypair::Keypair, Signer},
|
||||||
|
system_instruction, sysvar,
|
||||||
},
|
},
|
||||||
solana_program::{
|
solana_program::{
|
||||||
instruction::{AccountMeta, Instruction},
|
instruction::{AccountMeta, Instruction},
|
||||||
|
@ -240,4 +276,105 @@ mod tests {
|
||||||
Err(err) => assert_eq!(Some(err), None),
|
Err(err) => assert_eq!(Some(err), None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn nonced_transfer_tx() -> (Pubkey, Pubkey, VersionedTransaction) {
|
||||||
|
let from_keypair = Keypair::new();
|
||||||
|
let from_pubkey = from_keypair.pubkey();
|
||||||
|
let nonce_keypair = Keypair::new();
|
||||||
|
let nonce_pubkey = nonce_keypair.pubkey();
|
||||||
|
let instructions = [
|
||||||
|
system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
|
||||||
|
system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
|
||||||
|
];
|
||||||
|
let message = LegacyMessage::new(&instructions, Some(&nonce_pubkey));
|
||||||
|
let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
|
||||||
|
(from_pubkey, nonce_pubkey, tx.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_uses_nonce_ok() {
|
||||||
|
let (_, _, tx) = nonced_transfer_tx();
|
||||||
|
assert!(tx.uses_durable_nonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_uses_nonce_empty_ix_fail() {
|
||||||
|
assert!(!VersionedTransaction::default().uses_durable_nonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_uses_nonce_bad_prog_id_idx_fail() {
|
||||||
|
let (_, _, mut tx) = nonced_transfer_tx();
|
||||||
|
match &mut tx.message {
|
||||||
|
VersionedMessage::Legacy(message) => {
|
||||||
|
message.instructions.get_mut(0).unwrap().program_id_index = 255u8;
|
||||||
|
}
|
||||||
|
VersionedMessage::V0(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
assert!(!tx.uses_durable_nonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_uses_nonce_first_prog_id_not_nonce_fail() {
|
||||||
|
let from_keypair = Keypair::new();
|
||||||
|
let from_pubkey = from_keypair.pubkey();
|
||||||
|
let nonce_keypair = Keypair::new();
|
||||||
|
let nonce_pubkey = nonce_keypair.pubkey();
|
||||||
|
let instructions = [
|
||||||
|
system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
|
||||||
|
system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
|
||||||
|
];
|
||||||
|
let message = LegacyMessage::new(&instructions, Some(&from_pubkey));
|
||||||
|
let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
|
||||||
|
let tx = VersionedTransaction::from(tx);
|
||||||
|
assert!(!tx.uses_durable_nonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_uses_ro_nonce_account() {
|
||||||
|
let from_keypair = Keypair::new();
|
||||||
|
let from_pubkey = from_keypair.pubkey();
|
||||||
|
let nonce_keypair = Keypair::new();
|
||||||
|
let nonce_pubkey = nonce_keypair.pubkey();
|
||||||
|
let account_metas = vec![
|
||||||
|
AccountMeta::new_readonly(nonce_pubkey, false),
|
||||||
|
#[allow(deprecated)]
|
||||||
|
AccountMeta::new_readonly(sysvar::recent_blockhashes::id(), false),
|
||||||
|
AccountMeta::new_readonly(nonce_pubkey, true),
|
||||||
|
];
|
||||||
|
let nonce_instruction = Instruction::new_with_bincode(
|
||||||
|
system_program::id(),
|
||||||
|
&system_instruction::SystemInstruction::AdvanceNonceAccount,
|
||||||
|
account_metas,
|
||||||
|
);
|
||||||
|
let tx = Transaction::new_signed_with_payer(
|
||||||
|
&[nonce_instruction],
|
||||||
|
Some(&from_pubkey),
|
||||||
|
&[&from_keypair, &nonce_keypair],
|
||||||
|
Hash::default(),
|
||||||
|
);
|
||||||
|
let tx = VersionedTransaction::from(tx);
|
||||||
|
assert!(!tx.uses_durable_nonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_uses_nonce_wrong_first_nonce_ix_fail() {
|
||||||
|
let from_keypair = Keypair::new();
|
||||||
|
let from_pubkey = from_keypair.pubkey();
|
||||||
|
let nonce_keypair = Keypair::new();
|
||||||
|
let nonce_pubkey = nonce_keypair.pubkey();
|
||||||
|
let instructions = [
|
||||||
|
system_instruction::withdraw_nonce_account(
|
||||||
|
&nonce_pubkey,
|
||||||
|
&nonce_pubkey,
|
||||||
|
&from_pubkey,
|
||||||
|
42,
|
||||||
|
),
|
||||||
|
system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
|
||||||
|
];
|
||||||
|
let message = LegacyMessage::new(&instructions, Some(&nonce_pubkey));
|
||||||
|
let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
|
||||||
|
let tx = VersionedTransaction::from(tx);
|
||||||
|
assert!(!tx.uses_durable_nonce());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue