mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-08-26 20:01:16 +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>
166 lines
5.7 KiB
Rust
166 lines
5.7 KiB
Rust
use borsh::{BorshDeserialize, BorshSerialize};
|
|
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
Nullifier,
|
|
account::{Account, AccountId},
|
|
};
|
|
|
|
/// A commitment to all zero data.
|
|
/// ```python
|
|
/// from hashlib import sha256
|
|
/// prefix = b"/LEE/v0.3/Commitment/\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
/// hasher = sha256()
|
|
/// hasher.update(prefix + bytes([0] * 32 + [0] * 32 + [0] * 16 + [0] * 16 + list(sha256().digest())))
|
|
/// DUMMY_COMMITMENT = hasher.digest()
|
|
/// ```
|
|
pub const DUMMY_COMMITMENT: Commitment = Commitment([
|
|
55, 228, 215, 207, 112, 221, 239, 49, 238, 79, 71, 135, 155, 15, 184, 45, 104, 74, 51, 211,
|
|
238, 42, 160, 243, 15, 124, 253, 62, 3, 229, 90, 27,
|
|
]);
|
|
|
|
/// The hash of the dummy commitment.
|
|
/// ```python
|
|
/// from hashlib import sha256
|
|
/// hasher = sha256()
|
|
/// hasher.update(DUMMY_COMMITMENT)
|
|
/// DUMMY_COMMITMENT_HASH = hasher.digest()
|
|
/// ```
|
|
pub const DUMMY_COMMITMENT_HASH: [u8; 32] = [
|
|
250, 237, 192, 113, 155, 101, 119, 30, 235, 183, 20, 84, 26, 32, 196, 229, 154, 74, 254, 249,
|
|
129, 241, 118, 39, 41, 253, 141, 171, 184, 71, 8, 41,
|
|
];
|
|
|
|
#[derive(Copy, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
|
#[cfg_attr(
|
|
any(feature = "host", test),
|
|
derive(Default, PartialEq, Eq, Hash, PartialOrd, Ord)
|
|
)]
|
|
pub struct Commitment(pub(super) [u8; 32]);
|
|
|
|
#[cfg(any(feature = "host", test))]
|
|
impl std::fmt::Debug for Commitment {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
use std::fmt::Write as _;
|
|
|
|
let hex: String = self.0.iter().fold(String::new(), |mut acc, b| {
|
|
write!(acc, "{b:02x}").expect("writing to string should not fail");
|
|
acc
|
|
});
|
|
write!(f, "Commitment({hex})")
|
|
}
|
|
}
|
|
|
|
impl Commitment {
|
|
/// Generates the commitment to a private account owned by user for `account_id`:
|
|
/// SHA256( `Comm_DS` || `account_id` || `program_owner` || balance || nonce || SHA256(data)).
|
|
// TODO: Accept account_id by value as it's Copy
|
|
#[must_use]
|
|
pub fn new(account_id: &AccountId, account: &Account) -> Self {
|
|
const COMMITMENT_PREFIX: &[u8; 32] =
|
|
b"/LEE/v0.3/Commitment/\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
|
|
|
let mut bytes = Vec::new();
|
|
bytes.extend_from_slice(COMMITMENT_PREFIX);
|
|
bytes.extend_from_slice(account_id.value());
|
|
let account_bytes_with_hashed_data = {
|
|
let mut this = Vec::new();
|
|
this.extend_from_slice(account.program_owner.as_ref());
|
|
this.extend_from_slice(&account.balance.to_le_bytes());
|
|
this.extend_from_slice(&account.nonce.0.to_le_bytes());
|
|
let hashed_data: [u8; 32] = Impl::hash_bytes(&account.data)
|
|
.as_bytes()
|
|
.try_into()
|
|
.unwrap();
|
|
this.extend_from_slice(&hashed_data);
|
|
this
|
|
};
|
|
bytes.extend_from_slice(&account_bytes_with_hashed_data);
|
|
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn for_dummy(nullifier: &Nullifier, commitment_seed: &[u8; 32]) -> Self {
|
|
const DUMMY_PREFIX: &[u8; 32] = b"/LEE/v0.3/Commitment/Dummy/\x00\x00\x00\x00\x00";
|
|
let mut bytes = DUMMY_PREFIX.to_vec();
|
|
bytes.extend_from_slice(&nullifier.0);
|
|
bytes.extend_from_slice(commitment_seed);
|
|
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
|
}
|
|
}
|
|
|
|
pub type CommitmentSetDigest = [u8; 32];
|
|
|
|
pub type MembershipProof = (usize, Vec<[u8; 32]>);
|
|
|
|
/// Computes the resulting digest for the given membership proof and corresponding commitment.
|
|
#[must_use]
|
|
pub fn compute_digest_for_path(
|
|
commitment: &Commitment,
|
|
proof: &MembershipProof,
|
|
) -> CommitmentSetDigest {
|
|
let value_bytes = commitment.to_byte_array();
|
|
let mut result: [u8; 32] = Impl::hash_bytes(&value_bytes)
|
|
.as_bytes()
|
|
.try_into()
|
|
.unwrap();
|
|
let mut level_index = proof.0;
|
|
for node in &proof.1 {
|
|
let mut bytes = [0_u8; 64];
|
|
let is_left_child = level_index & 1 == 0;
|
|
if is_left_child {
|
|
bytes[..32].copy_from_slice(&result);
|
|
bytes[32..].copy_from_slice(node);
|
|
} else {
|
|
bytes[..32].copy_from_slice(node);
|
|
bytes[32..].copy_from_slice(&result);
|
|
}
|
|
result = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap();
|
|
level_index >>= 1;
|
|
}
|
|
result
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
|
|
|
use crate::{
|
|
Commitment, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, Nullifier,
|
|
account::{Account, AccountId},
|
|
};
|
|
|
|
#[test]
|
|
fn nothing_up_my_sleeve_dummy_commitment() {
|
|
let default_account = Account::default();
|
|
let account_id_null = AccountId::new([0; 32]);
|
|
let expected_dummy_commitment = Commitment::new(&account_id_null, &default_account);
|
|
assert_eq!(DUMMY_COMMITMENT, expected_dummy_commitment);
|
|
}
|
|
|
|
#[test]
|
|
fn nothing_up_my_sleeve_dummy_commitment_hash() {
|
|
let expected_dummy_commitment_hash: [u8; 32] =
|
|
Impl::hash_bytes(&DUMMY_COMMITMENT.to_byte_array())
|
|
.as_bytes()
|
|
.try_into()
|
|
.unwrap();
|
|
assert_eq!(DUMMY_COMMITMENT_HASH, expected_dummy_commitment_hash);
|
|
}
|
|
|
|
#[test]
|
|
fn for_dummy_matches_pinned_value() {
|
|
let nullifier = Nullifier::for_dummy(&[0; 32]);
|
|
let commitment_seed = [1; 32];
|
|
let expected_commitment = Commitment([
|
|
106, 88, 233, 248, 28, 251, 254, 48, 62, 53, 61, 248, 25, 148, 223, 133, 108, 213, 184,
|
|
83, 73, 145, 122, 104, 89, 220, 111, 132, 40, 87, 12, 105,
|
|
]);
|
|
assert_eq!(
|
|
Commitment::for_dummy(&nullifier, &commitment_seed),
|
|
expected_commitment
|
|
);
|
|
}
|
|
}
|