Skip to content

Refactor - Makes loader-v4 the default #2796

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion builtins/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ pub static BUILTINS: &[BuiltinPrototype] = &[
testable_prototype!(BuiltinPrototype {
core_bpf_migration_config: None,
name: loader_v4,
enable_feature_id: Some(feature_set::enable_program_runtime_v2_and_loader_v4::id()),
enable_feature_id: Some(feature_set::enable_loader_v4::id()),
program_id: solana_sdk_ids::loader_v4::id(),
entrypoint: solana_loader_v4_program::Entrypoint::vm,
}),
Expand Down
134 changes: 59 additions & 75 deletions cli/tests/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use {
solana_client::rpc_config::RpcSendTransactionConfig,
solana_commitment_config::CommitmentConfig,
solana_faucet::faucet::run_local_faucet,
solana_feature_set::enable_alt_bn128_syscall,
solana_feature_set::{enable_alt_bn128_syscall, enable_loader_v4},
solana_rpc::rpc::JsonRpcConfig,
solana_rpc_client::rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
solana_rpc_client_api::config::RpcTransactionConfig,
Expand All @@ -33,7 +33,7 @@ use {
transaction::Transaction,
},
solana_streamer::socket::SocketAddrSpace,
solana_test_validator::{TestValidator, TestValidatorGenesis},
solana_test_validator::TestValidatorGenesis,
solana_transaction_status::UiTransactionEncoding,
std::{
env,
Expand All @@ -45,6 +45,20 @@ use {
test_case::test_case,
};

fn test_validator_genesis(mint_keypair: Keypair) -> TestValidatorGenesis {
let mut genesis = TestValidatorGenesis::default();
genesis
.fee_rate_governor(FeeRateGovernor::new(0, 0))
.rent(Rent {
lamports_per_byte_year: 1,
exemption_threshold: 1.0,
..Rent::default()
})
.faucet_addr(Some(run_local_faucet(mint_keypair, None)))
.deactivate_features(&[enable_loader_v4::id()]);
genesis
}

#[track_caller]
fn expect_command_failure(config: &CliConfig, should_fail_because: &str, error_expected: &str) {
let error_actual = process_command(config).expect_err(should_fail_because);
Expand Down Expand Up @@ -81,9 +95,9 @@ fn test_cli_program_deploy_non_upgradeable() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -286,9 +300,9 @@ fn test_cli_program_deploy_no_authority() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -389,21 +403,11 @@ fn test_cli_program_deploy_feature(enable_feature: bool, skip_preflight: bool) {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let mut genesis = TestValidatorGenesis::default();
let mut test_validator_builder = genesis
.fee_rate_governor(FeeRateGovernor::new(0, 0))
.rent(Rent {
lamports_per_byte_year: 1,
exemption_threshold: 1.0,
..Rent::default()
})
.faucet_addr(Some(faucet_addr));
let mut test_validator_builder = test_validator_genesis(mint_keypair);

// Deactivate the enable alt bn128 syscall and try to submit a program with that syscall
if !enable_feature {
test_validator_builder =
test_validator_builder.deactivate_features(&[enable_alt_bn128_syscall::id()]);
test_validator_builder.deactivate_features(&[enable_alt_bn128_syscall::id()]);
}

let test_validator = test_validator_builder
Expand Down Expand Up @@ -524,22 +528,11 @@ fn test_cli_program_upgrade_with_feature(enable_feature: bool) {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);

let mut genesis = TestValidatorGenesis::default();
let mut test_validator_builder = genesis
.fee_rate_governor(FeeRateGovernor::new(0, 0))
.rent(Rent {
lamports_per_byte_year: 1,
exemption_threshold: 1.0,
..Rent::default()
})
.faucet_addr(Some(faucet_addr));
let mut test_validator_builder = test_validator_genesis(mint_keypair);

// Deactivate the enable alt bn128 syscall and try to submit a program with that syscall
if !enable_feature {
test_validator_builder =
test_validator_builder.deactivate_features(&[enable_alt_bn128_syscall::id()]);
test_validator_builder.deactivate_features(&[enable_alt_bn128_syscall::id()]);
}

let test_validator = test_validator_builder
Expand Down Expand Up @@ -690,9 +683,9 @@ fn test_cli_program_deploy_with_authority() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -1092,9 +1085,9 @@ fn test_cli_program_upgrade_auto_extend(skip_preflight: bool) {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -1254,9 +1247,9 @@ fn test_cli_program_close_program() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -1373,9 +1366,9 @@ fn test_cli_program_extend_program() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -1549,9 +1542,9 @@ fn test_cli_program_write_buffer() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -1932,21 +1925,11 @@ fn test_cli_program_write_buffer_feature(enable_feature: bool) {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let mut genesis = TestValidatorGenesis::default();
let mut test_validator_builder = genesis
.fee_rate_governor(FeeRateGovernor::new(0, 0))
.rent(Rent {
lamports_per_byte_year: 1,
exemption_threshold: 1.0,
..Rent::default()
})
.faucet_addr(Some(faucet_addr));
let mut test_validator_builder = test_validator_genesis(mint_keypair);

// Deactivate the enable alt bn128 syscall and try to submit a program with that syscall
if !enable_feature {
test_validator_builder =
test_validator_builder.deactivate_features(&[enable_alt_bn128_syscall::id()]);
test_validator_builder.deactivate_features(&[enable_alt_bn128_syscall::id()]);
}

let test_validator = test_validator_builder
Expand Down Expand Up @@ -2036,9 +2019,9 @@ fn test_cli_program_set_buffer_authority() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -2208,9 +2191,9 @@ fn test_cli_program_mismatch_buffer_authority() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -2334,9 +2317,9 @@ fn test_cli_program_deploy_with_offline_signing(use_offline_signer_as_fee_payer:

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -2527,9 +2510,9 @@ fn test_cli_program_show() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -2724,9 +2707,9 @@ fn test_cli_program_dump() {

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let test_validator = test_validator_genesis(mint_keypair)
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
Expand Down Expand Up @@ -2864,6 +2847,7 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b
exemption_threshold: 1.0,
..Rent::default()
})
.deactivate_features(&[enable_loader_v4::id()])
.rpc_config(JsonRpcConfig {
enable_rpc_transaction_history: true,
faucet_addr: Some(faucet_addr),
Expand Down
13 changes: 12 additions & 1 deletion programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use {
solana_compute_budget::compute_budget::MAX_INSTRUCTION_STACK_DEPTH,
solana_feature_set::{
bpf_account_data_direct_mapping, enable_bpf_loader_set_authority_checked_ix,
remove_accounts_executable_flag_checks,
enable_loader_v4, remove_accounts_executable_flag_checks,
},
solana_instruction::{error::InstructionError, AccountMeta},
solana_log_collector::{ic_logger_msg, ic_msg, LogCollector},
Expand Down Expand Up @@ -562,6 +562,14 @@ fn process_loader_upgradeable_instruction(
)?;
}
UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => {
if invoke_context
.get_feature_set()
.is_active(&enable_loader_v4::id())
{
ic_logger_msg!(log_collector, "Unsupported instruction");
return Err(InstructionError::InvalidInstructionData);
}
Comment on lines +565 to +571

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late flyby review, but disabling new deployments on the old loader needs to be done through a separate feature in a future release.

We can't expect all tooling to immediately switch over to loader-v4 as soon as it's available. There needs to be some amount time where both loaders are available for new deployments while people upgrade, same as we did when the upgradeable loader was added. This could be at least 1 minor release, but 3 minor releases (~1 year) would likely be ideal.

cc @nickfrosty for his thoughts, since he's been looking at the associated SIMD solana-foundation/solana-improvement-documents#167

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. cc @Lichtso

Copy link
Author

@Lichtso Lichtso Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that is why we only disable new deployments. All existing programs can be still be redeployed etc. under loader-v3. There is no point in turning on loader-v4 if we do not deprecate loader-v3, because getting rid of loader-v3 is the entire point. Also, keep in mind that tooling can be tested against the new loader on testnet and devnet, if that is the concern.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main point is that we can't immediately break users, especially when they're typically slow to adopt new releases.

Take the anchor deploy code for example: https://github.com/coral-xyz/anchor/blob/2b0f3a7c999771f40e240b3ce421895ce6b066cf/cli/src/lib.rs#L3874 -- they're relying on calling solana program deploy.

If they wanted to avoid breaking immediately, they would need logic to check the feature gate status, conditionally call program-v4 instead of program, and force everybody to upgrade to the newest version of the CLI using that code, all before the feature gate is enabled. It isn't reasonable to expect people to make changes and force adoption of new tools so quickly.

We didn't break people immediately when loader-v3 was added, and we had a fraction of developers at the time. We can't immediately break them here. I'm happy to come up with an alternative roll out plan together.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do the feature gate checking in our CLI and alias program deploy to program-v4 deploy.

What I am saying is that the day that people start deploying to loader-v4 will not be the day it becomes available, but the day the old one stops working. And that day will be breaking users independently of whether the new loader is available before or not.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That plan still requires people to update to the newest tools immediately, which is neither reasonable nor a good developer experience.

And I'm not sure I agree with you -- for example, if we alias program to program-v4 as you suggest, then people will automatically start using loader-v4 as they adopt the new tools without realizing it.

Then, after some time, we can put out a big loud message that solana program deploy will start failing if you're using any version before 2.2, give people 3-6 months to upgrade, and then disable new deployments. That way, only people who haven't upgraded their tools in a long time will be broken, which should be a small amount of people, and we can point to the loud message that we put out months ago.

Either way, this change needs to come with a thoughtful rollout strategy.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I will add another feature gate. This however would mean there are four phases:

  1. Now: loader-v3 is active, loader-v4 is inactive
  2. v2.2: loader-v4 becomes active, loader-v3 stays active
  3. v2.3: loader-v3 deployments become inactive
  4. v2.4: All loader-v3 programs are force migrated to loader-v4

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful, thanks!

We can save it for the future, but I'll need to understand more about what "force migration" looks like, since that could potentially break tools like DAOs which rely on using loader-v3 instructions for program management. And we should start signaling this whole process as soon as possible to devrel.


instruction_context.check_number_of_instruction_accounts(4)?;
let payer_key = *transaction_context.get_key_of_account_at_index(
instruction_context.get_index_of_instruction_account_in_transaction(0)?,
Expand Down Expand Up @@ -1683,6 +1691,9 @@ mod tests {
expected_result,
Entrypoint::vm,
|invoke_context| {
let mut feature_set = invoke_context.get_feature_set().clone();
feature_set.deactivate(&enable_loader_v4::id());
invoke_context.mock_set_feature_set(Arc::new(feature_set));
test_utils::load_all_invoked_programs(invoke_context);
},
|_invoke_context| {},
Expand Down
6 changes: 3 additions & 3 deletions programs/sbf/benches/bpf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use {
bank::Bank,
bank_client::BankClient,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
loader_utils::{load_program_from_file, load_upgradeable_program_and_advance_slot},
loader_utils::{load_program_from_file, load_program_of_loader_v4},
},
solana_sbpf::{
ebpf::MM_INPUT_START, elf::Executable, memory_region::MemoryRegion,
Expand Down Expand Up @@ -201,9 +201,9 @@ fn bench_program_execute_noop(bencher: &mut Bencher) {
let authority_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();

let (_, invoke_program_id) = load_upgradeable_program_and_advance_slot(
let (_bank, invoke_program_id) = load_program_of_loader_v4(
&mut bank_client,
bank_forks.as_ref(),
&bank_forks,
&mint_keypair,
&authority_keypair,
"noop",
Expand Down
11 changes: 5 additions & 6 deletions programs/sbf/c/src/invoked/invoked.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@ extern uint64_t entrypoint(const uint8_t *input) {
static const int INVOKED_PROGRAM_DUP_INDEX = 3;
sol_assert(sol_deserialize(input, &params, 4));

SolPubkey sbf_loader_upgradeable_id =
(SolPubkey){.x = {
2, 168, 246, 145, 78, 136, 161, 176, 226, 16, 21, 62,
247, 99, 174, 43, 0, 194, 185, 61, 22, 193, 36, 210, 192,
83, 122, 16, 4, 128, 0, 0}};
SolPubkey loader_v4_id =
(SolPubkey){.x = {
5, 18, 180, 17, 81, 81, 227, 122, 173, 10, 139, 197, 211, 136, 46, 123, 127, 218, 76, 243, 210, 192, 40, 200, 207, 131, 54, 24, 0, 0, 0, 0
}};

for (int i = 0; i < params.data_len; i++) {
sol_assert(params.data[i] == i);
Expand Down Expand Up @@ -64,7 +63,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_assert(
SolPubkey_same(accounts[INVOKED_PROGRAM_INDEX].key, params.program_id))
sol_assert(SolPubkey_same(accounts[INVOKED_PROGRAM_INDEX].owner,
&sbf_loader_upgradeable_id));
&loader_v4_id));
sol_assert(!accounts[INVOKED_PROGRAM_INDEX].is_signer);
sol_assert(!accounts[INVOKED_PROGRAM_INDEX].is_writable);
sol_assert(accounts[INVOKED_PROGRAM_INDEX].rent_epoch == UINT64_MAX);
Expand Down
4 changes: 1 addition & 3 deletions programs/sbf/c/src/read_program/read_program.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ extern uint64_t entrypoint(const uint8_t *input) {
return ERROR_INVALID_ARGUMENT;
}

char ka_data[] = {0x02, 0x00, 0x00, 0x00};

sol_assert(params.ka_num == 1);
sol_assert(!sol_memcmp(params.ka[0].data, ka_data, 4));
sol_assert(!sol_memcmp(params.ka[0].data, params.data, params.data_len));
sol_assert(params.ka[0].is_signer == false);
sol_assert(params.ka[0].is_writable == false);
sol_assert(params.ka[0].executable == true);
Expand Down
7 changes: 2 additions & 5 deletions programs/sbf/rust/invoked/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use {
solana_program::{
account_info::AccountInfo,
bpf_loader_upgradeable,
entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE},
loader_v4,
log::sol_log_64,
msg,
program::{get_return_data, invoke, invoke_signed, set_return_data},
Expand Down Expand Up @@ -69,10 +69,7 @@ fn process_instruction(
assert!(!accounts[INVOKED_ARGUMENT_INDEX].executable);

assert_eq!(accounts[INVOKED_PROGRAM_INDEX].key, program_id);
assert_eq!(
accounts[INVOKED_PROGRAM_INDEX].owner,
&bpf_loader_upgradeable::id()
);
assert_eq!(accounts[INVOKED_PROGRAM_INDEX].owner, &loader_v4::id());
assert!(!accounts[INVOKED_PROGRAM_INDEX].is_signer);
assert!(!accounts[INVOKED_PROGRAM_INDEX].is_writable);
assert_eq!(accounts[INVOKED_PROGRAM_INDEX].rent_epoch, u64::MAX);
Expand Down
Loading
Loading