From 5506b5d5a1476faf72fd5557f7950913ddf63d0e Mon Sep 17 00:00:00 2001 From: Trent Nelson <490004+t-nelson@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:19:40 -0700 Subject: [PATCH] feat(ledger-tool): fix ed25519 program testnet deployment (#9352) (cherry picked from commit 5f82055840289bad9ab22ed79bd27ff2ae2b7995) --- ledger-tool/src/main.rs | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 82e7abddead6ad..0463231a5263c0 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -1516,6 +1516,14 @@ fn main() { .long("enable-capitalization-change") .takes_value(false) .help("If snapshot creation should succeed with a capitalization delta."), + ) + .arg( + Arg::with_name("fix_testnet_ed25519_precompile_account") + .long("fix-testnet-ed25519-precompile-account") + .help( + "correct misassigned owner and data on testnet ed25519 precompile \ + account deployment", + ), ), ) .subcommand( @@ -2072,6 +2080,9 @@ fn main() { archive_format }; + let fix_testnet_ed25519_precompile_account = + arg_matches.is_present("fix_testnet_ed25519_precompile_account"); + let genesis_config = open_genesis_config_by(&ledger_path, arg_matches); let mut process_options = parse_process_options(&ledger_path, arg_matches); @@ -2172,7 +2183,8 @@ fn main() { || !feature_gates_to_deactivate.is_empty() || !vote_accounts_to_destake.is_empty() || faucet_pubkey.is_some() - || bootstrap_validator_pubkeys.is_some(); + || bootstrap_validator_pubkeys.is_some() + || fix_testnet_ed25519_precompile_account; if child_bank_required { let mut child_bank = Bank::new_from_parent( @@ -2284,6 +2296,48 @@ fn main() { } } + if fix_testnet_ed25519_precompile_account { + use solana_sdk_ids::{ed25519_program, native_loader, system_program}; + + if bank.cluster_type() != ClusterType::Testnet { + eprintln!( + "--fix-testnet-ed25519-precompile-account is incompatible with \ + the supplied base snapshot" + ); + std::process::exit(1); + } + + let mut ed25519_program_account = + bank.get_account(&ed25519_program::id()).unwrap_or_else(|| { + eprintln!("Error: `{}` is not deployed", ed25519_program::id()); + exit(1); + }); + + if ed25519_program_account.owner() != &system_program::id() { + eprintln!( + "Error: expected `{}` to be owned by `{}`, found `{}`", + ed25519_program::id(), + system_program::id(), + ed25519_program_account.owner(), + ); + exit(1); + } + + if !ed25519_program_account.data().is_empty() { + eprintln!( + "Error: expected `{}` account data to be empty, found {} bytes", + ed25519_program::id(), + ed25519_program_account.data().len(), + ); + exit(1); + } + + ed25519_program_account.set_owner(native_loader::id()); + ed25519_program_account.set_data_from_slice(b"ed25519_program"); + + bank.store_account(&ed25519_program::id(), &ed25519_program_account); + } + if let Some(bootstrap_validator_pubkeys) = bootstrap_validator_pubkeys { assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);