@@ -28,9 +28,10 @@ pub use charms_client::{
2828 CURRENT_VERSION , NormalizedCharms , NormalizedSpell , NormalizedTransaction , Proof ,
2929 SpellProverInput , to_tx,
3030} ;
31- use charms_client:: { MOCK_SPELL_VK , bitcoin_tx:: BitcoinTx , tx:: Tx , well_formed } ;
31+ use charms_client:: { MOCK_SPELL_VK , bitcoin_tx:: BitcoinTx , tx:: Tx } ;
3232use charms_data:: {
33- App , AppInput , B32 , Charms , Data , TOKEN , Transaction , TxId , UtxoId , is_simple_transfer, util,
33+ App , AppInput , B32 , Charms , Data , TOKEN , Transaction , TxId , UtxoId , check, is_simple_transfer,
34+ util,
3435} ;
3536use charms_lib:: SPELL_VK ;
3637use const_format:: formatcp;
@@ -971,6 +972,39 @@ impl ProveSpellTx for ProveSpellTxImpl {
971972 }
972973}
973974
975+ pub fn ensure_exact_app_binaries (
976+ norm_spell : & NormalizedSpell ,
977+ app_private_inputs : & BTreeMap < App , Data > ,
978+ tx : & Transaction ,
979+ binaries : & BTreeMap < B32 , Vec < u8 > > ,
980+ ) -> anyhow:: Result < ( ) > {
981+ let required_vks: BTreeSet < _ > = norm_spell
982+ . app_public_inputs
983+ . iter ( )
984+ . filter ( |( app, data) | {
985+ !data. is_empty ( )
986+ || !app_private_inputs
987+ . get ( app)
988+ . is_none_or ( |data| data. is_empty ( ) )
989+ || !is_simple_transfer ( app, tx)
990+ } )
991+ . map ( |( app, _) | & app. vk )
992+ . collect ( ) ;
993+
994+ let provided_vks: BTreeSet < _ > = binaries. keys ( ) . collect ( ) ;
995+
996+ ensure ! (
997+ required_vks == provided_vks,
998+ "binaries must contain exactly the required app binaries.\n \
999+ Required VKs: {:?}\n \
1000+ Provided VKs: {:?}",
1001+ required_vks,
1002+ provided_vks
1003+ ) ;
1004+
1005+ Ok ( ( ) )
1006+ }
1007+
9741008pub fn ensure_no_zero_amounts ( norm_spell : & NormalizedSpell ) -> anyhow:: Result < ( ) > {
9751009 let apps: Vec < _ > = norm_spell
9761010 . app_public_inputs
@@ -1018,6 +1052,60 @@ fn ensure_all_prev_txs_are_present(
10181052 Ok ( ( ) )
10191053}
10201054
1055+ // NOTE: copied over from charms-spell-checker to make cherry-picked code work.
1056+ // This is in charms-client in later versions.
1057+ /// Check if the spell is correct.
1058+ fn spell_is_correct (
1059+ spell : & NormalizedSpell ,
1060+ prev_txs : & Vec < Tx > ,
1061+ app_input : Option < AppInput > ,
1062+ spell_vk : & str ,
1063+ tx_ins_beamed_source_utxos : & BTreeMap < UtxoId , UtxoId > ,
1064+ ) -> bool {
1065+ let prev_spells = charms_client:: prev_spells ( prev_txs, spell_vk, false ) ;
1066+
1067+ check ! ( charms_client:: well_formed(
1068+ spell,
1069+ & prev_spells,
1070+ tx_ins_beamed_source_utxos
1071+ ) ) ;
1072+
1073+ let Some ( prev_txids) = spell. tx . prev_txids ( ) else {
1074+ unreachable ! ( "the spell is well formed: tx.ins MUST be Some" ) ;
1075+ } ;
1076+ let all_prev_txids: BTreeSet < _ > = tx_ins_beamed_source_utxos
1077+ . values ( )
1078+ . map ( |u| & u. 0 )
1079+ . chain ( prev_txids)
1080+ . collect ( ) ;
1081+ check ! ( all_prev_txids == prev_spells. keys( ) . collect( ) ) ;
1082+
1083+ let apps = charms_client:: apps ( spell) ;
1084+
1085+ let charms_tx = charms_client:: to_tx ( spell, & prev_spells, tx_ins_beamed_source_utxos) ;
1086+ let tx_is_simple_transfer_or_app_contracts_satisfied =
1087+ apps. iter ( ) . all ( |app| is_simple_transfer ( app, & charms_tx) ) && app_input. is_none ( )
1088+ || app_input. is_some_and ( |app_input| apps_satisfied ( & app_input, & charms_tx) ) ;
1089+ check ! ( tx_is_simple_transfer_or_app_contracts_satisfied) ;
1090+
1091+ true
1092+ }
1093+
1094+ // NOTE: copied over from charms-spell-checker to make cherry-picked code work.
1095+ // This is in charms-client in later versions.
1096+ fn apps_satisfied ( app_input : & AppInput , tx : & Transaction ) -> bool {
1097+ let app_runner = charms_app_runner:: AppRunner :: new ( false ) ;
1098+ app_runner
1099+ . run_all (
1100+ & app_input. app_binaries ,
1101+ & tx,
1102+ & app_input. app_public_inputs ,
1103+ & app_input. app_private_inputs ,
1104+ )
1105+ . expect ( "all apps should run successfully" ) ;
1106+ true
1107+ }
1108+
10211109impl ProveSpellTxImpl {
10221110 pub fn validate_prove_request (
10231111 & self ,
@@ -1035,19 +1123,46 @@ impl ProveSpellTxImpl {
10351123 let prev_spells = charms_client:: prev_spells ( & prev_txs, SPELL_VK , self . mock ) ;
10361124
10371125 let tx = to_tx ( & norm_spell, & prev_spells, & tx_ins_beamed_source_utxos) ;
1038- // prove charms-app-checker run
1039- let cycles = AppRunner :: new ( true ) . run_all (
1040- & prove_request. binaries ,
1041- & tx,
1042- & norm_spell. app_public_inputs ,
1126+
1127+ ensure_exact_app_binaries (
1128+ & norm_spell,
10431129 & app_private_inputs,
1130+ & tx,
1131+ & prove_request. binaries ,
10441132 ) ?;
1045- let total_cycles = cycles. iter ( ) . sum ( ) ;
1046- ensure ! ( well_formed(
1047- & norm_spell,
1048- & prev_spells,
1049- & tx_ins_beamed_source_utxos
1050- ) ) ;
1133+
1134+ let app_input = match prove_request. binaries . is_empty ( ) {
1135+ true => None ,
1136+ false => Some ( AppInput {
1137+ app_binaries : prove_request. binaries . clone ( ) ,
1138+ app_public_inputs : norm_spell. app_public_inputs . clone ( ) ,
1139+ app_private_inputs : app_private_inputs. clone ( ) ,
1140+ } ) ,
1141+ } ;
1142+
1143+ ensure ! (
1144+ spell_is_correct(
1145+ & norm_spell,
1146+ & prev_txs,
1147+ app_input. clone( ) ,
1148+ SPELL_VK ,
1149+ & tx_ins_beamed_source_utxos,
1150+ ) ,
1151+ "spell verification failed"
1152+ ) ;
1153+
1154+ // Calculate cycles for fee estimation
1155+ let total_cycles = if let Some ( app_input) = & app_input {
1156+ let cycles = AppRunner :: new ( true ) . run_all (
1157+ & app_input. app_binaries ,
1158+ & tx,
1159+ & norm_spell. app_public_inputs ,
1160+ & app_input. app_private_inputs ,
1161+ ) ?;
1162+ cycles. iter ( ) . sum ( )
1163+ } else {
1164+ 0
1165+ } ;
10511166
10521167 match prove_request. chain . as_str ( ) {
10531168 BITCOIN => {
0 commit comments