charms 13.0.0

Programmable assets on Bitcoin and beyond
Documentation
pub mod prove;
pub mod prove_spell_tx;
pub mod request;
mod validate;

// Re-export public API to preserve existing `crate::spell::*` imports.
pub use prove::{MockProver, Prove, Prover};
pub use prove_spell_tx::{ProveSpellTx, ProveSpellTxImpl, committed_data_hash};
pub use request::{CharmsFee, FeeAddressForNetwork, ProveRequest};
pub use validate::{
    adjust_coin_contents, ensure_all_prev_txs_are_present, ensure_exact_app_binaries,
};

pub use charms_client::{
    BeamSource, CURRENT_VERSION, NormalizedCharms, NormalizedSpell, Proof, SpellProverInput, to_tx,
};

use anyhow::Context;
use bitcoin::Amount;
use charms_client::tx::Tx;
use charms_data::{App, Data};
use serde::Deserialize;
use serde_with::{DisplayFromStr, serde_as};
use std::collections::BTreeMap;

/// Private inputs to apps, deserializable from a standalone YAML/JSON file.
#[serde_as]
#[derive(Clone, Debug, Deserialize)]
struct AppPrivateInputs(#[serde_as(as = "BTreeMap<DisplayFromStr, _>")] BTreeMap<App, Data>);

pub fn read_private_inputs(path: &std::path::Path) -> anyhow::Result<BTreeMap<App, Data>> {
    let data = std::fs::read(path)?;
    let inputs: AppPrivateInputs = serde_yaml::from_slice(&data)?;
    Ok(inputs.0)
}

pub fn from_strings(prev_txs: &[String]) -> anyhow::Result<Vec<Tx>> {
    prev_txs
        .iter()
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .map(|tx_hex| {
            Tx::try_from(tx_hex)
                .context("failed to convert from hex")
                .or_else(|_| serde_json::from_str(tx_hex).context("failed to convert from JSON"))
                .or_else(|_| serde_yaml::from_str(tx_hex).context("failed to convert from YAML"))
        })
        .collect()
}

pub fn get_charms_fee(charms_fee: &Option<CharmsFee>, total_cycles: u64) -> Amount {
    charms_fee
        .as_ref()
        .map(|charms_fee| {
            Amount::from_sat(total_cycles * charms_fee.fee_rate / 1000000 + charms_fee.fee_base)
        })
        .unwrap_or_default()
}

#[cfg(test)]
mod test {
    use super::*;
    use charms_client::{cardano_tx::CardanoTx, tx::EnchantedTx};

    #[test]
    fn txs_from_strings() {
        let b_tx_hex = "!bitcoin {tx: 0200000000010236c4d581d18b974562cf32e835c2c1e5d2590cd7c070e5ece35ebcb672216ae00000000000ffffffff36c4d581d18b974562cf32e835c2c1e5d2590cd7c070e5ece35ebcb672216ae00400000000ffffffff042302000000000000160014b9698eae2e61774bd88749e0f9b169acc50f99fb23020000000000001600144b1f883dd56c33fb7300dc27fc125eab88a462cf0000000000000000fd01036a057370656c6c4df70282a36776657273696f6e09627478a2646f75747382a1001a05f5e100a1001b00000001954fc4006b6265616d65645f6f757473a1009820189b18d30f18460718be1833181b18bc18a518b318ae18fa18bc18c9185b18b1185618c2185218e618a912182718d318cb18ac182a18b6184418ab189e716170705f7075626c69635f696e70757473a18361749820183d187f18e718e418ce18a6121819184718af187318d70e1851181918be18bd188a18a518b718ed18fe187418bf18af186e1877189a1818184718bd189b982018c9187518d418e018c2189218fb189518ef18bd18a518c118331218d618ac181d188b185a18ef18f718f018f118e518571886184518a218da187018ff185ff699010418a41859184c185909186c18de186e18ca18a818a618b81318201841185118df1827183718ad18ed18501866185418f1182f18e41861185c08182d185318da182418dc1883181a187918b018c6183c0f02185218c416186818c918a518c91865182118a618af18fc184018e218ba18f518cd181b1318dc18a0184d184a18e418da0b188c18c0184718691835185f18e018b218ce18ba189b18fe188118d918d4185b1845183b18e5188218b818f71865182f0218bb185318330e187518581819181909182d18cd18271898188b182f18d018571881183b18e218d818e918b4185418ed184c1832186e091218e1030918fd18e7187d187c0d1820187b18f5182118621834182818ff183b187c18c71867186b183818631876182618d918e218a018d918ad121893181d18f5181918a913184e183700171818182d189e18b118520418b1187718a318e818ea1882184118fd183818830e181a18b3184018cf18ab18d7185c18a218e3182e1825183718fb18a50b184718b6185b184e001866188e184c1898188518b518a8183e189618e0185d15186718c8185c18e3188618cf18f6189718b418d5184f186618df040d00183f18351855188918c1182b18c9181d18dc0418d31882186114181b188817181b185018e318d618ae1847182116186b18b618d8185f1877db0e000000000000160014542e0cd4e07fa3d919cf5aa5f8242612d4a3b3550247304402202bd582e27041fc7ae426853826e9c52cf8a5c8e5755026a6ba21892bdbcf51cb02201fef093148d7e5fa8ad7a10c8f953398db4a7efca4003d6a5c7cbbb58e468d84012103d5453d402d158c84de22dd20caf3cc1968178b5f674f5ec6d063d9fe8675fb27024730440220344cf2c2812b1c086c931bb6c1643bc91f33f4347011c594c37f54c4b23ea16a022049fad130880b7e88fc20e267cf151e23d32ed2669d94937e066658defea8c97b012103d5453d402d158c84de22dd20caf3cc1968178b5f674f5ec6d063d9fe8675fb2700000000, proof: 00000020e67d17e26a835ef6908dc53d3df10ec11447ff2e2cf401000000000000000000a5edec1fb14884f4d91dd3670135e4351aaec3c8e976b9ed13318d367875197d9d487c69a1fc01179e3678ef670b00000de18f42fa5fbce803e39c85b79c0b12e3ac41aaf57391184e96718e7f5406862614b4b7625aab6d003128248c31431c16e93e2cda808d7a9a63eaacfa79ae6a175578cb388029709c71f5590c03e6d254cd7068910251f30c10ceaba4f3748e46d6184d3b3dd743ef6033f2664830103881059ae8c26e792592e4b2db93acb6ab0e01f2312e73d2fb918b1977ac45c631066a1fbff4cededc4394c8eea8c1f6994fcf8eac574db5bcca603e56d142dca3a07fd83733faeb164e700fd82a3e821da3332e71ad3cd8f0f3843f8f80f16c9c668d03c6fd76fbcf845dced13a83708d29f1ba8550143e4841a10205a30202ec2ca8163a5e3fef928536e64612110b86ecd1cbbfe8c1c496346a46760edc33723e472ed9d8b35dd30f9dbefa4cac3f86f8eec2bc997cbef3136cbe4a7f21303efa55e7b04ac5bd1532419b82cc752ba53306d2a97e3f498d53ddcfe95c87e831c780ed513c3438b5e64db4d98d67796fba6fbb641ff92c51aaecb15d6df48b7993d966a3df0fe066e5a3dabe2e4ce639d5178235b0e05389a6d12cb4be8acf7770f955a73a7844732561c60f84cae52f04ebab0600, headers: [0020092042685204c1d593020a0e4a04fcaae7cbd146662c105c010000000000000000000f9d569050c3db02605efe19a24f50ebb6cc0567154662993ff682aaeeb8d9fccf487c69a1fc0117c8e0d65a, 00600624fe6344692b6693004e3ba99eb240402e4061ac015ef9000000000000000000008cc52b45e82d205b486314723fe865f196e84ec4fb247ddbea5b42e8fda23351fc4a7c69a1fc01178d3ae452, 0000eb23416b11e4c80cbb1d03c22b7957e108ccb958aef2fdc70100000000000000000013e9da44cbd9dd81b8c4269a5c8a88e850fb5af075e714991504d4e815fe95ac184b7c69a1fc011714370fe6, 00000034c603010c44c62e293b4e9476f4176dcd50c590c942a201000000000000000000b79acf6981a4cadcac7b843e4e4c7f155f87fc6add4ce2ea800fb77b4685cb26bd4e7c69a1fc01177e66ec4c, 04a0da26722d4343dc67afbc27aacff592d9ab2a0f7584df56fb01000000000000000000c315a0ca900f1d334523a4e5aeffbae7f95d69e60eb12c331e7a2ad5da2870fe5f507c69a1fc0117932dd198, 0080072070d9db116f24106e39b4da8a205725cfc701cfe141d400000000000000000000ee5e2b33f543016f8588935dbe972f36365b6db1c663d883f703f7e4f69c2426c8507c69a1fc01173560a4cb]}".to_string();

        let txs = from_strings(&[b_tx_hex]).unwrap();
        let Tx::Bitcoin(tx) = &txs[0] else {
            unreachable!()
        };
        assert!(tx.proven_final());
    }

    #[test]
    fn tx_from_string_cardano() {
        let b_tx_hex = "!cardano {tx: 84a900d9010283825820632eaa5275f8100a018606c2f3bfd35127d78a5e37bcee340a0bd232cc7aee95008258207a67e71100f64ea4a77dcd04c0bd09a4e478d83c10c788b1c6c12bb2cfa1f35e00825820ce50f4fff47d1ab9c604049c35adbd9a29e283c9f2514466c431273d4c08cf9f000184a200581d716fc738a0bf8ac27febd3749637068f289d944caaba131fd859c68c4a01821a003d0900a2581cc48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ada1480014df105553444d1a00989680581c279c909f348e533da5808898f87f9a14bb2c3dfbbacccd631d927a3fa144534e454b1903e8a200583901466609ab4003d2a479cc0c3d3c24c4e576d550670c417266066288b3a983900c94aa06dea303d2c70cc955c054d98fe2c347d2e318d2bcd5011a001e8480a300583901466609ab4003d2a479cc0c3d3c24c4e576d550670c417266066288b3a983900c94aa06dea303d2c70cc955c054d98fe2c347d2e318d2bcd5011a00635351028201d8185904525f584082a36776657273696f6e0b627478a2646f75747382a100f6a3011a003d0900021a00989680031903e86b6265616d65645f6f757473a101982000189f18b4188958401880182618bc18a018a502184518e4181e18e718cc1876181e1018f6183f183118bb185b18801829189818231864187818ff18e018af716170705f7075626c695840635f696e70757473a483616398200000000000000000000000000000000000000000000000000000000000000000982018e7187718540718bb181a18e41618d358400b0518eb18cf18af18f4184b186b12031828183718cc182b1868182f0b18e218ee185918c718c118d5f683617498200000000000000000000000001833184518584052184a18bf186b18be18180918441892182418b51897182c184118790b186c18f2982018e7187718540718bb181a18e41618d30b0518eb18cf18af18f4184b1858406b12031828183718cc182b1868182f0b18e218ee185918c718c118d5f683617498200000000000000000000000001852185218d9181e18d6185318aa187a18405840187e18ed1833186d18ca184d18261870182318860a982018e7187718540718bb181a18e41618d30b0518eb18cf18af18f4184b186b12031828183718cc182b18584068182f0b18e218ee185918c718c118d5f68361749820000000000000000000000000187918cd18c718ab16187018d318831882051869187d18b41218f518b4185840ce188618f018ae982018e7187718540718bb181a18e41618d30b0518eb18cf18af18f4184b186b12031828183718cc182b1868182f0b18e218ee185918c718c1584018d5f699010418a41859184c1859120e1718621824171824187218be183d18d8186918cb185e18e2091885183918ad18331819189718fb05186a189b185a189a584018871118b0189e17184a09181a18c4131618d9185c188b18a018a6187f18c918a409183918e918ce18b314184c189d18521857184209181e18641821189718a858401318c318aa1865188f18cc1864186f18801891184e18ce161827182d188818c518e718c5187f1869189a18cb188e18771618d818f7187e18bc182b186d04181e584010187c183218521887182a182e1860188e187c187318fb18be182018ef189218721874184618c418350d1836182618f018da18ff184418cf18391824186d183c5840187718731618c018bf1818188d18c01860186018a318b218d9184f186718731898182a1897184818f8188e186d18da18e7181c18821879186c1828186c18ab1858409415189018bb18ac18c018750818dd183f188218cd182918f5186618c618a0187d1888183718e8182d185218ac183b188e18dd186b18db06188b187e182e1898584018c6184118ea187e0f0118d818860b18ff187418e0186918f01869182b188418d81888189118f7184e18ce186918a1188218cd071869189018b918f0183b18bc582e1864181a18b218730618e418b904188018ba185e18f518e118a918c41890187518ff18d618b20b185f1875187e10ffa200583901466609ab4003d2a479cc0c3d3c24c4e576d550670c417266066288b3a983900c94aa06dea303d2c70cc955c054d98fe2c347d2e318d2bcd5011a0562c949021a0004c81505a1581df129764648940a3b7208bc99a246bc96a69817bea017560972432f076f000b58207af9e369d7769a0e49c88c064cd26c0cfe1a0ce6505d354bf5f3819bcc8358b40dd90102818258203cf7822be2f4e9eaeeed3c242f484d3789d6e973fecc8a16df154cbb644edadb000ed9010281581c15bf560dabf4fe7f7ef78ac49c4fa846ebcde7009b1e886dd70d350d0f0112d901028182582049d1f96be0002bcd0241917142aa6a58344923eda8ed54fb46da4ec5f4e3bff200a200d901028282582030e99359bc028dbf5a369df63744eb2a2e0e99512d8f6bdb0124ef2f5c7cf80a58407807b8bac400b1ca0b0f58ebea912eeafb694e5a0e050f884dee6daaf992ee70ba3780a67cda29c7850b48c735a1c1d378b8b4b22ee0fa381dd679a511726603825820720c93bc822efbf4aabccaa7ae875cd46a556c99bc1b3f2fbdd8c561f4723d535840f97824c411e43c5146c797ee379834b727973d6ec1213bc3eea834867530c0e2fa977fa6f41f3a41d757bb058371c232e680214246e48ad9f975cb4548ed5200058184030044830002018219c3501a01c9c380f5f6, signature: a56c741f41832345616e99a820e40c67543deffa3bf5682412ec6213f6153e6ff3e40a9b5c9944fbfa093c217a693a9197c6c0a535911746048f7700ac602d02}".to_string();

        let txs = from_strings(&[b_tx_hex]).unwrap();
        let Tx::Cardano(tx) = &txs[0] else {
            unreachable!()
        };
        assert!(tx.proven_final());

        let CardanoTx::WithFinalityProof { tx, .. } = tx else {
            unreachable!()
        };
        let str = serde_yaml::to_string(&tx.body.outputs[0].amount()).unwrap();
        eprintln!("{}", str)
    }
}