mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-08-25 11:21:12 +00:00
* feat(lee): store deployed programs as Account-shaped state, keyed by AccountId Program-as-Account migration, first slice: V03State.programs becomes HashMap<AccountId, Account> instead of HashMap<ProgramId, Program>, with the elf held directly in Account.data. The map key is derived from ProgramId via a new 1:1 From<ProgramId> for AccountId conversion (both types are exactly 32 bytes) rather than a hash, since ProgramId is already content-derived from the elf. Account.program_owner stays ProgramId-typed everywhere - this only changes how deployed programs are stored and looked up host-side, not the dispatch/authorization model any guest program logic depends on. Dispatch resolves a ChainedCall's program_id by converting to AccountId, fetching the Account, and reconstructing a Program via new_unchecked for execution. DATA_MAX_LENGTH is raised from 100 KiB to 700 KiB to fit real program elfs (observed 375 KB-631 KB) directly in Account.data; noted in its docstring as a rough placeholder pending real transaction/block-size budget analysis. * fix(lee): store deployed programs as Account-shaped state, correct SeenShard cap Corrects lee/state_machine internals for the Program-as-Account migration and fixes SeenShard::MAX_DELIVERIES, which was still calibrated for the old 100 KiB DATA_MAX_LENGTH instead of the current 700 KiB cap. Rebuilds program artifacts and the sequencer test fixture to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * address PR #720 review nits - Use FIXME instead of TODO for the temporary ProgramId->AccountId conversion, per review convention for patches guaranteed to be fixed later. - Derive cross_zone_inbox's MAX_DELIVERIES from DATA_MAX_LENGTH instead of a hand-recomputed literal, so it stays in sync automatically the next time the cap changes. * feat(lee): migrate Account.program_owner from ProgramId to AccountId Account.program_owner is now AccountId-typed instead of ProgramId, via a new bijective From<ProgramId> for AccountId / From<AccountId> for ProgramId conversion pair (pure byte reinterpretation, not a hash - both types are exactly 32 bytes). Adds DEFAULT_PROGRAM_OWNER as the AccountId-typed counterpart to DEFAULT_PROGRAM_ID, used at every program_owner comparison/claim site instead of an inline AccountId::default(). Touches every call site across lee_core, lee (including the guest-side privacy-preserving circuit), all 16 deployed guest programs, wallet/wallet-ffi, indexer_ffi/indexer_service/ indexer_service_protocol, sequencer_core, testnet_initial_state, system_accounts, cross_zone, storage, cycle_bench, and integration_tests - mostly mechanical .into() conversions, plus two simplifications: wallet's manual base58 encode/decode of program_owner was dead code once it's AccountId (which already has Display/FromStr), and the FFI crates' program_owner field now reuses the existing generic FfiBytes32 wrapper instead of the now-unused FfiProgramId one. Rebuilds every guest ELF artifact and the prebuilt sequencer test fixture via just build-artifacts, since execute_and_prove runs against the checked-in precompiled privacy_preserving_circuit.bin, which isn't rebuilt automatically by cargo test/check. * chore(lee): rebuild artifacts after rebase, drop unused base58 dep Rebases marvin/program-as-account-2 onto the updated marvin/program-as-account (SeenShard cap fix), regenerating program and circuit artifacts plus the sequencer test fixture to match. Also removes lez/wallet's now-unused base58 dependency, dead since AccountId gained its own Display/FromStr base58 encoding. * docs(lee): trim DEFAULT_PROGRAM_OWNER and From<AccountId> for ProgramId docs * test(lee): add known-answer tests for ProgramId/AccountId conversion, rebuild artifacts * fix(lee): apply program_owner AccountId migration to code added after rebase dev grew new program_owner call sites (sequencer_stake genesis/config handling, committee_discovery, a new selective_pda_delegator test program, and related tests) after this branch's ProgramId->AccountId migration commit was originally written, so they predated the .into() sweep and didn't conflict during the rebase - they just still assumed the old ProgramId-typed field. Converts all of them, fixes a stray unseparated hex literal clippy caught along the way, and rebuilds artifacts against the fixed source. * chore(lee): regenerate test fixture after rebasing onto dev --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
105 lines
3.7 KiB
Rust
105 lines
3.7 KiB
Rust
#![expect(
|
|
clippy::tests_outside_test_module,
|
|
reason = "We don't care about these in tests"
|
|
)]
|
|
|
|
use std::{io::Write as _, time::Duration};
|
|
|
|
use anyhow::Result;
|
|
use common::transaction::LeeTransaction;
|
|
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, get_account, new_account};
|
|
use sequencer_service_rpc::RpcClient as _;
|
|
use test_fixtures::{
|
|
MultiZoneTestContextBuilder, ZoneTestContextBuilder, config::MultiNodeTestContextConfig,
|
|
};
|
|
use tokio::test;
|
|
use wallet::{cli::Command, config::WalletConfigOverrides};
|
|
|
|
#[test]
|
|
async fn deploy_and_execute_program() -> Result<()> {
|
|
let mut ctx = TestContext::new().await?;
|
|
|
|
let claimer = test_programs::claimer();
|
|
let mut tempfile = tempfile::NamedTempFile::new()?;
|
|
tempfile.write_all(claimer.elf())?;
|
|
|
|
let binary_filepath = tempfile.path().to_owned();
|
|
|
|
let command = Command::DeployProgram {
|
|
binary_filepath: binary_filepath.clone(),
|
|
};
|
|
|
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
|
|
|
let account_id = new_account(&mut ctx, false, None).await?;
|
|
|
|
let nonces = ctx.wallet_mut().get_accounts_nonces(&[account_id]).await?;
|
|
let private_key = ctx
|
|
.wallet()
|
|
.get_account_public_signing_key(account_id)
|
|
.unwrap();
|
|
let message =
|
|
lee::public_transaction::Message::try_new(claimer.id(), vec![account_id], nonces, ())?;
|
|
let witness_set = lee::public_transaction::WitnessSet::for_message(&message, &[private_key]);
|
|
let transaction = lee::PublicTransaction::new(message, witness_set);
|
|
let _response = ctx
|
|
.sequencer_client()
|
|
.send_transaction(LeeTransaction::Public(transaction))
|
|
.await?;
|
|
|
|
log::info!("Waiting for next block creation");
|
|
// Waiting for long time as it may take some time for such a big transaction to be included in a
|
|
// block
|
|
tokio::time::sleep(Duration::from_secs(2 * TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
|
|
|
let post_state_account = get_account(&ctx, account_id).await?;
|
|
|
|
let expected_data: &[u8] = &[];
|
|
assert_eq!(post_state_account.program_owner, claimer.id().into());
|
|
assert_eq!(post_state_account.balance, 0);
|
|
assert_eq!(post_state_account.data.as_ref(), expected_data);
|
|
assert_eq!(post_state_account.nonce.0, 1);
|
|
|
|
log::info!("Successfully deployed and executed program");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
async fn deploy_invalid_program_fails() -> Result<()> {
|
|
// An invalid program bytecode is rejected by the sequencer during block production, so the
|
|
// deployment transaction is never included in a block. Shrink the wallet's polling window so
|
|
// the command gives up quickly instead of waiting for the full default timeout.
|
|
|
|
let mut ctx = MultiZoneTestContextBuilder::default()
|
|
.with_zone(
|
|
ZoneTestContextBuilder::new(MultiNodeTestContextConfig::default())
|
|
.with_wallet_config_overrides(WalletConfigOverrides {
|
|
seq_poll_timeout: Some(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)),
|
|
seq_tx_poll_max_blocks: Some(5),
|
|
seq_poll_max_retries: Some(2),
|
|
..WalletConfigOverrides::default()
|
|
}),
|
|
)
|
|
.build()
|
|
.await?;
|
|
|
|
let mut tempfile = tempfile::NamedTempFile::new()?;
|
|
tempfile.write_all(b"this is not a valid program binary")?;
|
|
|
|
let command = Command::DeployProgram {
|
|
binary_filepath: tempfile.path().to_owned(),
|
|
};
|
|
|
|
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await;
|
|
|
|
assert!(
|
|
result.is_err(),
|
|
"Deploying an invalid program should fail, but got: {result:?}"
|
|
);
|
|
|
|
log::info!("Deploying an invalid program failed as expected");
|
|
|
|
Ok(())
|
|
}
|