ido: migrate to bitcoincash 0.32 script API; fix mempool diff

Update ido/mod.rs for the bitcoincash 0.32 API: Script -> ScriptBuf,
push_slice via &PushBytes (new pb() helper), as_byte_array/as_bytes.
Fix update_mempool to diff against cauldron_txs instead of defi_txs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hossein Zoda 2026-06-10 22:27:50 +00:00
parent edf7955a23
commit a410aee95f
2 changed files with 96 additions and 88 deletions

View file

@ -3,7 +3,7 @@ use anyhow::Result;
use log::{info,warn,debug}; use log::{info,warn,debug};
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, ScriptBuf, Instruction, PushBytes};
use bitcoincash::blockdata::opcodes; use bitcoincash::blockdata::opcodes;
use bitcoin_hashes::sha256d; use bitcoin_hashes::sha256d;
use bitcoin_hashes::Hash; use bitcoin_hashes::Hash;
@ -322,40 +322,46 @@ pub enum IdoState {
Distributed(IdoDistributedState), Distributed(IdoDistributedState),
} }
fn build_p2nfth_script(nfthash: &[u8]) -> Script { /// Convert a byte slice into a `&PushBytes` for use with `Builder::push_slice`.
/// bitcoincash 0.32 requires `AsRef<PushBytes>` rather than a raw `&[u8]`.
fn pb(data: &[u8]) -> &PushBytes {
data.try_into().expect("push slice exceeds maximum length")
}
fn build_p2nfth_script(nfthash: &[u8]) -> ScriptBuf {
let mut bytes = Builder::new() let mut bytes = Builder::new()
.push_slice(nfthash) .push_slice(pb(nfthash))
.into_script() .into_script()
.to_bytes(); .to_bytes();
bytes.extend_from_slice(&P2NFTH_CONTRACT); bytes.extend_from_slice(&P2NFTH_CONTRACT);
Script::from(bytes) ScriptBuf::from(bytes)
} }
fn build_storage_script(governingOutpointIndex: u32) -> Script { fn build_storage_script(governingOutpointIndex: u32) -> ScriptBuf {
let mut bytes = Builder::new() let mut bytes = Builder::new()
.push_slice(&encode_padded_vm_number(&BigInt::from(governingOutpointIndex), 2).unwrap()) .push_slice(pb(&encode_padded_vm_number(&BigInt::from(governingOutpointIndex), 2).unwrap()))
.into_script() .into_script()
.to_bytes(); .to_bytes();
bytes.extend_from_slice(&STORAGE_CONTRACT); bytes.extend_from_slice(&STORAGE_CONTRACT);
Script::from(bytes) ScriptBuf::from(bytes)
} }
fn build_storage_script_with_data_and_size(governingOutpointIndex: u32, data: &[u8]) -> Script { fn build_storage_script_with_data_and_size(governingOutpointIndex: u32, data: &[u8]) -> ScriptBuf {
let mut data_and_size = Vec::new(); let mut data_and_size = Vec::new();
data_and_size.extend_from_slice(data); data_and_size.extend_from_slice(data);
data_and_size.extend_from_slice(&encode_padded_vm_number(&BigInt::from(data.len()), 2).unwrap()); data_and_size.extend_from_slice(&encode_padded_vm_number(&BigInt::from(data.len()), 2).unwrap());
let mut bytes = Builder::new() let mut bytes = Builder::new()
.push_slice(&encode_padded_vm_number(&BigInt::from(governingOutpointIndex), 2).unwrap()) .push_slice(pb(&encode_padded_vm_number(&BigInt::from(governingOutpointIndex), 2).unwrap()))
.into_script() .into_script()
.to_bytes(); .to_bytes();
bytes.extend_from_slice(&STORAGE_CONTRACT); bytes.extend_from_slice(&STORAGE_CONTRACT);
let suffix = Builder::new() let suffix = Builder::new()
.push_slice(&data_and_size) .push_slice(pb(&data_and_size))
.push_opcode(opcodes::all::OP_DROP) .push_opcode(opcodes::all::OP_DROP)
.into_script() .into_script()
.to_bytes(); .to_bytes();
bytes.extend_from_slice(&suffix); bytes.extend_from_slice(&suffix);
Script::from(bytes) ScriptBuf::from(bytes)
} }
fn extract_data_from_storage_script_with_data_and_size(script: &Script) -> Result<Vec<u8>> { fn extract_data_from_storage_script_with_data_and_size(script: &Script) -> Result<Vec<u8>> {
@ -365,7 +371,7 @@ fn extract_data_from_storage_script_with_data_and_size(script: &Script) -> Resul
return Err(anyhow::anyhow!("storage with data should have at least two instructions")); return Err(anyhow::anyhow!("storage with data should have at least two instructions"));
} }
match &inst_list[n - 2] { match &inst_list[n - 2] {
Ok(Instruction::PushBytes(data)) => Ok(data[0..data.len()-2].to_vec()), Ok(Instruction::PushBytes(data)) => { let data = data.as_bytes(); Ok(data[0..data.len()-2].to_vec()) },
_ => Err(anyhow::anyhow!("storage with data should have a push opcode at instructions[count - 2]")), _ => Err(anyhow::anyhow!("storage with data should have a push opcode at instructions[count - 2]")),
} }
} }
@ -377,7 +383,7 @@ fn extract_data_from_unlocking_bytecode_of_storage_with_data_and_size(unlocking_
} }
match &instructions[1] { match &instructions[1] {
Ok(Instruction::PushBytes(redeem_bytecode)) => { Ok(Instruction::PushBytes(redeem_bytecode)) => {
extract_data_from_storage_script_with_data_and_size(&Script::from(redeem_bytecode.to_vec())) extract_data_from_storage_script_with_data_and_size(Script::from_bytes(redeem_bytecode.as_bytes()))
}, },
_ => Err(anyhow::anyhow!("invalid unlocking bytecode, p2sh redeem script push opcode is expected")), _ => Err(anyhow::anyhow!("invalid unlocking bytecode, p2sh redeem script push opcode is expected")),
} }
@ -387,6 +393,7 @@ fn has_script_ido_signature(unlocking_bytecode: &Script) -> bool {
let instructions: Vec<_> = unlocking_bytecode.instructions().collect(); let instructions: Vec<_> = unlocking_bytecode.instructions().collect();
match instructions.last() { match instructions.last() {
Some(Ok(Instruction::PushBytes(redeem_bytecode))) => { Some(Ok(Instruction::PushBytes(redeem_bytecode))) => {
let redeem_bytecode = redeem_bytecode.as_bytes();
redeem_bytecode.len() >= IDO_SIGNATURE.len() && redeem_bytecode.len() >= IDO_SIGNATURE.len() &&
&redeem_bytecode[0..IDO_SIGNATURE.len()] == IDO_SIGNATURE &redeem_bytecode[0..IDO_SIGNATURE.len()] == IDO_SIGNATURE
}, },
@ -396,8 +403,8 @@ fn has_script_ido_signature(unlocking_bytecode: &Script) -> bool {
fn build_offering_bytecode(maxDiscountRate: &BigInt, discountAnnualRate: &BigInt, price: &BigInt, minOffer: &BigInt, xTokenCategory: &[u8]) -> Vec<u8> { fn build_offering_bytecode(maxDiscountRate: &BigInt, discountAnnualRate: &BigInt, price: &BigInt, minOffer: &BigInt, xTokenCategory: &[u8]) -> Vec<u8> {
let mut input_bytecode = Builder::new() let mut input_bytecode = Builder::new()
.push_slice(&STORAGE_CONTRACT) .push_slice(pb(&STORAGE_CONTRACT))
.push_slice(&OFFERING_ENTRY_CONTRACT) .push_slice(pb(&OFFERING_ENTRY_CONTRACT))
.into_script() .into_script()
.to_bytes(); .to_bytes();
input_bytecode.extend_from_slice(&bigint_to_push_opcode(maxDiscountRate)); input_bytecode.extend_from_slice(&bigint_to_push_opcode(maxDiscountRate));
@ -405,7 +412,7 @@ fn build_offering_bytecode(maxDiscountRate: &BigInt, discountAnnualRate: &BigInt
input_bytecode.extend_from_slice(&bigint_to_push_opcode(price)); input_bytecode.extend_from_slice(&bigint_to_push_opcode(price));
input_bytecode.extend_from_slice(&bigint_to_push_opcode(minOffer)); input_bytecode.extend_from_slice(&bigint_to_push_opcode(minOffer));
input_bytecode.extend_from_slice(&Builder::new() input_bytecode.extend_from_slice(&Builder::new()
.push_slice(xTokenCategory) .push_slice(pb(xTokenCategory))
.into_script() .into_script()
.to_bytes() .to_bytes()
); );
@ -418,14 +425,14 @@ fn build_offering_bytecode(maxDiscountRate: &BigInt, discountAnnualRate: &BigInt
fn build_launcher_bytecode(collectorNFTH: &[u8], platformFeeNFTH: &[u8], platformFee: &BigInt, delphiCategory: &[u8], expiresAt: &BigInt, deployThreshold: &BigInt, immediateDeployThreshold: &BigInt) -> Vec<u8> { fn build_launcher_bytecode(collectorNFTH: &[u8], platformFeeNFTH: &[u8], platformFee: &BigInt, delphiCategory: &[u8], expiresAt: &BigInt, deployThreshold: &BigInt, immediateDeployThreshold: &BigInt) -> Vec<u8> {
let rev_delphi_cat: Vec<u8> = delphiCategory.iter().copied().rev().collect(); let rev_delphi_cat: Vec<u8> = delphiCategory.iter().copied().rev().collect();
let mut input_bytecode = Builder::new() let mut input_bytecode = Builder::new()
.push_slice(collectorNFTH) .push_slice(pb(collectorNFTH))
.push_slice(platformFeeNFTH) .push_slice(pb(platformFeeNFTH))
.push_slice(&encode_padded_vm_number(&platformFee, 4).unwrap()) .push_slice(pb(&encode_padded_vm_number(&platformFee, 4).unwrap()))
.push_slice(&EXECUTION_FEE_PAYOUT_CONTRACT) .push_slice(pb(&EXECUTION_FEE_PAYOUT_CONTRACT))
.push_slice(&STORAGE_CONTRACT) .push_slice(pb(&STORAGE_CONTRACT))
.push_slice(&OFFERING_INTEGRATED_TIMELOCKED_P2NFTH_CONTRACT) .push_slice(pb(&OFFERING_INTEGRATED_TIMELOCKED_P2NFTH_CONTRACT))
.push_slice(&P2NFTH_CONTRACT) .push_slice(pb(&P2NFTH_CONTRACT))
.push_slice(&rev_delphi_cat) .push_slice(pb(&rev_delphi_cat))
.into_script() .into_script()
.to_bytes(); .to_bytes();
input_bytecode.extend_from_slice(&bigint_to_push_opcode(expiresAt)); input_bytecode.extend_from_slice(&bigint_to_push_opcode(expiresAt));
@ -439,7 +446,7 @@ fn build_launcher_bytecode(collectorNFTH: &[u8], platformFeeNFTH: &[u8], platfor
fn build_partial_offering_initiator_bytecode(extensionOutpointIndex: &BigInt, tokenStorageOutpointIndex: &BigInt, offeringBcmrStorageOutpointIndex: &BigInt, distRefundOutpointIndex: &BigInt, distDeployOutpointIndex: &BigInt, launcherBytecodeOutpointIndex: &BigInt, offeringBytecodeOutpointIndex: &BigInt, executionFee: &BigInt) -> Vec<u8> { fn build_partial_offering_initiator_bytecode(extensionOutpointIndex: &BigInt, tokenStorageOutpointIndex: &BigInt, offeringBcmrStorageOutpointIndex: &BigInt, distRefundOutpointIndex: &BigInt, distDeployOutpointIndex: &BigInt, launcherBytecodeOutpointIndex: &BigInt, offeringBytecodeOutpointIndex: &BigInt, executionFee: &BigInt) -> Vec<u8> {
let mut bytecode = Builder::new() let mut bytecode = Builder::new()
.push_slice(&STORAGE_CONTRACT) .push_slice(pb(&STORAGE_CONTRACT))
.into_script() .into_script()
.to_bytes(); .to_bytes();
bytecode.extend_from_slice(&bigint_to_push_opcode(extensionOutpointIndex)); bytecode.extend_from_slice(&bigint_to_push_opcode(extensionOutpointIndex));
@ -465,9 +472,9 @@ fn build_partial_postlaunch_bytecode(permanentLiquidityShare: &BigInt, price: &B
fn build_partial_ido_initiator_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(pb(postLaunchPartialBytecode))
.push_slice(&STORAGE_CONTRACT) .push_slice(pb(&STORAGE_CONTRACT))
.push_slice(&P2NFTH_CONTRACT) .push_slice(pb(&P2NFTH_CONTRACT))
.into_script() .into_script()
.to_bytes(); .to_bytes();
bytecode.extend_from_slice(&bigint_to_push_opcode(permanentPoolOTokenReserveOutpointIndex)); bytecode.extend_from_slice(&bigint_to_push_opcode(permanentPoolOTokenReserveOutpointIndex));
@ -481,17 +488,17 @@ fn create_rebuild_ipfs_placeholder_bcmr_partial_inputs(data: &IpfsBcmrWithPlaceh
bcmrUrisWithReplaceCalls.extend_from_slice(&data.metadata.bcmrUrisReplaceCalls); bcmrUrisWithReplaceCalls.extend_from_slice(&data.metadata.bcmrUrisReplaceCalls);
bcmrUrisWithReplaceCalls.extend_from_slice( bcmrUrisWithReplaceCalls.extend_from_slice(
&Builder::new() &Builder::new()
.push_slice(&data.urisBytecode) .push_slice(pb(&data.urisBytecode))
.into_script() .into_script()
.to_bytes() .to_bytes()
); );
Builder::new() Builder::new()
.push_slice(&data.contentHash) .push_slice(pb(&data.contentHash))
.push_slice(&data.metadata.registryReplaceCalls) .push_slice(pb(&data.metadata.registryReplaceCalls))
.push_int(1i64) .push_int(1i64)
// OP_DEFINE // OP_DEFINE
.push_opcode(bitcoincash::blockdata::opcodes::All::from(0x89u8)) .push_opcode(bitcoincash::blockdata::opcodes::All::from(0x89u8))
.push_slice(&bcmrUrisWithReplaceCalls) .push_slice(pb(&bcmrUrisWithReplaceCalls))
.push_int(2i64) .push_int(2i64)
// OP_DEFINE // OP_DEFINE
.push_opcode(bitcoincash::blockdata::opcodes::All::from(0x89u8)) .push_opcode(bitcoincash::blockdata::opcodes::All::from(0x89u8))
@ -512,14 +519,14 @@ fn build_preinit_bytecode(parameters: &IdoPreInitLockParameters, state: &IdoPreI
}; };
let middle = Builder::new() let middle = Builder::new()
.push_slice(&rev_ido) .push_slice(pb(&rev_ido))
.push_slice(&rev_authguard) .push_slice(pb(&rev_authguard))
.push_slice(&parameters.authguardLockingBytecode) .push_slice(pb(&parameters.authguardLockingBytecode))
.push_slice(&offering_bcmr_partial_inputs) .push_slice(pb(&offering_bcmr_partial_inputs))
.push_slice(&oToken_bcmr_partial_inputs) .push_slice(pb(&oToken_bcmr_partial_inputs))
.push_slice(&REBUILD_IPFS_PLACEHOLDER_BCMR_FOR_GENESIS_WITH_AUTHGUARD_CONTRACT) .push_slice(pb(&REBUILD_IPFS_PLACEHOLDER_BCMR_FOR_GENESIS_WITH_AUTHGUARD_CONTRACT))
.push_slice(&CASHTOKENS_STUDIO_PAY2CATEGORY_CONTRACT) .push_slice(pb(&CASHTOKENS_STUDIO_PAY2CATEGORY_CONTRACT))
.push_slice(&STORAGE_CONTRACT) .push_slice(pb(&STORAGE_CONTRACT))
.into_script() .into_script()
.to_bytes(); .to_bytes();
let mut bytecode = IDO_SIGNATURE.to_vec(); let mut bytecode = IDO_SIGNATURE.to_vec();
@ -533,10 +540,10 @@ fn build_preinit_bytecode(parameters: &IdoPreInitLockParameters, state: &IdoPreI
bytecode bytecode
} }
fn build_p2sh32_script(bytecode: &[u8]) -> Script { fn build_p2sh32_script(bytecode: &[u8]) -> ScriptBuf {
Builder::new() Builder::new()
.push_opcode(opcodes::all::OP_HASH256) .push_opcode(opcodes::all::OP_HASH256)
.push_slice(sha256d::Hash::hash(bytecode).as_inner()) .push_slice(pb(sha256d::Hash::hash(bytecode).as_byte_array()))
.push_opcode(opcodes::all::OP_EQUAL) .push_opcode(opcodes::all::OP_EQUAL)
.into_script() .into_script()
} }
@ -545,7 +552,7 @@ fn build_p2sh32_script(bytecode: &[u8]) -> Script {
fn build_p2sh20_script(bytecode: &[u8]) -> Script { fn build_p2sh20_script(bytecode: &[u8]) -> Script {
Builder::new() Builder::new()
.push_opcode(opcodes::all::OP_HASH160) .push_opcode(opcodes::all::OP_HASH160)
.push_slice(hash160::Hash::hash(bytecode).as_inner()) .push_slice(pb(hash160::Hash::hash(bytecode).as_byte_array()))
.push_opcode(opcodes::all::OP_EQUAL) .push_opcode(opcodes::all::OP_EQUAL)
.into_script() .into_script()
} }
@ -957,19 +964,19 @@ fn deserialize_ipfs_bcmr_with_placeholder(
return Err(anyhow::anyhow!("bcmr needs metadata!")); return Err(anyhow::anyhow!("bcmr needs metadata!"));
} }
let metadata_size = bcmr_data[0].to_usize().unwrap(); let metadata_size = bcmr_data[0].to_usize().unwrap();
let metadata_script = Script::from(bcmr_data[1..(metadata_size+1)].to_vec()); let metadata_script = ScriptBuf::from(bcmr_data[1..(metadata_size+1)].to_vec());
let metadata_inst_list: Vec<_> = metadata_script.instructions().collect(); let metadata_inst_list: Vec<_> = metadata_script.instructions().collect();
if metadata_inst_list.len() != 2 { if metadata_inst_list.len() != 2 {
return Err(anyhow::anyhow!("bcmr metadata should have two push opcodes!")); return Err(anyhow::anyhow!("bcmr metadata should have two push opcodes!"));
} }
let registryReplaceCalls = match &metadata_inst_list[0] { let registryReplaceCalls = match &metadata_inst_list[0] {
Ok(Instruction::PushBytes(data)) => data.to_vec(), Ok(Instruction::PushBytes(data)) => data.as_bytes().to_vec(),
_ => { _ => {
return Err(anyhow::anyhow!("oToken bcmr metadata opcode#0 is not a push opcode")); return Err(anyhow::anyhow!("oToken bcmr metadata opcode#0 is not a push opcode"));
} }
}; };
let bcmrUrisReplaceCalls = match &metadata_inst_list[1] { let bcmrUrisReplaceCalls = match &metadata_inst_list[1] {
Ok(Instruction::PushBytes(data)) => data.to_vec(), Ok(Instruction::PushBytes(data)) => data.as_bytes().to_vec(),
_ => { _ => {
return Err(anyhow::anyhow!("oToken bcmr metadata opcode#0 is not a push opcode")); return Err(anyhow::anyhow!("oToken bcmr metadata opcode#0 is not a push opcode"));
} }
@ -995,8 +1002,8 @@ fn serialize_ipfs_bcmr_with_placeholder(
match input { match input {
Some(value) => { Some(value) => {
let metadata_bytecode = Builder::new() let metadata_bytecode = Builder::new()
.push_slice(&value.metadata.registryReplaceCalls) .push_slice(pb(&value.metadata.registryReplaceCalls))
.push_slice(&value.metadata.bcmrUrisReplaceCalls) .push_slice(pb(&value.metadata.bcmrUrisReplaceCalls))
.into_script() .into_script()
.to_bytes(); .to_bytes();
let mut bcmr_bytecode = Vec::new(); let mut bcmr_bytecode = Vec::new();
@ -1004,7 +1011,7 @@ fn serialize_ipfs_bcmr_with_placeholder(
bcmr_bytecode.extend_from_slice(&value.contentHash); bcmr_bytecode.extend_from_slice(&value.contentHash);
bcmr_bytecode.extend_from_slice(&value.urisBytecode); bcmr_bytecode.extend_from_slice(&value.urisBytecode);
let metadata_push = Builder::new() let metadata_push = Builder::new()
.push_slice(&metadata_bytecode) .push_slice(pb(&metadata_bytecode))
.into_script() .into_script()
.to_bytes(); .to_bytes();
let mut bytes = Vec::new(); let mut bytes = Vec::new();
@ -1056,7 +1063,7 @@ fn parse_ido_preinit_tx_params(tx: &Transaction, delphi_token_id: &[u8], platfor
let authguardLockingBytecode; let authguardLockingBytecode;
match &instructions[2] { match &instructions[2] {
Ok(Instruction::PushBytes(data)) => { Ok(Instruction::PushBytes(data)) => {
authguardLockingBytecode = data.to_vec(); authguardLockingBytecode = data.as_bytes().to_vec();
} }
_ => { _ => {
errors.push(anyhow::anyhow!("invalid announcement (3)")); errors.push(anyhow::anyhow!("invalid announcement (3)"));
@ -1066,6 +1073,7 @@ fn parse_ido_preinit_tx_params(tx: &Transaction, delphi_token_id: &[u8], platfor
// params main // params main
match &instructions[1] { match &instructions[1] {
Ok(Instruction::PushBytes(data)) => { Ok(Instruction::PushBytes(data)) => {
let data = data.as_bytes();
if data.len() < 187 { if data.len() < 187 {
errors.push(anyhow::anyhow!("Incorrect announcement.mainData size")); errors.push(anyhow::anyhow!("Incorrect announcement.mainData size"));
return None; return None;
@ -1505,7 +1513,7 @@ fn parse_ido_preinit_tx(network: Option<Network>, tx: &Transaction, errors: &mut
if build_p2sh32_script( if build_p2sh32_script(
&build_p2nfth_script(&platform_fee_nfth).to_bytes() &build_p2nfth_script(&platform_fee_nfth).to_bytes()
) == output.script_pubkey { ) == output.script_pubkey {
preinit_paid_fee += output.value; preinit_paid_fee += output.value.to_sat();
} }
} }
if BigInt::from(preinit_paid_fee) < *IDO_CREATE_EXECUTION_FEE { if BigInt::from(preinit_paid_fee) < *IDO_CREATE_EXECUTION_FEE {
@ -1538,7 +1546,7 @@ fn create_ido_context_from_preinit(network: Option<Network>, tx: &Transaction, e
let params = result.parameters.expect("parameters should be defined!"); let params = result.parameters.expect("parameters should be defined!");
let state = result.state.expect("state should be defined!"); let state = result.state.expect("state should be defined!");
Ok(IdoContext { Ok(IdoContext {
preinit_txid: tx.txid().to_blob(), preinit_txid: tx.compute_txid().to_blob(),
init_txid: None, init_txid: None,
launch_txid: None, launch_txid: None,
otoken_genesis_txid: None, otoken_genesis_txid: None,
@ -1549,7 +1557,7 @@ fn create_ido_context_from_preinit(network: Option<Network>, tx: &Transaction, e
offered_token_id: result.offered_token_id, offered_token_id: result.offered_token_id,
offering_token_id: None, offering_token_id: None,
is_token_created_at_preinit: !result.offered_token_is_in_supply, is_token_created_at_preinit: !result.offered_token_is_in_supply,
head_txid: tx.txid().to_blob(), head_txid: tx.compute_txid().to_blob(),
}) })
} }
} }
@ -1560,18 +1568,18 @@ async fn on_create_ido(
tx: &Transaction, tx: &Transaction,
block_height: i64, block_height: i64,
) -> Result<()> { ) -> Result<()> {
debug!("IDO on_create_ido: {}", blob_to_display_hex::<Txid>(&tx.txid())?); debug!("IDO on_create_ido: {}", blob_to_display_hex::<Txid>(&tx.compute_txid().to_blob())?);
let mut errors: Vec<Error> = Vec::new(); let mut errors: Vec<Error> = Vec::new();
let mut invalid_ido_reasons: Vec<Error> = Vec::new(); let mut invalid_ido_reasons: Vec<Error> = Vec::new();
let result = create_ido_context_from_preinit(network, tx, &mut errors, &mut invalid_ido_reasons); let result = create_ido_context_from_preinit(network, tx, &mut errors, &mut invalid_ido_reasons);
if errors.len() > 0 { if errors.len() > 0 {
info!("Parse ido preinit failed, txid: {}\nError(s):", hex::encode(tx.txid().to_blob())); info!("Parse ido preinit failed, txid: {}\nError(s):", hex::encode(tx.compute_txid().to_blob()));
for error in errors { for error in errors {
info!(" - {}", error); info!(" - {}", error);
} }
} }
if invalid_ido_reasons.len() > 0 { if invalid_ido_reasons.len() > 0 {
info!("Invalid ido found, preinit_txid: {}\nReason(s):", hex::encode(tx.txid().to_blob())); info!("Invalid ido found, preinit_txid: {}\nReason(s):", hex::encode(tx.compute_txid().to_blob()));
for reason in invalid_ido_reasons { for reason in invalid_ido_reasons {
info!(" - {}", reason); info!(" - {}", reason);
} }
@ -1725,12 +1733,12 @@ fn ido_add_tx(
if preinit_state.initiatorCreated { if preinit_state.initiatorCreated {
// 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.compute_txid().to_blob()));
let launcherBytecodeIn = tx.input.get(2) let launcherBytecodeIn = tx.input.get(2)
.ok_or_else(|| anyhow::anyhow!("launcher bytecode storage does not exist!"))?; .ok_or_else(|| anyhow::anyhow!("launcher bytecode storage does not exist!"))?;
let launcher_data = extract_data_from_unlocking_bytecode_of_storage_with_data_and_size(&launcherBytecodeIn.script_sig) let launcher_data = extract_data_from_unlocking_bytecode_of_storage_with_data_and_size(&launcherBytecodeIn.script_sig)
.map_err(|e| anyhow::anyhow!("failed to extract launcher bytecode: {e}"))?; .map_err(|e| anyhow::anyhow!("failed to extract launcher bytecode: {e}"))?;
let launcher_script = Script::from(launcher_data); let launcher_script = ScriptBuf::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 { if launcher_inst_list.len() == 0 {
@ -1738,7 +1746,7 @@ fn ido_add_tx(
} }
match &launcher_inst_list[2] { match &launcher_inst_list[2] {
Ok(Instruction::PushBytes(data)) => { Ok(Instruction::PushBytes(data)) => {
collectorNFTH = data.to_vec(); collectorNFTH = data.as_bytes().to_vec();
} }
_ => return Err(anyhow::anyhow!("expecting push opcode for collectorNFTH")), _ => return Err(anyhow::anyhow!("expecting push opcode for collectorNFTH")),
} }
@ -1786,7 +1794,7 @@ fn ido_add_tx(
next_state.authguardCategory = context.head_txid.iter().copied().rev().collect(); next_state.authguardCategory = context.head_txid.iter().copied().rev().collect();
} else if preinit_state.oTokenGenerated == BigInt::from(0) { } else if preinit_state.oTokenGenerated == BigInt::from(0) {
next_state.oTokenGenerated = BigInt::from(1); next_state.oTokenGenerated = BigInt::from(1);
updates.push(IdoUpdate::OTokenGenesisTxId(tx.txid().to_blob())); updates.push(IdoUpdate::OTokenGenesisTxId(tx.compute_txid().to_blob()));
} else if !preinit_state.idoCategory.iter().any(|&b| b != 0) { } else if !preinit_state.idoCategory.iter().any(|&b| b != 0) {
next_state.idoCategory = context.head_txid.iter().copied().rev().collect(); next_state.idoCategory = context.head_txid.iter().copied().rev().collect();
} }
@ -1841,7 +1849,7 @@ fn ido_add_tx(
} }
match &instructions[0] { match &instructions[0] {
Ok(Instruction::PushBytes(data)) => { Ok(Instruction::PushBytes(data)) => {
owner_nfthash = data.to_vec(); owner_nfthash = data.as_bytes().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!")),
} }
@ -1852,9 +1860,9 @@ fn ido_add_tx(
return Err(anyhow::anyhow!("Incorrect commitment size at output#1")); 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.compute_txid().to_blob(),
owner_nfthash: owner_nfthash, owner_nfthash: owner_nfthash,
supply_amount: second_output.value, supply_amount: second_output.value.to_sat(),
demand_amount: second_output.token.as_ref().unwrap().amount as u64, demand_amount: second_output.token.as_ref().unwrap().amount as u64,
lockup_timeval: decode_padded_vm_number(&second_output.token.as_ref().unwrap().commitment[1..7]).to_u64().unwrap_or(0), 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: decode_padded_vm_number(&second_output.token.as_ref().unwrap().commitment[7..15]).to_u64().unwrap_or(0),
@ -1874,7 +1882,7 @@ fn ido_add_tx(
return Err(anyhow::anyhow!("Incorrect commitment size at output#0")); 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.compute_txid().to_blob()));
updates.push(IdoUpdate::State( updates.push(IdoUpdate::State(
IdoState::Distributing(IdoDistributingState { IdoState::Distributing(IdoDistributingState {
authguardCategory: active_state.authguardCategory.clone(), authguardCategory: active_state.authguardCategory.clone(),
@ -1896,7 +1904,7 @@ fn ido_add_tx(
return Err(anyhow::anyhow!("Incorrect commitment size at output#0")); 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.compute_txid().to_blob()));
updates.push(IdoUpdate::State( updates.push(IdoUpdate::State(
IdoState::PostLaunch(IdoPostLaunchState { IdoState::PostLaunch(IdoPostLaunchState {
authguardCategory: active_state.authguardCategory.clone(), authguardCategory: active_state.authguardCategory.clone(),
@ -1965,7 +1973,7 @@ fn ido_add_tx(
earnedAmount: decode_padded_vm_number(&dist_commitment[11..19]), earnedAmount: decode_padded_vm_number(&dist_commitment[11..19]),
refundAmount: decode_padded_vm_number(&dist_commitment[19..27]), refundAmount: decode_padded_vm_number(&dist_commitment[19..27]),
discountAmount: decode_padded_vm_number(&dist_commitment[27..35]), discountAmount: decode_padded_vm_number(&dist_commitment[27..35]),
platformEarnedAmount: &prev_state.platformEarnedAmount + BigInt::from(platform_fee_output.value), platformEarnedAmount: &prev_state.platformEarnedAmount + BigInt::from(platform_fee_output.value.to_sat()),
}) })
)); ));
} 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 {
@ -1984,7 +1992,7 @@ fn ido_add_tx(
earnedAmount: decode_padded_vm_number(&conf_commitment[7..15]), earnedAmount: decode_padded_vm_number(&conf_commitment[7..15]),
refundAmount: decode_padded_vm_number(&conf_commitment[15..23]), refundAmount: decode_padded_vm_number(&conf_commitment[15..23]),
discountAmount: decode_padded_vm_number(&conf_commitment[23..31]), discountAmount: decode_padded_vm_number(&conf_commitment[23..31]),
platformEarnedAmount: &prev_state.platformEarnedAmount + BigInt::from(platform_fee_output.value), platformEarnedAmount: &prev_state.platformEarnedAmount + BigInt::from(platform_fee_output.value.to_sat()),
}) })
)); ));
} else { } else {
@ -2015,18 +2023,18 @@ fn ido_add_tx(
tokenAmount: BigInt::from(pp_output.token.as_ref() tokenAmount: BigInt::from(pp_output.token.as_ref()
.ok_or_else(|| anyhow::anyhow!("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.to_sat()),
}) }, }) },
refundAmount: BigInt::from(collector_p2nfth.token.as_ref() refundAmount: BigInt::from(collector_p2nfth.token.as_ref()
.ok_or_else(|| anyhow::anyhow!("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.to_sat()),
platformEarnedAmount: prev_state.platformEarnedAmount.clone(), platformEarnedAmount: prev_state.platformEarnedAmount.clone(),
}) })
)); ));
updates.push(IdoUpdate::EntryDistributed { updates.push(IdoUpdate::EntryDistributed {
txid: tx.txid().to_blob(), txid: tx.compute_txid().to_blob(),
}); });
print_updates(&updates); print_updates(&updates);
return Ok(IdoAddResult { return Ok(IdoAddResult {
@ -2170,7 +2178,7 @@ async fn upsert_ido_txchain(
) )
.bind(prev_txchain_id) .bind(prev_txchain_id)
.bind(internal_id) .bind(internal_id)
.bind(tx.txid().to_blob()) .bind(tx.compute_txid().to_blob())
.bind(serialized_tx) .bind(serialized_tx)
.execute(&mut **dbtx) .execute(&mut **dbtx)
.await?; .await?;
@ -2180,7 +2188,7 @@ async fn upsert_ido_txchain(
"REPLACE INTO ido_txchain_tracker_map (txid, next_output_index, height, txchain_id) VALUES "REPLACE INTO ido_txchain_tracker_map (txid, next_output_index, height, txchain_id) VALUES
(?, ?, ?, ?)" (?, ?, ?, ?)"
) )
.bind(tx.txid().to_blob()) .bind(tx.compute_txid().to_blob())
.bind(next_output_index) .bind(next_output_index)
.bind(block_height) .bind(block_height)
.bind(txchain_item_id) .bind(txchain_item_id)
@ -2200,7 +2208,7 @@ async fn update_ido_txchain_tracker_block_height(
"UPDATE ido_txchain_tracker_map SET height = ? WHERE txid = ?" "UPDATE ido_txchain_tracker_map SET height = ? WHERE txid = ?"
) )
.bind(block_height) .bind(block_height)
.bind(tx.txid().to_blob()) .bind(tx.compute_txid().to_blob())
.execute(&mut **dbtx) .execute(&mut **dbtx)
.await?; .await?;
Ok(()) Ok(())
@ -2214,16 +2222,16 @@ async fn on_add_ido_tx(
tx: &Transaction, tx: &Transaction,
block_height: i64, block_height: i64,
) -> Result<()> { ) -> Result<()> {
debug!("IDO on_add_ido_tx: {}", blob_to_display_hex::<Txid>(&tx.txid())?); debug!("IDO on_add_ido_tx: {}", blob_to_display_hex::<Txid>(&tx.compute_txid().to_blob())?);
let mut dbtx = pool.begin().await?; let mut dbtx = pool.begin().await?;
let current_item = get_txchain_item_by_txid(pool, &tx.txid()) let current_item = get_txchain_item_by_txid(pool, &tx.compute_txid())
.await?; .await?;
if current_item.is_some() && Some(current_item.unwrap().id) == ido.txchain_head { if current_item.is_some() && Some(current_item.unwrap().id) == ido.txchain_head {
// already added, only update block height // already added, only update block height
update_ido_txchain_tracker_block_height(&mut dbtx, tx, block_height) update_ido_txchain_tracker_block_height(&mut dbtx, tx, block_height)
.await?; .await?;
} else if prev_txchain_item.id != ido.txchain_head.unwrap_or(0) { } else if prev_txchain_item.id != ido.txchain_head.unwrap_or(0) {
debug!("IDO rebuild: {}, prev: {}", blob_to_display_hex::<Txid>(&tx.txid())?, blob_to_display_hex::<Txid>(&prev_txchain_item.txid)?); debug!("IDO rebuild: {}, prev: {}", blob_to_display_hex::<Txid>(&tx.compute_txid().to_blob())?, blob_to_display_hex::<Txid>(&prev_txchain_item.txid)?);
// rebuild the state, txchain is broken, recreate the chain // rebuild the state, txchain is broken, recreate the chain
let items = get_ido_txchain_list(pool, ido.internal_id) let items = get_ido_txchain_list(pool, ido.internal_id)
.await?; .await?;
@ -2256,13 +2264,13 @@ async fn on_add_ido_tx(
let item_tx = items_tx_map.get(&item.txid).ok_or_else(|| anyhow::anyhow!("txchain tx not found in map"))?; 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)?;
context.head_txid = item_tx.txid().to_blob(); context.head_txid = item_tx.compute_txid().to_blob();
apply_updates_to_context(&mut context, &result.updates); apply_updates_to_context(&mut context, &result.updates);
updates.extend_from_slice(&result.updates); updates.extend_from_slice(&result.updates);
} }
// add the tx to the chain // add the tx to the chain
let result = ido_add_tx(&context, tx)?; let result = ido_add_tx(&context, tx)?;
context.head_txid = tx.txid().to_blob(); context.head_txid = tx.compute_txid().to_blob();
apply_updates_to_context(&mut context, &result.updates); apply_updates_to_context(&mut context, &result.updates);
updates.extend_from_slice(&result.updates); updates.extend_from_slice(&result.updates);
let txchain_item_id = upsert_ido_txchain(&mut dbtx, tx, ido.internal_id, Some(prev_txchain_item.id), block_height, result.next_output_index) let txchain_item_id = upsert_ido_txchain(&mut dbtx, tx, ido.internal_id, Some(prev_txchain_item.id), block_height, result.next_output_index)
@ -2301,7 +2309,7 @@ async fn on_add_ido_tx(
}; };
// add to the chain // add to the chain
let result = ido_add_tx(&context, tx)?; let result = ido_add_tx(&context, tx)?;
context.head_txid = tx.txid().to_blob(); context.head_txid = tx.compute_txid().to_blob();
apply_updates_to_context(&mut context, &result.updates); apply_updates_to_context(&mut context, &result.updates);
let txchain_item_id = upsert_ido_txchain(&mut dbtx, tx, ido.internal_id, Some(prev_txchain_item.id), block_height, result.next_output_index) let txchain_item_id = upsert_ido_txchain(&mut dbtx, tx, ido.internal_id, Some(prev_txchain_item.id), block_height, result.next_output_index)
.await?; .await?;
@ -2335,8 +2343,8 @@ async fn index_txs(
for tx in sorted_txs { for tx in sorted_txs {
// detect a new ido // detect a new ido
if is_preinit_broadcast(tx) { if is_preinit_broadcast(tx) {
debug!("IDO index preinit: {}", blob_to_display_hex::<Txid>(&tx.txid())?); debug!("IDO index preinit: {}", blob_to_display_hex::<Txid>(&tx.compute_txid().to_blob())?);
if lookup_internal_id_by_preinit_txid(pool, &tx.txid().to_blob()).await?.is_some() { if lookup_internal_id_by_preinit_txid(pool, &tx.compute_txid().to_blob()).await?.is_some() {
// already created (eg. indexed from the mempool), only update block height // already created (eg. indexed from the mempool), only update block height
let mut dbtx = pool.begin().await?; let mut dbtx = pool.begin().await?;
update_ido_txchain_tracker_block_height(&mut dbtx, tx, block_height) update_ido_txchain_tracker_block_height(&mut dbtx, tx, block_height)
@ -2346,11 +2354,11 @@ async fn index_txs(
} }
match on_create_ido(network, pool, tx, block_height) match on_create_ido(network, pool, tx, block_height)
.await { .await {
Err(err) => info!("failed to detect an ido, or an invalid ido detected, txid: {}, {}", hex::encode(tx.txid().to_blob()), err), Err(err) => info!("failed to detect an ido, or an invalid ido detected, txid: {}, {}", hex::encode(tx.compute_txid().to_blob()), err),
_ => (), _ => (),
} }
} else if is_ido_sig_in_tx_inputs(tx) { } else if is_ido_sig_in_tx_inputs(tx) {
debug!("IDO tx to index: {}", blob_to_display_hex::<Txid>(&tx.txid())?); debug!("IDO tx to index: {}", blob_to_display_hex::<Txid>(&tx.compute_txid().to_blob())?);
// has ido sig // has ido sig
for input_index in [ 0, 1, 3 ] { for input_index in [ 0, 1, 3 ] {
let input = tx.input.get(input_index); let input = tx.input.get(input_index);
@ -2368,7 +2376,7 @@ async fn index_txs(
Ok(Some(ido)) => { 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.compute_txid().to_blob()), err),
_ => (), _ => (),
} }
}, },
@ -2521,13 +2529,13 @@ mod tests {
let network = Some(Network::Chipnet); let network = Some(Network::Chipnet);
let result = parse_ido_preinit_tx(network, &tx, &mut errors, &mut invalid_ido_reasons); let result = parse_ido_preinit_tx(network, &tx, &mut errors, &mut invalid_ido_reasons);
if errors.len() > 0 { if errors.len() > 0 {
debug!("Parse ido preinit failed, txid: {}\nError(s):", hex::encode(tx.txid().to_blob())); debug!("Parse ido preinit failed, txid: {}\nError(s):", hex::encode(tx.compute_txid().to_blob()));
for error in &errors { for error in &errors {
debug!(" - {}", error); debug!(" - {}", error);
} }
} }
if invalid_ido_reasons.len() > 0 { if invalid_ido_reasons.len() > 0 {
debug!("Invalid ido found, preinit_txid: {}\nReason(s):", hex::encode(tx.txid().to_blob())); debug!("Invalid ido found, preinit_txid: {}\nReason(s):", hex::encode(tx.compute_txid().to_blob()));
for reason in &invalid_ido_reasons { for reason in &invalid_ido_reasons {
debug!(" - {}", reason); debug!(" - {}", reason);
} }

View file

@ -81,8 +81,8 @@ pub async fn update_mempool(
}) })
.await??; .await??;
let txs_to_delete: Vec<Txid> = our_mempool_txs.difference(&defi_txs).cloned().collect(); let txs_to_delete: Vec<Txid> = our_mempool_txs.difference(&cauldron_txs).cloned().collect();
let txs_to_add: Vec<&Txid> = defi_txs.difference(&our_mempool_txs).collect(); let txs_to_add: Vec<&Txid> = cauldron_txs.difference(&our_mempool_txs).collect();
let electrum_for_fetch = electrum.clone(); let electrum_for_fetch = electrum.clone();
let txids_to_fetch: Vec<Txid> = txs_to_add.iter().map(|t| **t).collect(); let txids_to_fetch: Vec<Txid> = txs_to_add.iter().map(|t| **t).collect();