Skip to content

Commit

Permalink
impl UiTransaction -> VersionedTransaction
Browse files Browse the repository at this point in the history
  • Loading branch information
OliverNChalk committed Nov 30, 2024
1 parent 7be6037 commit 91cebf5
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 12 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/svm-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ async-trait = "0.1.81"
auto_impl.workspace = true
dashmap = "6.0.1"
derivative = "2.2.0"
eyre = "0.6.12"
flate2 = "1.0.32"
futures = "0.3.30"
itertools = "0.13.0"
Expand Down
184 changes: 172 additions & 12 deletions crates/svm-test/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use solana_sdk::account::Account;
use solana_sdk::clock::Epoch;
use solana_sdk::instruction::CompiledInstruction;
use solana_sdk::message::v0::MessageAddressTableLookup;
use solana_sdk::message::{legacy, v0, VersionedMessage};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::transaction::VersionedTransaction;
use solana_transaction_status::{EncodableWithMeta, EncodedTransaction, UiTransaction};
use solana_transaction_status::{
EncodableWithMeta, EncodedTransaction, UiMessage, UiRawMessage, UiTransaction,
};

serde_with::serde_conv!(
pub AccountAsJsonAccount,
Expand Down Expand Up @@ -67,14 +72,92 @@ fn to_json_tx(tx: &VersionedTransaction) -> UiTransaction {
}
}

fn from_json_tx(tx: UiTransaction) -> Result<VersionedTransaction, std::convert::Infallible> {
Ok(EncodedTransaction::Json(tx).decode().unwrap())
fn from_json_tx(tx: UiTransaction) -> Result<VersionedTransaction, eyre::Error> {
Ok(VersionedTransaction {
signatures: tx
.signatures
.iter()
.try_fold(Vec::default(), |mut sigs, sig| {
sig.parse().map(|sig| {
sigs.push(sig);

sigs
})
})?,
message: match tx.message {
UiMessage::Parsed(_) => unimplemented!(),
UiMessage::Raw(UiRawMessage {
header,
account_keys,
recent_blockhash,
instructions,
address_table_lookups,
}) => {
let account_keys =
account_keys
.iter()
.try_fold(Vec::default(), |mut keys, key| {
key.parse().map(|key| {
keys.push(key);

keys
})
})?;
let recent_blockhash = recent_blockhash.parse()?;
let instructions =
instructions
.into_iter()
.try_fold(Vec::default(), |mut ixs, ix| {
solana_sdk::bs58::decode(&ix.data).into_vec().map(|data| {
ixs.push(CompiledInstruction {
program_id_index: ix.program_id_index,
accounts: ix.accounts,
data,
});

ixs
})
})?;

match address_table_lookups {
Some(address_table_lookups) => VersionedMessage::V0(v0::Message {
header,
account_keys,
recent_blockhash,
instructions,
address_table_lookups: address_table_lookups.into_iter().try_fold(
Vec::default(),
|mut alts, alt| {
alt.account_key.parse().map(|account_key| {
alts.push(MessageAddressTableLookup {
account_key,
writable_indexes: alt.writable_indexes,
readonly_indexes: alt.readonly_indexes,
});

alts
})
},
)?,
}),
None => VersionedMessage::Legacy(legacy::Message {
header,
account_keys,
recent_blockhash,
instructions,
}),
}
}
},
})
}

#[cfg(test)]
mod tests {
use expect_test::expect;
use solana_sdk::address_lookup_table::AddressLookupTableAccount;
use solana_sdk::hash::Hash;
use solana_sdk::message::VersionedMessage;
use solana_sdk::signer::Signer;
use solana_sdk::system_instruction;
use solana_sdk::transaction::Transaction;
Expand All @@ -85,18 +168,24 @@ mod tests {
const DUMMY_PUBKEY: Pubkey = Pubkey::new_from_array([1; 32]);
const DUMMY_HASH: Hash = Hash::new_from_array([2; 32]);

#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
struct Wrapper(#[serde_as(as = "TxAsJsonTx")] VersionedTransaction);

#[test]
fn serialize_legacy_transaction() {
let tx = Transaction::new_signed_with_payer(
&[system_instruction::transfer(&test_payer_keypair().pubkey(), &DUMMY_PUBKEY, 500)],
Some(&test_payer_keypair().pubkey()),
&[test_payer_keypair()],
DUMMY_HASH,
)
.into();
fn round_trip_legacy_tx() {
let tx = Wrapper(
Transaction::new_signed_with_payer(
&[system_instruction::transfer(&test_payer_keypair().pubkey(), &DUMMY_PUBKEY, 500)],
Some(&test_payer_keypair().pubkey()),
&[test_payer_keypair()],
DUMMY_HASH,
)
.into(),
);

// Act - Serialize.
let serialized = serde_json::to_string_pretty(&to_json_tx(&tx)).unwrap();
let serialized = serde_json::to_string_pretty(&tx).unwrap();

// Assert.
expect![[r#"
Expand Down Expand Up @@ -130,5 +219,76 @@ mod tests {
}
}"#]]
.assert_eq(&serialized);

// Act - Recover.
let recovered: Wrapper = serde_json::from_str(&serialized).unwrap();

// Assert.
assert_eq!(recovered.0, tx.0);
}

#[test]
fn round_trip_v0_tx() {
let message = solana_sdk::message::v0::Message::try_compile(
&test_payer_keypair().pubkey(),
&[system_instruction::transfer(&test_payer_keypair().pubkey(), &DUMMY_PUBKEY, 500)],
&vec![AddressLookupTableAccount { key: DUMMY_PUBKEY, addresses: vec![DUMMY_PUBKEY] }],
DUMMY_HASH,
)
.unwrap();
let tx = Wrapper(
VersionedTransaction::try_new(VersionedMessage::V0(message), &[test_payer_keypair()])
.unwrap(),
);

// Act - Serialize.
let serialized = serde_json::to_string_pretty(&tx).unwrap();

// Assert.
expect![[r#"
{
"signatures": [
"44z9pb2J9mfT6VSj3hL2KmFUjU7BQub8xV1iyCTduaSVb83db1GL4gfbamAgrP2GB1yk7rkLHmHfsB1mgxouxDRH"
],
"message": {
"header": {
"numRequiredSignatures": 1,
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 1
},
"accountKeys": [
"AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
"11111111111111111111111111111111"
],
"recentBlockhash": "8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR",
"instructions": [
{
"programIdIndex": 1,
"accounts": [
0,
2
],
"data": "3Bxs4hfoaMPsQgGf",
"stackHeight": null
}
],
"addressTableLookups": [
{
"accountKey": "4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi",
"writableIndexes": [
0
],
"readonlyIndexes": []
}
]
}
}"#]]
.assert_eq(&serialized);

// Act - Recover.
let recovered: Wrapper = serde_json::from_str(&serialized).unwrap();

// Assert.
assert_eq!(recovered.0, tx.0);
}
}

0 comments on commit 91cebf5

Please sign in to comment.