Skip to content

Commit a7d8998

Browse files
Backport PR #65 to solana-v2.0 (#68)
Co-authored-by: Joe C <[email protected]>
1 parent 9cb2390 commit a7d8998

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Backport to solana-v1.18
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
jobs:
8+
backport:
9+
runs-on: ubuntu-latest
10+
11+
permissions:
12+
pull-requests: write
13+
contents: write
14+
15+
steps:
16+
- name: Git Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Backport to v1.18
20+
uses: ./.github/actions/backport
21+
with:
22+
label: v1.18
23+
base_branch: solana-v1.18
24+
github_token: ${{ secrets.GITHUB_TOKEN }}

harness/tests/fd_test_vectors.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#![cfg(feature = "fuzz-fd")]
2+
3+
use {
4+
mollusk_svm::fuzz::firedancer::{build_fixture_from_mollusk_test, load_firedancer_fixture},
5+
mollusk_svm_fuzz_fixture_firedancer::{account::SeedAddress, Fixture},
6+
rayon::prelude::*,
7+
solana_sdk::{
8+
account::AccountSharedData, feature_set::FeatureSet, pubkey::Pubkey,
9+
transaction_context::InstructionAccount,
10+
},
11+
std::{assert_eq, fs, path::Path, process::Command},
12+
};
13+
14+
const TEST_VECTORS_PATH: &str = "tests/test-vectors";
15+
const TEST_VECTORS_REPOSITORY: &str = "https://github.com/firedancer-io/test-vectors.git";
16+
const TEST_VECTORS_TO_TEST: &[&str] = &[
17+
"instr/fixtures/address-lookup-table",
18+
"instr/fixtures/config",
19+
"instr/fixtures/stake",
20+
// Add more here!
21+
];
22+
23+
#[test]
24+
fn test_load_firedancer_fixtures() {
25+
let test_vectors_out_dir = Path::new(TEST_VECTORS_PATH);
26+
27+
// Fetch the test vectors.
28+
if test_vectors_out_dir.exists() {
29+
Command::new("git")
30+
.arg("-C")
31+
.arg(test_vectors_out_dir)
32+
.arg("fetch")
33+
.status()
34+
.expect("Failed to execute git pull");
35+
} else {
36+
Command::new("git")
37+
.arg("clone")
38+
.arg("--depth=1")
39+
.arg(TEST_VECTORS_REPOSITORY)
40+
.arg(test_vectors_out_dir)
41+
.status()
42+
.expect("Failed to execute git clone");
43+
}
44+
45+
// Attempt to go fixture -> Mollusk -> fixture and compare.
46+
TEST_VECTORS_TO_TEST.par_iter().for_each(|directory| {
47+
fs::read_dir(test_vectors_out_dir.join(directory))
48+
.unwrap()
49+
.par_bridge()
50+
.for_each(|entry| {
51+
let path = entry.unwrap().path();
52+
if path.is_file() && path.extension().is_some_and(|ext| ext == "fix") {
53+
let loaded_fixture = Fixture::load_from_blob_file(path.to_str().unwrap());
54+
let (mollusk, instruction, accounts, result) =
55+
load_firedancer_fixture(&loaded_fixture);
56+
let generated_fixture = build_fixture_from_mollusk_test(
57+
&mollusk,
58+
&instruction,
59+
&accounts,
60+
&result,
61+
/* checks */ &[],
62+
);
63+
64+
assert_eq!(loaded_fixture.metadata, generated_fixture.metadata);
65+
assert_eq!(
66+
loaded_fixture.input.program_id,
67+
generated_fixture.input.program_id,
68+
);
69+
// Sometimes ordering is not the same because of the `KeyMap`.
70+
// Contents should match though.
71+
compare_accounts(
72+
&loaded_fixture.input.accounts,
73+
&generated_fixture.input.accounts,
74+
);
75+
compare_instruction_accounts(
76+
&loaded_fixture.input.instruction_accounts,
77+
&generated_fixture.input.instruction_accounts,
78+
);
79+
assert_eq!(
80+
loaded_fixture.input.compute_units_available,
81+
generated_fixture.input.compute_units_available,
82+
);
83+
assert_eq!(
84+
loaded_fixture.input.slot_context,
85+
generated_fixture.input.slot_context,
86+
);
87+
// Feature set is not always ordered the same as a side effect
88+
// of `HashMap`.
89+
compare_feature_sets(
90+
&loaded_fixture.input.epoch_context.feature_set,
91+
&generated_fixture.input.epoch_context.feature_set,
92+
);
93+
assert_eq!(
94+
loaded_fixture.output.program_result,
95+
generated_fixture.output.program_result,
96+
);
97+
assert_eq!(
98+
loaded_fixture.output.program_custom_code,
99+
generated_fixture.output.program_custom_code,
100+
);
101+
compare_accounts(
102+
&loaded_fixture.output.modified_accounts,
103+
&generated_fixture.output.modified_accounts,
104+
);
105+
assert_eq!(
106+
loaded_fixture.output.compute_units_available,
107+
generated_fixture.output.compute_units_available,
108+
);
109+
// assert_eq!(
110+
// loaded_fixture.output.return_data,
111+
// generated_fixture.output.return_data,
112+
// );
113+
}
114+
});
115+
});
116+
}
117+
118+
fn compare_accounts(
119+
a: &[(Pubkey, AccountSharedData, Option<SeedAddress>)],
120+
b: &[(Pubkey, AccountSharedData, Option<SeedAddress>)],
121+
) -> bool {
122+
if a.len() != b.len() {
123+
return false;
124+
}
125+
126+
let mut a_sorted = a.to_vec();
127+
let mut b_sorted = b.to_vec();
128+
129+
// Sort by Pubkey
130+
a_sorted.sort_by(|(pubkey_a, _, _), (pubkey_b, _, _)| pubkey_a.cmp(pubkey_b));
131+
b_sorted.sort_by(|(pubkey_a, _, _), (pubkey_b, _, _)| pubkey_a.cmp(pubkey_b));
132+
133+
// Compare sorted lists
134+
a_sorted == b_sorted
135+
}
136+
137+
fn compare_instruction_accounts(a: &[InstructionAccount], b: &[InstructionAccount]) -> bool {
138+
if a.len() != b.len() {
139+
return false;
140+
}
141+
142+
let mut a_sorted = a.to_vec();
143+
let mut b_sorted = b.to_vec();
144+
145+
// Sort by Pubkey
146+
a_sorted.sort_by(|ia_a, ia_b| ia_a.index_in_transaction.cmp(&ia_b.index_in_transaction));
147+
b_sorted.sort_by(|ia_a, ia_b| ia_a.index_in_transaction.cmp(&ia_b.index_in_transaction));
148+
149+
// Compare sorted lists
150+
a_sorted == b_sorted
151+
}
152+
153+
fn compare_feature_sets(from_fixture: &FeatureSet, from_mollusk: &FeatureSet) {
154+
assert_eq!(from_fixture.active.len(), from_mollusk.active.len());
155+
assert_eq!(from_fixture.inactive.len(), from_mollusk.inactive.len());
156+
for f in from_fixture.active.keys() {
157+
assert!(from_mollusk.active.contains_key(f));
158+
}
159+
}

0 commit comments

Comments
 (0)