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