Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 2120ef5

Browse files
mergify[bot]jstarryjackcmay
authored
Fix ed25519 builtin program handling (backport #23182) (#23195)
* Fix ed25519 builtin program handling (#23182) * Fix ed25519 builtin program handling * Fix tests * Add integration tests for processing transactions with ed25519 ixs * Fix another test * fix formatting (cherry picked from commit 813725d) * fix tests Co-authored-by: Justin Starry <[email protected]> Co-authored-by: Jack May <[email protected]>
1 parent c08af09 commit 2120ef5

File tree

9 files changed

+182
-17
lines changed

9 files changed

+182
-17
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ members = [
4848
"program-test",
4949
"programs/address-lookup-table",
5050
"programs/address-lookup-table-tests",
51+
"programs/ed25519-tests",
5152
"programs/bpf_loader",
5253
"programs/bpf_loader/gen-syscall-list",
5354
"programs/compute-budget",

program-test/src/lib.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use {
2525
account_info::AccountInfo,
2626
clock::Slot,
2727
entrypoint::{ProgramResult, SUCCESS},
28+
feature_set::FEATURE_NAMES,
2829
fee_calculator::{FeeCalculator, FeeRateGovernor},
2930
genesis_config::{ClusterType, GenesisConfig},
3031
hash::Hash,
@@ -41,7 +42,7 @@ use {
4142
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
4243
std::{
4344
cell::RefCell,
44-
collections::HashMap,
45+
collections::{HashMap, HashSet},
4546
convert::TryFrom,
4647
fs::File,
4748
io::{self, Read},
@@ -58,7 +59,10 @@ use {
5859
tokio::task::JoinHandle,
5960
};
6061
// Export types so test clients can limit their solana crate dependencies
61-
pub use {solana_banks_client::BanksClient, solana_program_runtime::invoke_context::InvokeContext};
62+
pub use {
63+
solana_banks_client::{BanksClient, BanksClientError},
64+
solana_program_runtime::invoke_context::InvokeContext,
65+
};
6266

6367
pub mod programs;
6468

@@ -469,6 +473,7 @@ pub struct ProgramTest {
469473
compute_max_units: Option<u64>,
470474
prefer_bpf: bool,
471475
use_bpf_jit: bool,
476+
deactivate_feature_set: HashSet<Pubkey>,
472477
}
473478

474479
impl Default for ProgramTest {
@@ -499,6 +504,7 @@ impl Default for ProgramTest {
499504
compute_max_units: None,
500505
prefer_bpf,
501506
use_bpf_jit: false,
507+
deactivate_feature_set: HashSet::default(),
502508
}
503509
}
504510
}
@@ -728,6 +734,13 @@ impl ProgramTest {
728734
.push(Builtin::new(program_name, program_id, process_instruction));
729735
}
730736

737+
/// Deactivate a runtime feature.
738+
///
739+
/// Note that all features are activated by default.
740+
pub fn deactivate_feature(&mut self, feature_id: Pubkey) {
741+
self.deactivate_feature_set.insert(feature_id);
742+
}
743+
731744
fn setup_bank(
732745
&self,
733746
) -> (
@@ -767,6 +780,25 @@ impl ProgramTest {
767780
ClusterType::Development,
768781
vec![],
769782
);
783+
784+
// Remove features tagged to deactivate
785+
for deactivate_feature_pk in &self.deactivate_feature_set {
786+
if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
787+
match genesis_config.accounts.remove(deactivate_feature_pk) {
788+
Some(_) => debug!("Feature for {:?} deactivated", deactivate_feature_pk),
789+
None => warn!(
790+
"Feature {:?} set for deactivation not found in genesis_config account list, ignored.",
791+
deactivate_feature_pk
792+
),
793+
}
794+
} else {
795+
warn!(
796+
"Feature {:?} set for deactivation is not a known Feature public key",
797+
deactivate_feature_pk
798+
);
799+
}
800+
}
801+
770802
let target_tick_duration = Duration::from_micros(100);
771803
genesis_config.poh_config = PohConfig::new_sleep(target_tick_duration);
772804
debug!("Payer address: {}", mint_keypair.pubkey());

programs/ed25519-tests/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "solana-ed25519-program-tests"
3+
version = "1.9.7"
4+
authors = ["Solana Maintainers <[email protected]>"]
5+
repository = "https://github.com/solana-labs/solana"
6+
license = "Apache-2.0"
7+
homepage = "https://solana.com/"
8+
edition = "2021"
9+
publish = false
10+
11+
[dev-dependencies]
12+
assert_matches = "1.5.0"
13+
ed25519-dalek = "=1.0.1"
14+
rand = "0.7.0"
15+
solana-program-test = { path = "../../program-test", version = "=1.9.7" }
16+
solana-sdk = { path = "../../sdk", version = "=1.9.7" }
17+
18+
[package.metadata.docs.rs]
19+
targets = ["x86_64-unknown-linux-gnu"]
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use {
2+
assert_matches::assert_matches,
3+
rand::thread_rng,
4+
solana_program_test::*,
5+
solana_sdk::{
6+
ed25519_instruction::new_ed25519_instruction,
7+
feature_set,
8+
signature::Signer,
9+
transaction::{Transaction, TransactionError},
10+
transport::TransportError,
11+
},
12+
};
13+
14+
#[tokio::test]
15+
async fn test_success() {
16+
let mut context = ProgramTest::default().start_with_context().await;
17+
18+
let client = &mut context.banks_client;
19+
let payer = &context.payer;
20+
let recent_blockhash = context.last_blockhash;
21+
22+
let privkey = ed25519_dalek::Keypair::generate(&mut thread_rng());
23+
let message_arr = b"hello";
24+
let instruction = new_ed25519_instruction(&privkey, message_arr);
25+
26+
let transaction = Transaction::new_signed_with_payer(
27+
&[instruction],
28+
Some(&payer.pubkey()),
29+
&[payer],
30+
recent_blockhash,
31+
);
32+
33+
assert_matches!(client.process_transaction(transaction).await, Ok(()));
34+
}
35+
36+
#[tokio::test]
37+
async fn test_failure() {
38+
let mut context = ProgramTest::default().start_with_context().await;
39+
40+
let client = &mut context.banks_client;
41+
let payer = &context.payer;
42+
let recent_blockhash = context.last_blockhash;
43+
44+
let privkey = ed25519_dalek::Keypair::generate(&mut thread_rng());
45+
let message_arr = b"hello";
46+
let mut instruction = new_ed25519_instruction(&privkey, message_arr);
47+
48+
instruction.data[0] += 1;
49+
50+
let transaction = Transaction::new_signed_with_payer(
51+
&[instruction],
52+
Some(&payer.pubkey()),
53+
&[payer],
54+
recent_blockhash,
55+
);
56+
57+
assert_matches!(
58+
client.process_transaction(transaction).await,
59+
Err(TransportError::TransactionError(
60+
TransactionError::InvalidAccountIndex
61+
))
62+
);
63+
}
64+
65+
#[tokio::test]
66+
async fn test_success_call_builtin_program() {
67+
let mut program_test = ProgramTest::default();
68+
program_test.deactivate_feature(feature_set::prevent_calling_precompiles_as_programs::id());
69+
let mut context = program_test.start_with_context().await;
70+
71+
let client = &mut context.banks_client;
72+
let payer = &context.payer;
73+
let recent_blockhash = context.last_blockhash;
74+
75+
let privkey = ed25519_dalek::Keypair::generate(&mut thread_rng());
76+
let message_arr = b"hello";
77+
let instruction = new_ed25519_instruction(&privkey, message_arr);
78+
79+
let transaction = Transaction::new_signed_with_payer(
80+
&[instruction],
81+
Some(&payer.pubkey()),
82+
&[payer],
83+
recent_blockhash,
84+
);
85+
86+
assert_matches!(client.process_transaction(transaction).await, Ok(()));
87+
}

runtime/src/bank.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7101,7 +7101,7 @@ pub(crate) mod tests {
71017101
cluster_type: ClusterType::MainnetBeta,
71027102
..GenesisConfig::default()
71037103
}));
7104-
let sysvar_and_builtin_program_delta0 = 11;
7104+
let sysvar_and_builtin_program_delta0 = 12;
71057105
assert_eq!(
71067106
bank0.capitalization(),
71077107
42 * 42 + sysvar_and_builtin_program_delta0
@@ -8856,7 +8856,7 @@ pub(crate) mod tests {
88568856
// not being eagerly-collected for exact rewards calculation
88578857
bank0.restore_old_behavior_for_fragile_tests();
88588858

8859-
let sysvar_and_builtin_program_delta0 = 11;
8859+
let sysvar_and_builtin_program_delta0 = 12;
88608860
assert_eq!(
88618861
bank0.capitalization(),
88628862
42 * 1_000_000_000 + sysvar_and_builtin_program_delta0
@@ -8991,7 +8991,7 @@ pub(crate) mod tests {
89918991
// not being eagerly-collected for exact rewards calculation
89928992
bank.restore_old_behavior_for_fragile_tests();
89938993

8994-
let sysvar_and_builtin_program_delta = 11;
8994+
let sysvar_and_builtin_program_delta = 12;
89958995
assert_eq!(
89968996
bank.capitalization(),
89978997
42 * 1_000_000_000 + sysvar_and_builtin_program_delta
@@ -13041,25 +13041,25 @@ pub(crate) mod tests {
1304113041
if bank.slot == 0 {
1304213042
assert_eq!(
1304313043
bank.hash().to_string(),
13044-
"DqaWg7EVKzb5Fpe92zNBtXAWqLwcedgHDicYrCBnf3QK"
13044+
"CMCWTWsU67zjmayMhSMGBTzHbW1WMCtkM5m7xk9qSnY5"
1304513045
);
1304613046
}
1304713047
if bank.slot == 32 {
1304813048
assert_eq!(
1304913049
bank.hash().to_string(),
13050-
"AYdhzhKrM74r9XuZBDGcHeFzg2DEtp1boggnEnzDjZSq"
13050+
"4kbXeShX8vMnRuuADCkxSEir1oc2PrBNbx6vPkWcDtJU"
1305113051
);
1305213052
}
1305313053
if bank.slot == 64 {
1305413054
assert_eq!(
1305513055
bank.hash().to_string(),
13056-
"EsbPVYzo1qz5reEUH5okKW4ExB6WbcidkVdW5mzpFn7C"
13056+
"CSZ8QCDF8qhqKDxafPzjNJpHcRAXmQzAb8eUi1Emt35E"
1305713057
);
1305813058
}
1305913059
if bank.slot == 128 {
1306013060
assert_eq!(
1306113061
bank.hash().to_string(),
13062-
"H3DWrQ6FqbLkFNDxbWQ62UKRbw2dbuxf3oVF2VpBk6Ga"
13062+
"Ewh1SYKy8eiSE77sEvjav33SznfWYSwa5TwqbiYWseG2"
1306313063
);
1306413064
break;
1306513065
}
@@ -13287,7 +13287,7 @@ pub(crate) mod tests {
1328713287
// No more slots should be shrunk
1328813288
assert_eq!(bank2.shrink_candidate_slots(), 0);
1328913289
// alive_counts represents the count of alive accounts in the three slots 0,1,2
13290-
assert_eq!(alive_counts, vec![10, 1, 7]);
13290+
assert_eq!(alive_counts, vec![11, 1, 7]);
1329113291
}
1329213292

1329313293
#[test]
@@ -13335,7 +13335,7 @@ pub(crate) mod tests {
1333513335
.map(|_| bank.process_stale_slot_with_budget(0, force_to_return_alive_account))
1333613336
.sum();
1333713337
// consumed_budgets represents the count of alive accounts in the three slots 0,1,2
13338-
assert_eq!(consumed_budgets, 11);
13338+
assert_eq!(consumed_budgets, 12);
1333913339
}
1334013340

1334113341
#[test]

runtime/src/builtins.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,15 @@ fn genesis_builtins() -> Vec<Builtin> {
129129
solana_sdk::secp256k1_program::id(),
130130
dummy_process_instruction,
131131
),
132+
Builtin::new(
133+
"ed25519_program",
134+
solana_sdk::ed25519_program::id(),
135+
dummy_process_instruction,
136+
),
132137
]
133138
}
134139

135-
/// place holder for secp256k1, remove when the precompile program is deactivated via feature activation
140+
/// place holder for precompile programs, remove when the precompile program is deactivated via feature activation
136141
fn dummy_process_instruction(
137142
_first_instruction_account: usize,
138143
_data: &[u8],
@@ -172,6 +177,18 @@ fn feature_builtins() -> Vec<(Builtin, Pubkey, ActivationType)> {
172177
feature_set::prevent_calling_precompiles_as_programs::id(),
173178
ActivationType::RemoveProgram,
174179
),
180+
// TODO when feature `prevent_calling_precompiles_as_programs` is
181+
// cleaned up also remove "ed25519_program" from the main builtins
182+
// list
183+
(
184+
Builtin::new(
185+
"ed25519_program",
186+
solana_sdk::ed25519_program::id(),
187+
dummy_process_instruction,
188+
),
189+
feature_set::prevent_calling_precompiles_as_programs::id(),
190+
ActivationType::RemoveProgram,
191+
),
175192
(
176193
Builtin::new(
177194
"address_lookup_table_program",

runtime/src/non_circulating_supply.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ mod tests {
278278
..GenesisConfig::default()
279279
};
280280
let mut bank = Arc::new(Bank::new_for_tests(&genesis_config));
281-
let sysvar_and_native_program_delta = 11;
281+
let sysvar_and_native_program_delta = 12;
282282
assert_eq!(
283283
bank.capitalization(),
284284
(num_genesis_accounts + num_non_circulating_accounts + num_stake_accounts) * balance

sdk/src/precompiles.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
use {
66
crate::{
77
decode_error::DecodeError,
8-
feature_set::{
9-
ed25519_program_enabled, prevent_calling_precompiles_as_programs, FeatureSet,
10-
},
8+
feature_set::{prevent_calling_precompiles_as_programs, FeatureSet},
119
instruction::CompiledInstruction,
1210
pubkey::Pubkey,
1311
},
@@ -88,7 +86,7 @@ lazy_static! {
8886
),
8987
Precompile::new(
9088
crate::ed25519_program::id(),
91-
Some(ed25519_program_enabled::id()),
89+
Some(prevent_calling_precompiles_as_programs::id()),
9290
crate::ed25519_instruction::verify,
9391
),
9492
];

0 commit comments

Comments
 (0)