Skip to content

Commit

Permalink
feat: Generate dynamic arguments / witnesses
Browse files Browse the repository at this point in the history
Create dynamic example programs based on the set of keys / hashes and
based on the transaction environment (sighash). Greet the user with a
program that runs on startup.
  • Loading branch information
uncomputable committed Nov 25, 2024
1 parent 6f80825 commit 11ed946
Show file tree
Hide file tree
Showing 7 changed files with 404 additions and 127 deletions.
13 changes: 8 additions & 5 deletions src/components/app.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use leptos::{component, provide_context, view, IntoView, RwSignal};

use super::program_window::{Program, ProgramWindow, Runtime};
use super::program_window::{select_example, Program, ProgramWindow, Runtime};
use crate::components::footer::Footer;
use crate::components::run_window::{HashCount, KeyCount, RunWindow, SignedData, TxEnv};
use crate::components::state::LocalStorage;
use crate::examples;
use crate::transaction::TxParams;
use crate::util::{HashedData, SigningKeys};

Expand All @@ -12,21 +13,23 @@ pub struct ActiveRunTab(pub RwSignal<&'static str>);

#[component]
pub fn App() -> impl IntoView {
let signing_keys = SigningKeys::load_from_storage().unwrap_or_default();
let program = Program::load_from_storage()
.unwrap_or_else(|| Program::new_p2pk(signing_keys.public_keys[0]));
provide_context(signing_keys);
let program = Program::load_from_storage().unwrap_or_default();
provide_context(program);
let tx_params = TxParams::load_from_storage().unwrap_or_default();
let tx_env = TxEnv::new(program, tx_params);
provide_context(tx_env);
provide_context(SigningKeys::load_from_storage().unwrap_or_default());
provide_context(SignedData::new(tx_env.lazy_env));
provide_context(HashedData::load_from_storage().unwrap_or_default());
provide_context(KeyCount::load_from_storage().unwrap_or_default());
provide_context(HashCount::load_from_storage().unwrap_or_default());
provide_context(Runtime::new(program, tx_env.lazy_env));
provide_context(ActiveRunTab::default());

if program.is_empty() {
select_example(examples::get("✍️️ P2PK").expect("P2PK example should exist"))
}

view! {
<ProgramWindow />
<RunWindow />
Expand Down
37 changes: 27 additions & 10 deletions src/components/program_window/examples_dropdown.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
use leptos::{component, use_context, view, IntoView, SignalSet, SignalUpdate};
use leptos::{component, use_context, view, IntoView, SignalGetUntracked, SignalSet, SignalUpdate};

use crate::components::app::ActiveRunTab;
use crate::components::dropdown::Dropdown;
use crate::components::program_window::Program;
use crate::components::run_window::TxEnv;
use crate::components::run_window::{SignedData, TxEnv};
use crate::examples::Example;
use crate::util::{HashedData, SigningKeys};

#[component]
pub fn ExamplesDropdown() -> impl IntoView {
pub fn select_example(example: Example) {
let program = use_context::<Program>().expect("program should exist in context");
let tx_env = use_context::<TxEnv>().expect("transaction environment should exist in context");
let active_run_tab =
use_context::<ActiveRunTab>().expect("active run tab should exist in context");
let signing_keys = use_context::<SigningKeys>().expect("signing keys should exist in context");
let signed_data = use_context::<SignedData>().expect("signed data should exist in context");
let hashed_data = use_context::<HashedData>().expect("hashed data should exist in context");

tx_env.params.set(example.params());
let arguments = example.arguments(&signing_keys.public_keys, &hashed_data.hashes);
let program_text = format!("{arguments}\n\n{}", example.template_text());
program.text.set(program_text.clone());
program.update_on_read();

let witness = example.witness(
&signing_keys.secret_keys,
&hashed_data.preimages,
signed_data.sighash_all.get_untracked(),
);
let satisfied_text = format!("{witness}\n\n{}", program_text);
program.text.set(satisfied_text);
active_run_tab.0.update(|_| {}); // refresh active tab
}

#[component]
pub fn ExamplesDropdown() -> impl IntoView {
let examples = crate::examples::keys().collect::<Vec<&'static str>>();
let select_example = move |selected| match crate::examples::get(selected) {
Some(example) => {
program.text.set(example.program_text().to_string());
program.update_on_read();
tx_env.params.set(example.params());
active_run_tab.0.update(|_| {}); // refresh active tab
}
Some(example) => select_example(example),
None => {
// do nothing
}
Expand Down
1 change: 1 addition & 0 deletions src/components/program_window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use self::share_button::ShareButton;
use self::transaction_button::TransactionButton;
use crate::components::toolbar::Toolbar;

pub use self::examples_dropdown::select_example;
pub use self::program_tab::{Program, Runtime};

#[component]
Expand Down
41 changes: 19 additions & 22 deletions src/components/program_window/program_tab.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::sync::Arc;

use hex_conservative::DisplayHex;
use itertools::Itertools;
use leptos::{
component, create_node_ref, create_rw_signal, ev, event_target_value, html, spawn_local,
use_context, view, IntoView, NodeRef, RwSignal, Signal, SignalGetUntracked, SignalSet,
SignalUpdate, SignalWith, SignalWithUntracked,
};
use simfony::elements::secp256k1_zkp as secp256k1;
use simfony::parse::ParseFromStr;
use simfony::simplicity::jet::elements::ElementsEnv;
use simfony::{elements, simplicity};
Expand All @@ -24,6 +22,12 @@ pub struct Program {
lazy_satisfied: RwSignal<Result<SatisfiedProgram, String>>,
}

impl Default for Program {
fn default() -> Self {
Self::new(String::default())
}
}

impl Program {
pub fn new(text: String) -> Self {
let program = Self {
Expand All @@ -36,21 +40,8 @@ impl Program {
program
}

pub fn new_p2pk(key: secp256k1::XOnlyPublicKey) -> Self {
let text = format!(
r#"mod witness {{
const SIG: Signature = 0x1d7d93f350e2db564f90da49fb00ee47294bb6d8f061929818b26065a3e50fdd87e0e8ab45eecd04df0b92b427e6d49a5c96810c23706566e9093c992e075dc5; // TODO: update this
}}
fn main() {{
let pk: Pubkey = 0x{};
let msg: u256 = jet::sig_all_hash();
jet::bip_0340_verify((pk, msg), witness::SIG)
}}"#,
key.serialize().as_hex()
);

Self::new(text)
pub fn is_empty(&self) -> bool {
self.text.with_untracked(String::is_empty)
}

pub fn cmr(self) -> Result<simplicity::Cmr, String> {
Expand All @@ -73,13 +64,19 @@ fn main() {{
}
self.text.with_untracked(|text| {
self.cached_text.set(text.clone());
let compiled = CompiledProgram::new(text.as_str(), simfony::Arguments::default());
self.lazy_cmr
.set(compiled.clone().map(|x| x.commit().cmr()));
self.lazy_satisfied.set(compiled.and_then(|x| {
let compiled = simfony::Arguments::parse_from_str(text)
.map_err(|error| error.to_string())
.and_then(|args| CompiledProgram::new(text.as_str(), args));
let cmr = compiled
.as_ref()
.map(|x| x.commit().cmr())
.map_err(Clone::clone);
self.lazy_cmr.set(cmr);
let satisfied = compiled.and_then(|x| {
let witness = WitnessValues::parse_from_str(text)?;
x.satisfy(witness)
}));
});
self.lazy_satisfied.set(satisfied);
});
}
}
Expand Down
1 change: 0 additions & 1 deletion src/components/run_window/key_store_tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ pub enum SignedDataMode {
pub struct SignedData {
pub mode: RwSignal<SignedDataMode>,
pub thirty_two_bytes: RwSignal<[u8; 32]>,
#[allow(dead_code)]
pub sighash_all: Signal<secp256k1::Message>,
pub hash_preimage_bytes: RwSignal<Vec<u8>>,
pub message: Signal<secp256k1::Message>,
Expand Down
Loading

0 comments on commit 11ed946

Please sign in to comment.