mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-06-29 10:29:32 +00:00
feat(cross-zone): seed bridge-lock holdings into indexer genesis for consistency
This commit is contained in:
parent
ff7610804a
commit
c9c999e0de
@ -13,6 +13,7 @@ logos-blockchain-zone-sdk.workspace = true
|
||||
lee.workspace = true
|
||||
lee_core.workspace = true
|
||||
cross_zone_inbox_core = { workspace = true, features = ["host"] }
|
||||
bridge_lock_core = { workspace = true, features = ["host"] }
|
||||
ping_core.workspace = true
|
||||
storage.workspace = true
|
||||
testnet_initial_state.workspace = true
|
||||
|
||||
@ -22,11 +22,12 @@ pub struct IndexerStore {
|
||||
impl IndexerStore {
|
||||
/// Starting database at the start of new chain.
|
||||
/// Creates files if necessary.
|
||||
pub fn open_db(location: &Path, genesis_seed: Option<(AccountId, Account)>) -> Result<Self> {
|
||||
pub fn open_db(location: &Path, genesis_seed: Vec<(AccountId, Account)>) -> Result<Self> {
|
||||
let mut initial_state = testnet_initial_state::initial_state();
|
||||
// Seed any zone-specific genesis accounts (e.g. the cross-zone inbox
|
||||
// config) so the indexer's replayed state matches the sequencer's.
|
||||
if let Some((account_id, account)) = genesis_seed {
|
||||
// Seed any zone-specific genesis accounts (the cross-zone inbox config and
|
||||
// bridge-lock holdings) so the indexer's replayed state matches the
|
||||
// sequencer's; none are produced by a transaction.
|
||||
for (account_id, account) in genesis_seed {
|
||||
initial_state.insert_genesis_account(account_id, account);
|
||||
}
|
||||
let dbio = RocksDBIO::open_or_create(location, &initial_state)?;
|
||||
@ -220,18 +221,37 @@ mod tests {
|
||||
fn correct_startup() {
|
||||
let home = tempdir().unwrap();
|
||||
|
||||
let storage = IndexerStore::open_db(home.as_ref(), None).unwrap();
|
||||
let storage = IndexerStore::open_db(home.as_ref(), Vec::new()).unwrap();
|
||||
|
||||
let final_id = storage.get_last_block_id().unwrap();
|
||||
|
||||
assert_eq!(final_id, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn seeds_bridge_lock_holding_into_genesis_state() {
|
||||
// The holding is force-inserted, not produced by a transaction, so the
|
||||
// indexer must seed it to match the sequencer. Use the same builder both
|
||||
// sides use, so this also guards against the two drifting.
|
||||
let home = tempdir().unwrap();
|
||||
let holder = AccountId::new([5; 32]);
|
||||
let (id, account) = bridge_lock_core::build_holding_account(holder, 42);
|
||||
|
||||
let storage = IndexerStore::open_db(home.as_ref(), vec![(id, account)]).unwrap();
|
||||
|
||||
let seeded = storage.account_current_state(&holder).await.unwrap();
|
||||
assert_eq!(
|
||||
bridge_lock_core::read_balance(&seeded.data.into_inner()),
|
||||
42,
|
||||
"indexer genesis must hold the seeded bridge-lock balance"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn state_transition() {
|
||||
let home = tempdir().unwrap();
|
||||
|
||||
let storage = IndexerStore::open_db(home.as_ref(), None).unwrap();
|
||||
let storage = IndexerStore::open_db(home.as_ref(), Vec::new()).unwrap();
|
||||
|
||||
let initial_accounts = initial_pub_accounts_private_keys();
|
||||
let from = initial_accounts[0].account_id;
|
||||
@ -283,7 +303,7 @@ mod tests {
|
||||
async fn account_state_at_block() {
|
||||
let home = tempdir().unwrap();
|
||||
|
||||
let storage = IndexerStore::open_db(home.as_ref(), None).unwrap();
|
||||
let storage = IndexerStore::open_db(home.as_ref(), Vec::new()).unwrap();
|
||||
|
||||
let mut prev_hash = None;
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ use anyhow::{Context as _, Result};
|
||||
use common::config::BasicAuth;
|
||||
use cross_zone_inbox_core::CrossZoneConfig;
|
||||
use humantime_serde;
|
||||
use lee::AccountId;
|
||||
pub use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
@ -31,6 +32,19 @@ pub struct IndexerConfig {
|
||||
/// Cross-zone configuration. `None` disables the indexer's cross-zone handling.
|
||||
#[serde(default)]
|
||||
pub cross_zone: Option<CrossZoneConfig>,
|
||||
/// Bridge-lock holdings to seed into genesis, mirroring the sequencer's
|
||||
/// `SupplyBridgeLockHolding` actions. They are not produced by any
|
||||
/// transaction, so the indexer must seed them to match the sequencer's state.
|
||||
#[serde(default)]
|
||||
pub bridge_lock_holdings: Vec<BridgeLockHolding>,
|
||||
}
|
||||
|
||||
/// A genesis-funded bridge-lock holder balance, configured identically on the
|
||||
/// sequencer (via `SupplyBridgeLockHolding`) and the indexer.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BridgeLockHolding {
|
||||
pub holder: AccountId,
|
||||
pub amount: u128,
|
||||
}
|
||||
|
||||
impl IndexerConfig {
|
||||
|
||||
@ -37,12 +37,23 @@ impl IndexerCore {
|
||||
);
|
||||
let zone_indexer = ZoneIndexer::new(config.channel_id, node);
|
||||
|
||||
// Seed the inbox config so the indexer can replay cross-zone dispatch
|
||||
// transactions, matching the account the sequencer seeds at genesis.
|
||||
let inbox_config_seed = config.cross_zone.as_ref().map(|cross_zone| {
|
||||
// Genesis accounts the indexer must seed to match the sequencer's state,
|
||||
// since none are produced by a transaction: the cross-zone inbox config
|
||||
// and any bridge-lock holdings. Both go through the same builders the
|
||||
// sequencer uses, so the states are byte-identical.
|
||||
let mut genesis_seed = Vec::new();
|
||||
if let Some(cross_zone) = config.cross_zone.as_ref() {
|
||||
let self_zone: [u8; 32] = *config.channel_id.as_ref();
|
||||
cross_zone_inbox_core::build_inbox_config_account(self_zone, cross_zone)
|
||||
});
|
||||
genesis_seed.push(cross_zone_inbox_core::build_inbox_config_account(
|
||||
self_zone, cross_zone,
|
||||
));
|
||||
}
|
||||
for holding in &config.bridge_lock_holdings {
|
||||
genesis_seed.push(bridge_lock_core::build_holding_account(
|
||||
holding.holder,
|
||||
holding.amount,
|
||||
));
|
||||
}
|
||||
|
||||
// Option B verifier: re-derives each cross-zone dispatch from the peer's
|
||||
// finalized blocks. `None` when cross-zone messaging is disabled.
|
||||
@ -51,7 +62,7 @@ impl IndexerCore {
|
||||
Ok(Self {
|
||||
zone_indexer: Arc::new(zone_indexer),
|
||||
config,
|
||||
store: IndexerStore::open_db(&home, inbox_config_seed)?,
|
||||
store: IndexerStore::open_db(&home, genesis_seed)?,
|
||||
verifier,
|
||||
})
|
||||
}
|
||||
|
||||
@ -182,6 +182,7 @@ pub fn indexer_config(
|
||||
},
|
||||
channel_id,
|
||||
cross_zone,
|
||||
bridge_lock_holdings: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user