Compare commits

...
Sign in to create a new pull request.

12 commits
master ... ido

Author SHA1 Message Date
Hossein Zoda
0eb86131f4 ido: keep offeredTokenTotalSupply in Active parameters
IdoActiveParameters dropped offeredTokenTotalSupply, so clients couldn't compute the offered-vs-total-supply share for live (non-PREINIT) IDOs. Carry it forward from the preinit parameters at the PreInit->Active transition and add it to the struct. A serde default of -1 (the pre-existing-token "unknown supply" sentinel) lets ido_state rows written before this field existed still deserialize.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 01:01:55 +03:00
Hossein Zoda
a73dff52df update offering initial output 2026-07-17 03:40:30 +03:00
Hossein Zoda
da26864cba update ipfs rebuild contract 2026-07-16 21:23:47 +03:00
Hossein Zoda
c1952142de ido: categories in display/UI byte order, drop hardcoded PLP limits
libriften moved encodeVmTokenCategory/decodeVmTokenCategory (which reverse
the 32 category bytes, display<->VM) into the ido announcement and ORB
IdoParams serialize/deserialize layer. Net wire effects the indexer sees:
- preinit announcement delphiCategory[44..76] and xTokenCategory[126..158]
  are now VM/little-endian (were display order);
- the IdoParams commitment stays VM on-wire, but the derived on-chain
  postlaunch orbPoolParamsCategory push flips display->VM (decode now
  returns display, the unchanged bake re-wraps it in encodeVmTokenCategory).

Keep the three categories (delphiCategory, xTokenCategory,
orbPoolParamsCategory) in display/UI byte order in-memory and over RPC:
reverse at every parse site (announcement + IdoParams NFT commitment) and
restore the .rev() on the output#0 delphi-NFT to_blob() compare. The
build_*_bytecode helpers keep reversing display->VM when baking, so the
output#7 P2SH32 rebuild matches the new on-chain layout.

Also drop the now-unused hardcoded MAX_PLP_SHARE / MAX_PLP_ANNUAL_DISCOUNT
/ MIN_PLP_ANNUAL_DISCOUNT statics and dead checks; the min-PLP limits are
NFT-pinned (minPlpShare / minPlpAfterDiscount) and the discount/max-discount
checks now just guard against negatives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 21:44:18 +00:00
Hossein Zoda
4677cc955e ido: port "CldIdo00" signature + announcement permanentLiquidityMinFee
Sync the IDO indexer to libriften 48aa1f7 (+ follow-ups):

- IDO_SIGNATURE 20->10 bytes: [0x08 "CldIdo00" 0x75] (was OP_PUSH18
  "CauldronIdo-2026Q2"). The separate "CauldronIdo0" announcement
  signature is folded into the same 8 raw bytes, so
  IDO_PREINIT_ANNOUNCEMENT_SIGNATURE = 6a 4c b9 + "CldIdo00";
  is_preinit_broadcast is now length-agnostic. Electrum mempool
  filters track automatically (they reference the constants).
- Recompile the 3 affected contracts from templates: IDO initiator
  233->231, IDO preinit 1469->1465, offering launcher 630->628.
  IDO_POSTLAUNCH_CONTRACT and the other offering contracts are
  byte-identical and left as-is.
- Announcement OP_RETURN carries permanentLiquidityMinFee as a 2-byte
  field at data[166..168]; mainData is now 185 bytes. Read minFee from
  the announcement rather than the (unreadable) postlaunch copy.

Fix the source of the two "free" postlaunch params:

- Output #7 (ido initiator bytecode storage) is P2SH32, so the partial
  bytecode cannot be read back from it. Drop the impossible extraction
  (extract_params_from_partial_ido_initiator_bytecode +
  PartialPostlaunchExtractedParams). extract_data_from_storage_script_
  with_data_and_size is kept for its valid callers (revealed redeem
  scripts, bare bcmr storages).
- orbPoolParamsCategory now comes from the ORB IdoParams NFT commitment
  (output #10). The commitment parse is moved above the params
  construction so the value is available; the now-tautological
  orbPoolParamsCategory vs NFT check is removed.
- Verification is the existing output #7 rebuild: build the expected
  bytecode from the announcement values plus the NFT-sourced
  orbPoolParamsCategory, hash to P2SH32, and compare. Since minFee is a
  2-byte announcement field it always fits the postlaunch's 2-byte push,
  so build_partial_postlaunch_bytecode stays infallible.

- Tests: preinit_detect -> legacy_preinit_announcement_not_detected
  (legacy fixture must no longer be detected under the new signature);
  replace the extraction round-trip test with a structural check of the
  partial postlaunch bytecode layout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 19:32:20 +00:00
Hossein Zoda
c8450e210b ido: sync IdoParams commitment to 127-byte layout, enforce NFT-pinned min PLP
libriften 0941c88 grew the ORB IdoParams NFT commitment 121->127 bytes,
inserting minPlpAfterDiscount/minPlpShare (4-byte pIntLE numerators over
PERMANENT_LIQUIDITY_SHARE_DENOMINATOR) between maxExpireDuration and
entryExecutionFee, and shrinking paramsUseFee 4->2 bytes.

- Parse the two new fields; shift entryExecutionFee/createExecutionFee offsets.
- Drop the hardcoded MIN_PLP_SHARE / MIN_PLP_AFTER_DISCOUNT statics; enforce
  both minimums from the NFT commitment inside the version-verified block
  (permanentLiquidityShareNumerator >= minPlpShare, and
  maxDiscountRateNumerator + minPlpAfterDiscount <= permanentLiquidityShareNumerator).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 11:22:41 +00:00
Hossein Zoda
ca7f829591 ido: resync postlaunch contract & enforce IdoParams NFT version
- update IDO_POSTLAUNCH_CONTRACT to the recompiled 1627-byte script
  (libriften cd58dd1): postlaunch.cash reordered the permanent-pool
  sibling config, moving the all-zero nftOwner to the end. The indexer
  byte-compares this body against on-chain output#7, so the stale
  1669-byte constant would have marked every new IDO invalid.
- set CHIPNET_IDO_PARAMS_NFT_CATEGORY (was an all-zero placeholder).
- validate the ORB IdoParams NFT commitment version byte: only index
  when it equals IDO_PARAMS_VERSION (0x00), else push an
  "incompatible with the indexer" reason and mark the IDO invalid.
  The other commitment fields are read at v0 offsets, so the parameter
  comparisons are skipped on a version mismatch to avoid spurious errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 01:51:56 +00:00
Hossein Zoda
c3f3c269e2 bcmr: index new registrations from mempool, no confirmation needed
Add an OP_RETURN "BCMR" prefix filter to the rostrum mempool.get pass and
index matching txs in update_mempool with the all-zeros sentinel blockhash
(same pattern as oracle). A newly registered BCMR becomes the auth head and
is downloaded immediately; the confirming block re-stamps the entry with
its real blockhash in place.

- insert_authheader: INSERT OR REPLACE -> upsert. REPLACE deletes the row,
  cascading away downloaded bcmr_data on every mempool->confirmed upgrade.
- update_mempool drops sentinel entries whose tx left the mempool (evicted
  or replaced), so stale auth heads never linger.
- clear bcmr mempool state at startup; always-run migration adds txid and
  blockhash indexes on auth_chain_entry for the per-pass lookups.

Auth chain transfers without a BCMR output still index at confirmation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:59:46 +00:00
Hossein Zoda
2bcf35172c ido: postlaunch collect/run lifecycle + bare-p2s locks, no backward compatibility
Sync with libriften ido+ttd head (37a5c01):

- postlaunch rework: collect copies the offering confirmation NFT through
  io#4 (the chain continues from the verified copy at output#4, state
  unchanged); run consumes it at input#5. The called method is dispatched
  from the carrier's unlocking bytecode (<args..> <functionIndex> <redeem>);
  run's altPPOut argument is decoded as a VM number — when true the pool
  legs were paid back to the collector (pool-deploy-failure fallback,
  recorded as altPPOutPayout) instead of deploying the permanent pool. The
  accumulated BCH pot paid to the platform p2nfth at output#5 is recorded
  as platformBchPayout. Chain-follow probe list extended to [0,1,3,4,5].
- postlaunch constructor gains a 4-byte executionFee push (partial
  postlaunch bytecode is now 8 pushes + body).
- p2nfth and plain storages lock as bare-p2s (blob storages stay p2sh32);
  nfthash preimage flipped to hash256(commitment + category);
  P2NFTH contract is 78cf7bce7eaa87.
- all changed contract constants regenerated from the libriften templates
  (initiator 233 B, postlaunch 1669 B, preinit 1469 B).
- fix IdoActiveParameters construction missing orbPoolParamsCategory and
  permanentLiquidityMinFee; box IdoUpdate::State to keep the enum small.
- ido db version 3; no migration — delete ido.db and re-index.
- legacy-generation fixture tests marked #[ignore].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 20:19:43 +00:00
Hossein Zoda
a5f4ce40d4 ido: port to ORB IdoParams generation, no backward compatibility
Preinit txs must now spend an ORB IdoParams NFT (use() at input #1,
preserved at output #10) whose per-network category is pinned and whose
121-byte commitment is cross-checked field-by-field against the
announced parameters; xToken must be a non-native token. ido.db is
version-gated (PRAGMA user_version = 2); legacy IDOs do not parse.

Also refresh the embedded IDOPostLaunch contract to the current
tokentoken-delegation build (1616 bytes, libriften ido+ttd a9fcfa0);
the previous copy predated the recompile and would have failed the
preinit output #7 byte-compare. All other embedded contracts verified
byte-identical to the libriften templates.

Per-network IdoParams NFT categories are still all-zero placeholders;
until set, every preinit indexes as is_valid=false.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 07:32:37 +00:00
Hossein Zoda
1ddab462aa ido minor fixes 2026-06-26 10:52:40 +03:00
Hossein Zoda
d363c2c930 ido followup update, offering contract updated & set ido create exe fee to 10m sats 2026-06-25 17:20:31 +03:00
6 changed files with 1135 additions and 380 deletions

View file

@ -7,6 +7,7 @@ use crate::bcmr::parsedbcmr::{FileMeta, ParsedBCMR, Token, Uris, SOURCE_ON_CHAIN
use crate::db::blob::{display_hex_to_blob, FromBlob, ToBlob};
use anyhow::*;
use bitcoin_hashes::Hash;
use bitcoincash::{BlockHash, TokenID, Txid};
use log::info;
use riftenlabs_defi::chainutil::OutPointHash;
@ -116,6 +117,23 @@ pub async fn prepare_tables(pool: &SqlitePool) {
.execute(pool)
.await
.expect("failed to create index");
ensure_indexes(pool).await;
}
/// Always-run migration: indexes for the txid and blockhash lookups used by
/// mempool indexing. Safe on both new and existing bcmr.db.
pub async fn ensure_indexes(pool: &SqlitePool) {
sqlx::query("CREATE INDEX IF NOT EXISTS idx_auth_chain_txid ON auth_chain_entry(txid)")
.execute(pool)
.await
.expect("failed to create index");
sqlx::query(
"CREATE INDEX IF NOT EXISTS idx_auth_chain_blockhash ON auth_chain_entry(blockhash)",
)
.execute(pool)
.await
.expect("failed to create index");
}
/// Delete all well-known BCMR entries for the given source.
@ -131,6 +149,51 @@ pub async fn delete_entries_for_well_known<'e>(
Ok(())
}
/// Whether any auth chain entry (confirmed or mempool) was indexed from this tx.
pub async fn has_indexed_tx(pool: &SqlitePool, txid: &Txid) -> Result<bool> {
let row: Option<(i64,)> =
sqlx::query_as("SELECT 1 FROM auth_chain_entry WHERE txid = ? LIMIT 1")
.bind(txid.to_blob())
.fetch_optional(pool)
.await?;
Ok(row.is_some())
}
/// Txids of auth chain entries indexed from the mempool (all-zeros blockhash sentinel).
pub async fn get_unconfirmed_txids(pool: &SqlitePool) -> Result<Vec<Txid>> {
let rows = sqlx::query("SELECT DISTINCT txid FROM auth_chain_entry WHERE blockhash = ?")
.bind(BlockHash::all_zeros().to_blob())
.fetch_all(pool)
.await?;
let mut txids = Vec::with_capacity(rows.len());
for r in rows {
let txid_blob: Vec<u8> = r.get(0);
txids.push(Txid::from_blob(&txid_blob).context("failed to decode txid blob")?);
}
Ok(txids)
}
/// Delete a mempool-indexed (all-zeros blockhash) auth chain entry. Entries the
/// tx confirmed into are untouched: they carry the real blockhash by then.
pub async fn delete_unconfirmed_tx(pool: &SqlitePool, txid: &Txid) -> Result<()> {
sqlx::query("DELETE FROM auth_chain_entry WHERE blockhash = ? AND txid = ?")
.bind(BlockHash::all_zeros().to_blob())
.bind(txid.to_blob())
.execute(pool)
.await?;
Ok(())
}
/// Delete all mempool-indexed (all-zeros blockhash) auth chain entries.
pub async fn clear_mempool(pool: &SqlitePool) -> Result<usize> {
let r = sqlx::query("DELETE FROM auth_chain_entry WHERE blockhash = ?")
.bind(BlockHash::all_zeros().to_blob())
.execute(pool)
.await?;
Ok(r.rows_affected() as usize)
}
pub async fn delete_entries_for_block(pool: &SqlitePool, blockhash: &BlockHash) -> Result<bool> {
let r = sqlx::query("DELETE FROM auth_chain_entry WHERE blockhash = ?")
.bind(blockhash.to_blob())
@ -149,10 +212,18 @@ pub async fn insert_authheader(
height: usize,
bcmr_data: Option<Vec<u8>>,
) -> Result<()> {
// Upsert rather than INSERT OR REPLACE: REPLACE deletes the existing row,
// which cascades into bcmr_data and would throw away already-downloaded
// metadata every time a mempool-indexed entry is re-indexed on confirmation.
sqlx::query(
"INSERT OR REPLACE INTO auth_chain_entry
"INSERT INTO auth_chain_entry
(utxo, blockhash, txid, token_id, height, bcmr_data)
VALUES (?, ?, ?, ?, ?, ?)",
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(token_id, utxo) DO UPDATE SET
blockhash = excluded.blockhash,
txid = excluded.txid,
height = excluded.height,
bcmr_data = excluded.bcmr_data",
)
.bind(utxo.to_blob())
.bind(blockhash.to_blob())
@ -506,6 +577,127 @@ pub async fn filter_tokens_with_bcmr(
mod tests {
use super::*;
#[tokio::test]
async fn confirming_mempool_entry_preserves_downloaded_bcmr_data() {
use sqlx::sqlite::SqliteConnectOptions;
use std::str::FromStr;
// foreign_keys on, as in production (db/init.rs): with INSERT OR
// REPLACE the confirmation re-insert would delete the row and cascade
// away bcmr_data; the upsert must keep it.
let opts = SqliteConnectOptions::from_str("sqlite::memory:")
.unwrap()
.foreign_keys(true);
let pool = SqlitePool::connect_with(opts).await.unwrap();
prepare_tables(&pool).await;
let token_id = "0101010101010101010101010101010101010101010101010101010101010101"
.parse::<TokenID>()
.unwrap();
let txid = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
.parse::<Txid>()
.unwrap();
let utxo = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
.parse::<OutPointHash>()
.unwrap();
// Indexed from mempool: sentinel blockhash
insert_authheader(
&pool,
&utxo,
&BlockHash::all_zeros(),
&txid,
&token_id,
0,
Some(vec![0x6a]),
)
.await
.unwrap();
assert_eq!(get_unconfirmed_txids(&pool).await.unwrap(), vec![txid]);
// Downloader stores the metadata while the tx is still unconfirmed
let parsed = ParsedBCMR {
name: "Test".into(),
description: "".into(),
token: Token {
category: token_id.to_string(),
symbol: "TST".into(),
decimals: 0,
},
uris: Uris {
web: None,
icon: None,
},
filemeta: FileMeta {
expected_hash: None,
actual_hash: None,
source: SOURCE_ON_CHAIN.into(),
},
};
insert_bcmr_data(&pool, &token_id, &utxo, &parsed)
.await
.unwrap();
// Tx confirms: same entry re-indexed with the real blockhash
let real_blockhash = "1111111111111111111111111111111111111111111111111111111111111111"
.parse::<BlockHash>()
.unwrap();
insert_authheader(
&pool,
&utxo,
&real_blockhash,
&txid,
&token_id,
0,
Some(vec![0x6a]),
)
.await
.unwrap();
assert!(get_unconfirmed_txids(&pool).await.unwrap().is_empty());
let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM bcmr_data")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(count.0, 1, "downloaded bcmr_data must survive confirmation");
let bh: (Vec<u8>,) = sqlx::query_as("SELECT blockhash FROM auth_chain_entry")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(bh.0, real_blockhash.to_blob());
}
#[tokio::test]
async fn delete_unconfirmed_tx_leaves_confirmed_entries() {
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
prepare_tables(&pool).await;
let token_id = "0101010101010101010101010101010101010101010101010101010101010101"
.parse::<TokenID>()
.unwrap();
let txid = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
.parse::<Txid>()
.unwrap();
let utxo = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
.parse::<OutPointHash>()
.unwrap();
let real_blockhash = "1111111111111111111111111111111111111111111111111111111111111111"
.parse::<BlockHash>()
.unwrap();
insert_authheader(&pool, &utxo, &real_blockhash, &txid, &token_id, 0, None)
.await
.unwrap();
// A confirmed entry from the same tx must not be dropped by the
// mempool cleanup pass.
delete_unconfirmed_tx(&pool, &txid).await.unwrap();
assert!(has_indexed_tx(&pool, &txid).await.unwrap());
}
#[tokio::test]
async fn test_update_bcmr_failure_multiple_tokens_same_utxo_txid() {
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();

File diff suppressed because one or more lines are too long

View file

@ -115,6 +115,8 @@ pub async fn initialize_databases(network: &str, read_slots: ReadSlots) -> Resul
if !db_exists {
bcmr_prepare_tables(&bcmr_db_write).await;
}
// Always-run migration: safe on both new and existing bcmr.db
crate::db::bcmr::ensure_indexes(&bcmr_db_write).await;
// Initialize CRC20 database
let (db_exists, crc20_db_write, crc20_db_read) =
@ -142,6 +144,9 @@ pub async fn initialize_databases(network: &str, read_slots: ReadSlots) -> Resul
create_db_pool(&db_path(db_dir, "ido.db"), read_slots.ido).await;
if !db_exists {
ido_prepare_tables(&ido_db_write).await;
crate::db::ido::set_db_version(&ido_db_write).await?;
} else {
crate::db::ido::check_db_version(&ido_db_read).await?;
}
Ok(DB {

View file

@ -18,6 +18,7 @@ use riftenlabs_defi::{
};
use serde_json::{json, Value};
use crate::bcmr::BCMR_PREFIX;
use crate::db::ido::{IDO_PREINIT_ANNOUNCEMENT_SIGNATURE, IDO_SIGNATURE};
/// Fetch blockchain tip from electrum server
@ -40,10 +41,10 @@ pub fn electrum_get_tip(client: &Client) -> Result<(BlockHeader, u64)> {
Ok((deserialize(&hex::decode(header)?)?, height as u64))
}
/// Fetch defi (cauldron + tokentoken) and oracle mempool transactions
/// Fetch defi (cauldron + tokentoken), oracle, ido and bcmr mempool transactions
pub fn electrum_fetch_mempool(
client: &Client,
) -> Result<(HashSet<Txid>, HashSet<Txid>, HashSet<Txid>)> {
) -> Result<(HashSet<Txid>, HashSet<Txid>, HashSet<Txid>, HashSet<Txid>)> {
let cauldron_filter = json!({
"scriptsig": hex::encode(&V2_CONTRACT_TEMPLATE[(V2_CONTRACT_TEMPLATE.len() - 43)..]), // cauldron spends
"scriptpubkey": hex::encode([0x6a /* op_return */, 0x06 /* push */, b'S', b'U', b'M', b'M', b'O', b'N']), // new pools (potentially)
@ -82,6 +83,13 @@ pub fn electrum_fetch_mempool(
"operation": "union"
});
// BCMR registrations carry an `OP_RETURN "BCMR"` output (genesis or auth
// chain update). Auth chain transfers without a BCMR output are only
// picked up once confirmed.
let bcmr_filter = json!({
"scriptpubkey": hex::encode(BCMR_PREFIX),
});
let fetch_txs = |filter: Value| -> Result<HashSet<Txid>> {
let response = client.raw_call("mempool.get", [Param::Value(filter)])?;
@ -115,8 +123,9 @@ pub fn electrum_fetch_mempool(
let mut oracle_txs = fetch_txs(oracle_v1_filter)?;
oracle_txs.extend(fetch_txs(oracle_v2_filter)?);
let ido_txs = fetch_txs(ido_filter)?;
let bcmr_txs = fetch_txs(bcmr_filter)?;
Ok((defi_txs, oracle_txs, ido_txs))
Ok((defi_txs, oracle_txs, ido_txs, bcmr_txs))
}
/// Fetch blockchain tip from electrum server

View file

@ -77,7 +77,7 @@ pub async fn update_mempool(
db::cauldron::mempool::load_mempool(&db.cauldron_w).await?;
let electrum_clone = electrum.clone();
let (cauldron_txs, oracle_txs, ido_txs) = tokio::task::spawn_blocking(move || {
let (cauldron_txs, oracle_txs, ido_txs, bcmr_txs) = tokio::task::spawn_blocking(move || {
electrum_fetch_mempool(&electrum_clone.lock().unwrap())
})
.await??;
@ -218,6 +218,47 @@ pub async fn update_mempool(
let txs_to_add = ttor_sorted_kahn(txs_to_add);
db::ido::index_txs(network, &db.ido_w, &txs_to_add, None).await?;
// bcmr updates: index newly registered BCMRs without waiting for a confirmation
// Drop mempool-indexed entries whose tx left the mempool: either it
// confirmed (index_blocks already re-stamped the entry with the real
// blockhash, so it no longer matches the sentinel) or it was evicted or
// replaced and must not linger as a stale auth head.
for txid in db::bcmr::get_unconfirmed_txids(&db.bcmr_w).await? {
if !bcmr_txs.contains(&txid) {
db::bcmr::delete_unconfirmed_tx(&db.bcmr_w, &txid).await?;
}
}
// filter txs already indexed into the auth chain
let mut bcmr_to_add = Vec::new();
for txid in bcmr_txs {
if !db::bcmr::has_indexed_tx(&db.bcmr_w, &txid).await? {
bcmr_to_add.push(txid);
}
}
let bcmr_electrum = electrum.clone();
let txs_to_add: Vec<Transaction> = tokio::task::spawn_blocking(move || {
bcmr_to_add
.into_iter()
.filter_map(
|txid| match electrum_get_tx(&bcmr_electrum.lock().unwrap(), &txid) {
Ok(tx) => Some(tx),
Err(e) => {
info!("Failed to get mempool tx {txid}: {e}");
None
}
},
)
.collect()
})
.await?;
// an auth chain can have several unconfirmed txs in flight; index parents first
let txs_to_add = ttor_sorted_kahn(txs_to_add);
index_bcmr(&db.bcmr_w, &BlockHash::all_zeros(), txs_to_add).await?;
Ok(())
}

View file

@ -232,6 +232,7 @@ async fn start_program(
db::oracle::clear_mempool(&db.oracle_w).await.unwrap();
db::moria::clear_mempool(&db.moria_w).await.unwrap();
db::bcmr::clear_mempool(&db.bcmr_w).await.unwrap();
let indexing_in_progress_clone = indexing_in_progress.clone();
let ibd_state_clone = ibd_state.clone();