Migrate off the deprecated bitcoincash 0.29 API (Amount/Version types, FromHex -> FromStr/parse, non-exhaustive Network) across the indexer and tests. Add indexing of native token-A <-> token-B (TokenToken) AMM pools: - tokentoken_pool / tokentoken_pool_history_entry tables in cauldron.db, created via an always-run idempotent migration (no DB_VERSION bump, so existing databases upgrade in place) - block-path indexing sharing the cauldron write transaction and reorg undo, with creation/swap/withdrawal state tracking and reserve deltas - mempool indexing: electrum mempool.get filters on the tokentoken contract code (spends) and the CONJURE op_return hint (creations); first_seen_timestamp reconciles with mtp on confirmation - RPC endpoints /tokentoken/pool/active (pair lookup, order-insensitive) and /tokentoken/tokens Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
49 lines
3.2 KiB
Rust
49 lines
3.2 KiB
Rust
// Copyright (C) 2024-2026 Whiterun LLC
|
|
//
|
|
// This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later.
|
|
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use bitcoin_hashes::Hash;
|
|
use bitcoincash::{TokenID, Transaction};
|
|
|
|
pub fn is_genesis_tx(tx: &Transaction) -> Option<TokenID> {
|
|
let potential_token_ids: HashSet<[u8; 32]> = tx
|
|
.input
|
|
.iter()
|
|
.filter(|i| i.previous_output.vout == 0)
|
|
.map(|i| i.previous_output.txid.to_byte_array())
|
|
.collect();
|
|
|
|
for o in &tx.output {
|
|
match &o.token {
|
|
Some(token) => {
|
|
if potential_token_ids.contains(&token.id.to_byte_array()) {
|
|
return Some(token.id);
|
|
}
|
|
}
|
|
None => continue,
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use bitcoincash::consensus::deserialize;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_is_genesis_tx() {
|
|
let tx_hex = "01000000032b70d6279de034fea76d4dd329c44a3e162b17a0cebc32eeeebbd14f5e8a97250100000064411b42a9dcb22c59ff0e800b3c63d6b8ac045bd18eb1effc8d2f23ec49ed0abdc316215a469a206d45153eac999d14eb8abc3d33b91c5ac4b61e7868d162d5d5184121021735db91ac477c1007da74c8c117c383eebc664d21f12871ca6e69401f8072f8feffffff2b70d6279de034fea76d4dd329c44a3e162b17a0cebc32eeeebbd14f5e8a972502000000644193e4d5bb533279fbcbebe15ba62f226e53e5c64a1624c755b255b13f0b1011e382182896026575370b5e30d03390d31faabf854a9f41c215b8a5bb57bae221df4121021735db91ac477c1007da74c8c117c383eebc664d21f12871ca6e69401f8072f8feffffff8d80b5567b6a3cac6e19a6934fd7544a02616fd6868a304c93bff6aed3eb12280100000064418b576b5defc8942182d01654cc2a0706b3e86e9031f7a13a694ffd830944ca00ecf5ad3271a242993580cbe78c13a41e35b06dfd68e72c4242dd7ff9e7e039a6412103b666cf8bc6ef2e3c3f100fc883013040f01f7085d75e5ab0811ae00b6a0a6d24feffffff03200300000000000040ef8d80b5567b6a3cac6e19a6934fd7544a02616fd6868a304c93bff6aed3eb1228600401db010076a914bfe9ed4ea9c7830c2ee96c17a3bd5ff563c12f2b88ac200300000000000040ef8d80b5567b6a3cac6e19a6934fd7544a02616fd6868a304c93bff6aed3eb1228600402db010076a914bfe9ed4ea9c7830c2ee96c17a3bd5ff563c12f2b88ac9f117901000000001976a91402a67e8d884367e7a22d6cda96a6df21e56a2f3288ac851b0c00";
|
|
let not_genesis_tx: Transaction = deserialize(&hex::decode(tx_hex).unwrap()).unwrap();
|
|
|
|
let tx_hex = "0200000001925bd92e424c0f0bc290a794f491abf19e61b6dcdec7e70747fcb54682fc9bb700000000644181707613f45069c8df981b2ef0cbd05158d5623602fac3ae2e13f69d05fa356c278f2f3a4e34f33a79e0f8375abd35051150ec024b3625807cea185528a5de9e412103b4680dffa1e34b3bfdb024fd0a8498eb24f59ba5ead34acfb74f9d8549db6f3a0000000003e8030000000000003eef925bd92e424c0f0bc290a794f491abf19e61b6dcdec7e70747fcb54682fc9bb710fdf40176a914bd4c90f2c64743fc0d3ea6a14973a5d628260b7388ac0000000000000000456a0442434d5220c705cc90a56ac7ef9a15ef90ebbc8ba7e60e4c622e5464d52d8baf7887949fcc1d736f636b2e6361756c64726f6e2e71756573742f62636d722e6a736f6ed9210000000000001976a914bd4c90f2c64743fc0d3ea6a14973a5d628260b7388ac00000000";
|
|
let genesis_tx: Transaction = deserialize(&hex::decode(tx_hex).unwrap()).unwrap();
|
|
|
|
assert!(is_genesis_tx(¬_genesis_tx).is_none());
|
|
assert!(is_genesis_tx(&genesis_tx).is_some());
|
|
}
|
|
}
|