ido: maintain entry aggregates in active state
Replace the per-request get_ido_entry_aggregates SQL scan with running totals carried in IdoActiveState: totalDemandAmount, totalSupplyAmount, and totalDiscount. Initialized to 0 at the preinit->active transition and incremented on each entry added; the rebuild path recomputes them by replaying the txchain, so they self-heal. Drop the now-unused get_ido_entry_aggregates DB fn, the IdoEntryAggregatesRpc type, the /<id>/aggregates RPC handler, and its route registration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b8468a1256
commit
9b0a54c926
3 changed files with 23 additions and 52 deletions
|
|
@ -241,6 +241,16 @@ pub struct IdoActiveState {
|
|||
idoCategory: Vec<u8>,
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
counter: BigInt,
|
||||
// Running totals over the IDO's entries, maintained while the IDO is ACTIVE
|
||||
// so "raised so far" can be served from state instead of aggregating the
|
||||
// ido_entry table on every request. Initialized to 0 at the preinit→active
|
||||
// transition and incremented on each entry added.
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
totalDemandAmount: BigInt,
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
totalSupplyAmount: BigInt,
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
totalDiscount: BigInt,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
|
|
@ -1821,7 +1831,10 @@ fn ido_add_tx(
|
|||
updates.push(IdoUpdate::State(IdoState::Active(IdoActiveState {
|
||||
authguardCategory: preinit_state.authguardCategory.clone(),
|
||||
idoCategory: preinit_state.idoCategory.clone(),
|
||||
counter: BigInt::from(0)
|
||||
counter: BigInt::from(0),
|
||||
totalDemandAmount: BigInt::from(0),
|
||||
totalSupplyAmount: BigInt::from(0),
|
||||
totalDiscount: BigInt::from(0),
|
||||
})));
|
||||
let tokenStorageOut = tx.output.get(2).ok_or_else(|| anyhow::anyhow!("token storage does not exist!"))?;
|
||||
let offeringOut = tx.output.get(1).ok_or_else(|| anyhow::anyhow!("offering does not exist!"))?;
|
||||
|
|
@ -1913,13 +1926,16 @@ fn ido_add_tx(
|
|||
if second_output.token.as_ref().unwrap().commitment.len() < 15 {
|
||||
return Err(anyhow::anyhow!("Incorrect commitment size at output#1"));
|
||||
}
|
||||
let supply_amount = second_output.value.to_sat();
|
||||
let demand_amount = second_output.token.as_ref().unwrap().amount as u64;
|
||||
let discount = decode_padded_vm_number(&second_output.token.as_ref().unwrap().commitment[7..15]).to_u64().unwrap_or(0);
|
||||
updates.push(IdoUpdate::Entry(IdoUpdateEntry {
|
||||
txid: tx.compute_txid().to_blob(),
|
||||
owner_nfthash: owner_nfthash,
|
||||
supply_amount: second_output.value.to_sat(),
|
||||
demand_amount: second_output.token.as_ref().unwrap().amount as u64,
|
||||
supply_amount,
|
||||
demand_amount,
|
||||
lockup_timeval: decode_padded_vm_number(&second_output.token.as_ref().unwrap().commitment[1..7]).to_u64().unwrap_or(0),
|
||||
discount: decode_padded_vm_number(&second_output.token.as_ref().unwrap().commitment[7..15]).to_u64().unwrap_or(0),
|
||||
discount,
|
||||
commitment: second_output.token.as_ref().unwrap().commitment.clone(),
|
||||
distributed: false,
|
||||
}));
|
||||
|
|
@ -1927,6 +1943,9 @@ fn ido_add_tx(
|
|||
authguardCategory: active_state.authguardCategory.clone(),
|
||||
idoCategory: active_state.idoCategory.clone(),
|
||||
counter: decode_padded_vm_number(&first_output.token.as_ref().unwrap().commitment[5..9]),
|
||||
totalDemandAmount: &active_state.totalDemandAmount + BigInt::from(demand_amount),
|
||||
totalSupplyAmount: &active_state.totalSupplyAmount + BigInt::from(supply_amount),
|
||||
totalDiscount: &active_state.totalDiscount + BigInt::from(discount),
|
||||
})));
|
||||
} else if (first_output.token.as_ref().unwrap().commitment[0] & ITEM_TYPE_BITS) == ITEM_TYPE_DISTRIBUTOR {
|
||||
// input#0 is the launcher
|
||||
|
|
@ -2790,34 +2809,6 @@ pub async fn list_ido_entries(
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct IdoEntryAggregatesRpc {
|
||||
pub total_demand_amount: i64,
|
||||
pub total_supply_amount: i64,
|
||||
pub entry_count: i64,
|
||||
}
|
||||
|
||||
/// Aggregate totals over an IDO's entries. Used to show "raised so far" for
|
||||
/// ACTIVE offerings, where on-chain state keeps no running total.
|
||||
pub async fn get_ido_entry_aggregates(
|
||||
pool: &SqlitePool,
|
||||
internal_id: i64,
|
||||
) -> Result<IdoEntryAggregatesRpc> {
|
||||
let row = sqlx::query(
|
||||
"SELECT COALESCE(SUM(demand_amount), 0) AS total_demand_amount, \
|
||||
COALESCE(SUM(supply_amount), 0) AS total_supply_amount, \
|
||||
COUNT(*) AS entry_count FROM ido_entry WHERE ido_id = ?",
|
||||
)
|
||||
.bind(internal_id)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
Ok(IdoEntryAggregatesRpc {
|
||||
total_demand_amount: row.get(0),
|
||||
total_supply_amount: row.get(1),
|
||||
entry_count: row.get(2),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn list_ido_txchain(
|
||||
pool: &SqlitePool,
|
||||
internal_id: i64,
|
||||
|
|
|
|||
|
|
@ -575,7 +575,6 @@ async fn launch() -> _ {
|
|||
rpc::ido::get_ido_by_id,
|
||||
rpc::ido::get_ido_by_offering_token,
|
||||
rpc::ido::list_ido_entries,
|
||||
rpc::ido::get_ido_entries_aggregates,
|
||||
rpc::ido::get_txchain_tx,
|
||||
];
|
||||
// Debug-only IDO endpoints, mounted only when `debug = true` in the config.
|
||||
|
|
|
|||
|
|
@ -175,25 +175,6 @@ pub async fn list_ido_entries(
|
|||
Ok(cached_ok(serde_json::to_value(items).unwrap(), CACHE_NONE))
|
||||
}
|
||||
|
||||
/// Aggregate totals over an IDO's entries (summed demand/supply and count).
|
||||
///
|
||||
/// - `id`: the IDO's public id (preinit txid, 64-char hex)
|
||||
///
|
||||
/// Returns `{"total_demand_amount", "total_supply_amount", "entry_count"}`.
|
||||
#[get("/<id>/aggregates")]
|
||||
pub async fn get_ido_entries_aggregates(
|
||||
id: &str,
|
||||
db: &State<DB>,
|
||||
) -> CachedApiResult<Value> {
|
||||
let (internal_id, _preinit_txid_hex) = resolve_ido(id, &db.ido_r).await?;
|
||||
|
||||
let agg = crate::db::ido::get_ido_entry_aggregates(&db.ido_r, internal_id)
|
||||
.await
|
||||
.map_err(db_error)?;
|
||||
|
||||
Ok(cached_ok(serde_json::to_value(agg).unwrap(), CACHE_NONE))
|
||||
}
|
||||
|
||||
/// Get the raw transaction hex for a txchain item by its txid.
|
||||
/// Returns `{"tx": "<hex>"}` or `{"tx": null}` if not found.
|
||||
#[get("/txchain/<txid>/tx")]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue