diff --git a/src/db/ido/mod.rs b/src/db/ido/mod.rs index 063cfe8..938d0d6 100644 --- a/src/db/ido/mod.rs +++ b/src/db/ido/mod.rs @@ -1,6 +1,6 @@ #![allow(non_snake_case)] use anyhow::Result; -use log::{info}; +use log::{info,warn}; use num_bigint::{BigInt, Sign}; use num_traits::{Zero, ToPrimitive}; use bitcoincash::blockdata::script::{Builder, Script, Instruction}; @@ -360,7 +360,7 @@ fn build_partial_postlaunch_bytecode(permanentLiquidityShare: &BigInt, price: &B bytecode } -fn build_partial_ido_initaitor_bytecode(postLaunchPartialBytecode: &[u8], permanentPoolOTokenReserveOutpointIndex: &BigInt, offeringInitiatorOutpointIndex: &BigInt) -> Vec { +fn build_partial_ido_initiator_bytecode(postLaunchPartialBytecode: &[u8], permanentPoolOTokenReserveOutpointIndex: &BigInt, offeringInitiatorOutpointIndex: &BigInt) -> Vec { let mut bytecode = Builder::new() .push_slice(postLaunchPartialBytecode) .push_slice(&STORAGE_CONTRACT) @@ -840,6 +840,9 @@ async fn create_ido_context_from_preinit(network: Option, tx: &Transact // params main match &instructions[1] { Ok(Instruction::PushBytes(data)) => { + if data.len() < 185 { + return Err(anyhow::anyhow!("Incorrect announcement.mainData size")); + } preinit_parameters = IdoPreInitParameters { authguardLockingBytecode: authguardLockingBytecode, offering: IdoParametersOffering { @@ -914,7 +917,7 @@ async fn create_ido_context_from_preinit(network: Option, tx: &Transact // find authguardCategory let mut authguardNftOut = None; if preinit_parameters.authguardNftOutputIndex >= BigInt::from(0) { - authguardNftOut = tx.output.get(preinit_parameters.authguardNftOutputIndex.to_usize().unwrap()); + authguardNftOut = tx.output.get(preinit_parameters.authguardNftOutputIndex.to_usize().ok_or_else(|| anyhow::anyhow!("authguardNftOutputIndex out of range"))?); } let preInit_lock_parameters = IdoPreInitLockParameters { @@ -1031,7 +1034,7 @@ async fn create_ido_context_from_preinit(network: Option, tx: &Transact if build_p2sh32_script( &build_storage_script_with_data( 1, - &build_partial_ido_initaitor_bytecode( + &build_partial_ido_initiator_bytecode( &build_partial_postlaunch_bytecode( &preinit_parameters.permanentLiquidityShareNumerator, &preinit_parameters.offering.offer.priceNumerator, @@ -1108,7 +1111,7 @@ async fn on_create_ido( let mut dbtx = pool.begin().await?; let result = sqlx::query( "INSERT INTO ido (preinit_txid, init_txid, launch_txid, offering_token_id, offered_token_id, status, parameters, state, is_valid, is_token_created_at_preinit, txchain_entrypoint, txchain_head) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(context.preinit_txid) .bind(context.init_txid) @@ -1153,8 +1156,8 @@ async fn on_create_ido( fn is_preinit_state_ready_to_init(state: &IdoPreInitState) -> bool { state.oTokenGenerated != BigInt::from(0) && state.nextTxSetterFlag == BigInt::from(0) && - vm_number_to_bigint(&state.authguardCategory) != BigInt::from(0) && - vm_number_to_bigint(&state.idoCategory) != BigInt::from(0) + state.authguardCategory.iter().any(|&b| b != 0) && + state.idoCategory.iter().any(|&b| b != 0) } fn parse_preinit_state_from_tx( @@ -1250,11 +1253,14 @@ fn ido_add_tx( // init outputs created & init tx updates.push(IdoUpdate::Status("ACTIVE".to_string())); updates.push(IdoUpdate::InitTxId(tx.txid().to_blob())); - let launcherBytecodeIn = tx.input.get(2).expect("launcher bytecode storage does not exist!"); - let launcher_data = extract_data_from_unlocking_bytecode_of_storage_with_data(launcherBytecodeIn.script_sig.clone()).expect("failed to extract launcher bytecode"); + let launcherBytecodeIn = tx.input.get(2).ok_or_else(|| anyhow::anyhow!("launcher bytecode storage does not exist!"))?; + let launcher_data = extract_data_from_unlocking_bytecode_of_storage_with_data(launcherBytecodeIn.script_sig.clone()).map_err(|e| anyhow::anyhow!("failed to extract launcher bytecode: {e}"))?; let launcher_script = Script::from(launcher_data); let launcher_inst_list: Vec<_> = launcher_script.instructions().collect(); let collectorNFTH: Vec; + if launcher_inst_list.len() == 0 { + return Err(anyhow::anyhow!("empty launcher bytecode!")); + } match &launcher_inst_list[0] { Ok(Instruction::PushBytes(data)) => { collectorNFTH = data.to_vec(); @@ -1277,9 +1283,12 @@ fn ido_add_tx( updates.push(IdoUpdate::State(IdoState::Active(IdoActiveState { counter: BigInt::from(0) }))); - let tokenStorageOut = tx.output.get(2).expect("token storage does not exist!"); + 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!"))?; + if tokenStorageOut.token.is_none() || offeringOut.token.is_none() { + return Err(anyhow::anyhow!("output#1 & output#2 should have token!")); + } updates.push(IdoUpdate::OfferedTokenId(tokenStorageOut.token.as_ref().unwrap().id.to_blob())); - let offeringOut = tx.output.get(1).expect("offering does not exist!"); updates.push(IdoUpdate::OfferingTokenId(offeringOut.token.as_ref().unwrap().id.to_blob())); return Ok(IdoAddResult { updates: updates, @@ -1303,12 +1312,15 @@ fn ido_add_tx( }, "ACTIVE" => { // output#0 should contain a token with offering_token_id - let first_output = tx.output.get(0).expect("Should have first output!"); + let first_output = tx.output.get(0).ok_or_else(|| anyhow::anyhow!("Should have first output!"))?; if first_output.token.is_none() || first_output.token.as_ref().unwrap().id.to_blob() != context.offering_token_id.clone().unwrap_or_default() || !first_output.token.as_ref().unwrap().has_nft() { return Err(anyhow::anyhow!("output#0 is not one of the offering nft!")); } + if first_output.token.as_ref().unwrap().commitment.len() == 0 { + return Err(anyhow::anyhow!("Incorrect commitment size at output#0, should be greater than zero")); + } if first_output.token.as_ref().unwrap().commitment[0] & ITEM_TYPE_BITS == ITEM_TYPE_OFFERING { // enforce limitation (xToken == NATIVE_BCH) if let IdoParameters::Active(ref active_params) = context.parameters { @@ -1316,7 +1328,7 @@ fn ido_add_tx( return Err(anyhow::anyhow!("non-null xTokenCategory is not supported")); } } - let second_output = tx.output.get(1).expect("Should have the second output!"); + let second_output = tx.output.get(1).ok_or_else(|| anyhow::anyhow!("Should have the second output!"))?; if second_output.token.is_none() || second_output.token.as_ref().unwrap().id.to_blob() != context.offering_token_id.clone().unwrap_or_default() || !second_output.token.as_ref().unwrap().has_nft() { @@ -1325,13 +1337,23 @@ fn ido_add_tx( // input#0 is the offering, add entry // pull owner_nfthash from unlocking bytecode of input#0 let owner_nfthash; - let instructions: Vec<_> = tx.input.get(0).expect("Should have first input!").script_sig.instructions().collect(); + let first_input = tx.input.get(0).ok_or_else(|| anyhow::anyhow!("Should have first input!"))?; + let instructions: Vec<_> = first_input.script_sig.instructions().collect(); + if instructions.len() == 0 { + return Err(anyhow::anyhow!("empty unlocking bytecode!")); + } match &instructions[0] { Ok(Instruction::PushBytes(data)) => { owner_nfthash = data.to_vec(); }, _ => return Err(anyhow::anyhow!("OP_PUSH expected in the unlocking bytecode of the offering utxo!")), } + if first_output.token.as_ref().unwrap().commitment.len() < 9 { + return Err(anyhow::anyhow!("Incorrect commitment size at output#0")); + } + if second_output.token.as_ref().unwrap().commitment.len() < 15 { + return Err(anyhow::anyhow!("Incorrect commitment size at output#1")); + } updates.push(IdoUpdate::Entry(IdoUpdateEntry { txid: tx.txid().to_blob(), owner_nfthash: owner_nfthash, @@ -1349,6 +1371,9 @@ fn ido_add_tx( // input#0 is the launcher // output#0 distributor let dist_commitment = first_output.token.as_ref().unwrap().commitment.clone(); + if dist_commitment.len() < 35 { + return Err(anyhow::anyhow!("Incorrect commitment size at output#0")); + } updates.push(IdoUpdate::Status("DISTRIBUTING".to_string())); updates.push(IdoUpdate::LaunchTxId(tx.txid().to_blob())); updates.push(IdoUpdate::State( @@ -1366,6 +1391,9 @@ fn ido_add_tx( // input#0 is the launcher, nodist, jump to postlaunch // output#0 is the confirmation nft let conf_commitment = first_output.token.as_ref().unwrap().commitment.clone(); + if conf_commitment.len() < 31 { + return Err(anyhow::anyhow!("Incorrect commitment size at output#0")); + } updates.push(IdoUpdate::Status("POSTLAUNCH".to_string())); updates.push(IdoUpdate::LaunchTxId(tx.txid().to_blob())); updates.push(IdoUpdate::State( @@ -1397,7 +1425,7 @@ fn ido_add_tx( _ => return Err(anyhow::anyhow!("expecting active parameters")), } // output#0 should contain a token with offering_token_id - let first_output = tx.output.get(0).expect("Should have first output!"); + let first_output = tx.output.get(0).ok_or_else(|| anyhow::anyhow!("Should have first output!"))?; if first_output.token.is_none() || first_output.token.as_ref().unwrap().id.to_blob() != context.offering_token_id.clone().unwrap_or_default() || !first_output.token.as_ref().unwrap().has_nft() { @@ -1410,10 +1438,16 @@ fn ido_add_tx( } _ => return Err(anyhow::anyhow!("expecting distributing state")), } - let platform_fee_output = tx.output.get(4).expect("platform fee output does not exist!"); + let platform_fee_output = tx.output.get(4).ok_or_else(|| anyhow::anyhow!("platform fee output does not exist!"))?; + if first_output.token.as_ref().unwrap().commitment.len() == 0 { + return Err(anyhow::anyhow!("Incorrect commitment size at output#0, should be greater than zero")); + } if (first_output.token.as_ref().unwrap().commitment[0] & ITEM_TYPE_BITS) == ITEM_TYPE_DISTRIBUTOR { // output#0 distributor let dist_commitment = first_output.token.as_ref().unwrap().commitment.clone(); + if dist_commitment.len() < 35 { + return Err(anyhow::anyhow!("Incorrect dist_commitment size at output#0")); + } updates.push(IdoUpdate::State( IdoState::Distributing(IdoDistributingState { isRefund: (dist_commitment[0] & DISTRIBUTOR_FLAG_IS_REFUND) != 0, @@ -1428,6 +1462,9 @@ fn ido_add_tx( } else if (first_output.token.as_ref().unwrap().commitment[0] & ITEM_TYPE_BITS) == ITEM_TYPE_CONFIRMATION_NFT { // output#0 is the confirmation nft let conf_commitment = first_output.token.as_ref().unwrap().commitment.clone(); + if conf_commitment.len() < 31 { + return Err(anyhow::anyhow!("Incorrect conf_commitment size at output#0")); + } updates.push(IdoUpdate::Status("POSTLAUNCH".to_string())); updates.push(IdoUpdate::State( IdoState::PostLaunch(IdoPostLaunchState { @@ -1448,8 +1485,8 @@ fn ido_add_tx( }); }, "POSTLAUNCH" => { - let pp_output = tx.output.get(0).expect("Should have first output!"); - let collector_p2nfth = tx.output.get(2).expect("Should have third output!"); + let pp_output = tx.output.get(0).ok_or_else(|| anyhow::anyhow!("Should have first output!"))?; + let collector_p2nfth = tx.output.get(2).ok_or_else(|| anyhow::anyhow!("Should have third output!"))?; updates.push(IdoUpdate::Status("DISTRIBUTED".to_string())); let prev_state: &IdoPostLaunchState; match &context.state { @@ -1462,21 +1499,21 @@ fn ido_add_tx( IdoState::Distributed(IdoDistributedState { permanentPool: if pp_output.token.is_none() { None } else { Some(IdoPermanentPoolV0 { tokenAmount: BigInt::from(pp_output.token.as_ref() - .expect("pp should have tokens") + .ok_or_else(|| anyhow::anyhow!("pp should have tokens"))? .amount), satoshiAmount: BigInt::from(pp_output.value), }) }, refundAmount: BigInt::from(collector_p2nfth.token.as_ref() - .expect("collector_p2nfth should have tokens") + .ok_or_else(|| anyhow::anyhow!("collector_p2nfth should have tokens"))? .amount), discountAmount: prev_state.discountAmount.clone(), collectorEarnedAmount: BigInt::from(collector_p2nfth.value), platformEarnedAmount: prev_state.platformEarnedAmount.clone(), }) )); - updates.push(IdoUpdate::EntryDistributed { - txid: tx.txid().to_blob(), - }); + updates.push(IdoUpdate::EntryDistributed { + txid: tx.txid().to_blob(), + }); return Ok(IdoAddResult { updates: updates, next_output_index: -1, @@ -1671,7 +1708,7 @@ async fn on_add_ido_tx( .await?; let mut items_tx_map: HashMap, Transaction> = HashMap::new(); for item in &items { - let tx_deser: Transaction = bitcoincash::consensus::deserialize(&item.tx).expect("failed to deserialize tx"); + let tx_deser: Transaction = bitcoincash::consensus::deserialize(&item.tx).map_err(|e| anyhow::anyhow!("failed to deserialize tx: {e}"))?; items_tx_map.insert(item.txid.clone(), tx_deser); } let mut new_chain: Vec<&IdoTxChainDBRecord> = Vec::new(); @@ -1680,20 +1717,20 @@ async fn on_add_ido_tx( new_chain.insert(0, current_item); while current_item.prev_id.is_some() { let prev_id = current_item.prev_id.unwrap(); - let found_index = items_copy.iter().position(|a| a.id == prev_id).expect("txchain prev item not found!"); + let found_index = items_copy.iter().position(|a| a.id == prev_id).ok_or_else(|| anyhow::anyhow!("txchain prev item not found!"))?; current_item = items_copy.remove(found_index); new_chain.insert(0, current_item); } - if new_chain.get(0).unwrap().txid != ido.preinit_txid { + if new_chain.get(0).ok_or_else(|| anyhow::anyhow!("txchain is empty"))?.txid != ido.preinit_txid { return Err(anyhow::anyhow!("txchain entrypoint does not match preinit_txid")); } // start from PREINIT - let preinit_tx = items_tx_map.get(&ido.preinit_txid).expect("ido's entrypoint does not exist in the txchain"); + let preinit_tx = items_tx_map.get(&ido.preinit_txid).ok_or_else(|| anyhow::anyhow!("ido's entrypoint does not exist in the txchain"))?; let mut context = create_ido_context_from_preinit(network, preinit_tx).await?; let mut updates: Vec = Vec::new(); for i in 1..new_chain.len() { - let item = new_chain.get(i).expect("should not be none!"); - let item_tx = items_tx_map.get(&item.txid).expect("should not be none!"); + let item = new_chain.get(i).ok_or_else(|| anyhow::anyhow!("txchain item not found at index"))?; + let item_tx = items_tx_map.get(&item.txid).ok_or_else(|| anyhow::anyhow!("txchain tx not found in map"))?; // add tx let result = ido_add_tx(&context, item_tx)?; apply_updates_to_context(&mut context, &result.updates); @@ -1719,13 +1756,17 @@ async fn on_add_ido_tx( .await?; } else { // create context from ido + let parameters: IdoParameters = serde_json::from_slice(&ido.parameters) + .map_err(|e| anyhow::anyhow!("Failed to parse parameters: {e}"))?; + let state: IdoState = serde_json::from_slice(&ido.state) + .map_err(|e| anyhow::anyhow!("Failed to parse state: {e}"))?; let mut context: IdoContext = IdoContext { preinit_txid: ido.preinit_txid.clone(), init_txid: ido.init_txid.clone(), launch_txid: ido.launch_txid.clone(), status: ido.status.clone(), - parameters: serde_json::from_slice(&ido.parameters).expect("Failed to parse parameters"), - state: serde_json::from_slice(&ido.state).expect("Failed to parse state"), + parameters, + state, is_valid_ido: ido.is_valid, is_token_created_at_preinit: ido.is_token_created_at_preinit, offered_token_id: ido.offered_token_id.clone(), @@ -1778,24 +1819,29 @@ pub async fn index_block( for input_index in [ 0, 1, 3 ] { let input = tx.input.get(input_index); if input.is_some() { - if let Some(txchain_item) = - txchain_lookup_next( - pool, - &input.unwrap().previous_output.txid, - input.unwrap().previous_output.vout - ) - .await? - { - let ido = ido_lookup(pool, txchain_item.ido_id) - .await? - .expect("ido record not found!!"); - match on_add_ido_tx(network, pool, &ido, &txchain_item, tx, block_height) + if let Some(txchain_item) = + txchain_lookup_next( + pool, + &input.unwrap().previous_output.txid, + input.unwrap().previous_output.vout + ) + .await? + { + match ido_lookup(pool, txchain_item.ido_id) .await { - Err(err) => info!("add tx to an ido failed, txid: {}, {}", hex::encode(tx.txid().to_blob()), err), - _ => (), - } - } - } + Ok(Some(ido)) => { + match on_add_ido_tx(network, pool, &ido, &txchain_item, tx, block_height) + .await { + Err(err) => info!("add tx to an ido failed, txid: {}, {}", hex::encode(tx.txid().to_blob()), err), + _ => (), + } + }, + Err(err) => warn!("ido record not found!!, {}", err), + _ => warn!("ido record not found!!"), + } + break; + } + } } } } @@ -2096,13 +2142,14 @@ mod tests { preinit_txid: Vec, status: &str, is_valid: bool, + is_token_created_at_preinit: bool, offered_token_id: Option>, offering_token_id: Option>, ) -> i64 { sqlx::query( "INSERT INTO ido (preinit_txid, init_txid, launch_txid, offering_token_id, - offered_token_id, status, parameters, state, is_valid, txchain_entrypoint, txchain_head) - VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, ?, NULL, NULL)", + offered_token_id, status, parameters, state, is_valid, is_token_created_at_preinit, txchain_entrypoint, txchain_head) + VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, ?, ?, NULL, NULL)", ) .bind(preinit_txid) .bind(offering_token_id) @@ -2111,6 +2158,7 @@ mod tests { .bind(b"{}".as_slice()) .bind(b"{}".as_slice()) .bind(is_valid as i64) + .bind(is_token_created_at_preinit as i64) .execute(pool) .await .unwrap() @@ -2270,8 +2318,8 @@ mod tests { #[rocket::async_test] async fn list_idos_returns_all() { let pool = make_pool().await; - insert_test_ido(&pool, txid(1), "PREINIT", true, None, None).await; - insert_test_ido(&pool, txid(2), "ACTIVE", true, None, None).await; + insert_test_ido(&pool, txid(1), "PREINIT", true, true, None, None).await; + insert_test_ido(&pool, txid(2), "ACTIVE", true, true, None, None).await; let results = list_idos(&pool, None, None, None, 0, 100).await.unwrap(); assert_eq!(results.len(), 2); } @@ -2279,8 +2327,8 @@ mod tests { #[rocket::async_test] async fn list_idos_filter_is_valid() { let pool = make_pool().await; - insert_test_ido(&pool, txid(1), "PREINIT", true, None, None).await; - insert_test_ido(&pool, txid(2), "PREINIT", false, None, None).await; + insert_test_ido(&pool, txid(1), "PREINIT", true, true, None, None).await; + insert_test_ido(&pool, txid(2), "PREINIT", false, false, None, None).await; let valid = list_idos(&pool, Some(true), None, None, 0, 100).await.unwrap(); assert_eq!(valid.len(), 1); assert!(valid[0].is_valid); @@ -2292,9 +2340,9 @@ mod tests { #[rocket::async_test] async fn list_idos_filter_status() { let pool = make_pool().await; - insert_test_ido(&pool, txid(1), "PREINIT", true, None, None).await; - insert_test_ido(&pool, txid(2), "ACTIVE", true, None, None).await; - insert_test_ido(&pool, txid(3), "ACTIVE", true, None, None).await; + insert_test_ido(&pool, txid(1), "PREINIT", true, true, None, None).await; + insert_test_ido(&pool, txid(2), "ACTIVE", true, true, None, None).await; + insert_test_ido(&pool, txid(3), "ACTIVE", true, true, None, None).await; let active = list_idos(&pool, None, Some("ACTIVE".to_string()), None, 0, 100).await.unwrap(); assert_eq!(active.len(), 2); assert!(active.iter().all(|r| r.status == "ACTIVE")); @@ -2304,8 +2352,8 @@ mod tests { async fn list_idos_filter_offered_token_id() { let pool = make_pool().await; let token = vec![0xAB; 32]; - insert_test_ido(&pool, txid(1), "ACTIVE", true, Some(token.clone()), None).await; - insert_test_ido(&pool, txid(2), "ACTIVE", true, None, None).await; + insert_test_ido(&pool, txid(1), "ACTIVE", true, true, Some(token.clone()), None).await; + insert_test_ido(&pool, txid(2), "ACTIVE", true, true, None, None).await; let filtered = list_idos(&pool, None, None, Some(token), 0, 100).await.unwrap(); assert_eq!(filtered.len(), 1); } @@ -2314,7 +2362,7 @@ mod tests { async fn list_idos_pagination() { let pool = make_pool().await; for i in 1..=5u8 { - insert_test_ido(&pool, txid(i), "PREINIT", true, None, None).await; + insert_test_ido(&pool, txid(i), "PREINIT", true, true, None, None).await; } let page1 = list_idos(&pool, None, None, None, 0, 2).await.unwrap(); let page2 = list_idos(&pool, None, None, None, 2, 2).await.unwrap(); @@ -2333,7 +2381,7 @@ mod tests { async fn get_ido_by_offering_token_found() { let pool = make_pool().await; let tok = vec![0xCC; 32]; - insert_test_ido(&pool, txid(1), "ACTIVE", true, None, Some(tok.clone())).await; + insert_test_ido(&pool, txid(1), "ACTIVE", true, true, None, Some(tok.clone())).await; let result = get_ido_by_offering_token_id(&pool, tok).await.unwrap(); assert!(result.is_some()); assert_eq!(result.unwrap().status, "ACTIVE"); @@ -2366,7 +2414,7 @@ mod tests { #[rocket::async_test] async fn list_ido_entries_all() { let pool = make_pool().await; - let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, None, None).await; + let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, true, None, None).await; insert_test_entry(&pool, ido_id, txid(10), false).await; insert_test_entry(&pool, ido_id, txid(11), true).await; let all = list_ido_entries(&pool, ido_id, None, 0, 100).await.unwrap(); @@ -2376,7 +2424,7 @@ mod tests { #[rocket::async_test] async fn list_ido_entries_filter_distributed() { let pool = make_pool().await; - let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, None, None).await; + let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, true, None, None).await; insert_test_entry(&pool, ido_id, txid(10), false).await; insert_test_entry(&pool, ido_id, txid(11), true).await; insert_test_entry(&pool, ido_id, txid(12), true).await; @@ -2391,8 +2439,8 @@ mod tests { #[rocket::async_test] async fn list_ido_entries_scoped_to_ido() { let pool = make_pool().await; - let ido1 = insert_test_ido(&pool, txid(1), "ACTIVE", true, None, None).await; - let ido2 = insert_test_ido(&pool, txid(2), "ACTIVE", true, None, None).await; + let ido1 = insert_test_ido(&pool, txid(1), "ACTIVE", true, true, None, None).await; + let ido2 = insert_test_ido(&pool, txid(2), "ACTIVE", true, true, None, None).await; insert_test_entry(&pool, ido1, txid(10), false).await; insert_test_entry(&pool, ido1, txid(11), false).await; insert_test_entry(&pool, ido2, txid(12), false).await; @@ -2435,7 +2483,7 @@ mod tests { #[rocket::async_test] async fn list_ido_txchain_ordered_oldest_first() { let pool = make_pool().await; - let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, None, None).await; + let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, true, None, None).await; let id_a = insert_txchain_item(&pool, ido_id, txid(10), None).await; let id_b = insert_txchain_item(&pool, ido_id, txid(11), Some(id_a)).await; let id_c = insert_txchain_item(&pool, ido_id, txid(12), Some(id_b)).await; @@ -2451,7 +2499,7 @@ mod tests { #[rocket::async_test] async fn list_ido_txchain_pagination() { let pool = make_pool().await; - let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, None, None).await; + let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, true, None, None).await; let id_a = insert_txchain_item(&pool, ido_id, txid(10), None).await; let id_b = insert_txchain_item(&pool, ido_id, txid(11), Some(id_a)).await; let id_c = insert_txchain_item(&pool, ido_id, txid(12), Some(id_b)).await; @@ -2471,7 +2519,7 @@ mod tests { #[rocket::async_test] async fn list_ido_txchain_no_head_returns_empty() { let pool = make_pool().await; - let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, None, None).await; + let ido_id = insert_test_ido(&pool, txid(1), "ACTIVE", true, true, None, None).await; // txchain_head is NULL (default from insert_test_ido) let chain = list_ido_txchain(&pool, ido_id, 0, 100).await.unwrap(); assert_eq!(chain.len(), 0); diff --git a/src/db/init.rs b/src/db/init.rs index 73fcb7e..e347ce4 100644 --- a/src/db/init.rs +++ b/src/db/init.rs @@ -142,8 +142,6 @@ 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; - } else { - check_db_version(&ido_db_read).await?; } Ok(DB {