-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
642 additions
and
307 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,12 @@ authors = [ | |
"Nikita Chashchinskii <[email protected]>" | ||
] | ||
edition = "2021" | ||
version = "0.5.0" | ||
version = "0.5.1" | ||
|
||
[workspace.dependencies.bip300301] | ||
git = "https://github.com/Ash-L2L/bip300301.git" | ||
rev = "c6e410e702f3d22f5801f21ffdf39edece3985df" | ||
|
||
|
||
[profile.release] | ||
lto = "fat" | ||
# lto = "fat" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use eframe::egui; | ||
use strum::{EnumIter, IntoEnumIterator}; | ||
|
||
use crate::app::App; | ||
|
||
mod tx_builder; | ||
mod tx_creator; | ||
mod utxo_creator; | ||
mod utxo_selector; | ||
|
||
use tx_builder::TxBuilder; | ||
|
||
#[derive(Default, EnumIter, Eq, PartialEq, strum::Display)] | ||
enum Tab { | ||
#[default] | ||
#[strum(to_string = "Transaction Builder")] | ||
TransactionBuilder, | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct Coins { | ||
tab: Tab, | ||
tx_builder: TxBuilder, | ||
} | ||
|
||
impl Coins { | ||
pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { | ||
egui::TopBottomPanel::top("coins_tabs").show(ui.ctx(), |ui| { | ||
ui.horizontal(|ui| { | ||
Tab::iter().for_each(|tab_variant| { | ||
let tab_name = tab_variant.to_string(); | ||
ui.selectable_value(&mut self.tab, tab_variant, tab_name); | ||
}) | ||
}); | ||
}); | ||
egui::CentralPanel::default().show(ui.ctx(), |ui| match self.tab { | ||
Tab::TransactionBuilder => { | ||
let () = self.tx_builder.show(app, ui).unwrap(); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
use std::collections::HashSet; | ||
|
||
use eframe::egui; | ||
|
||
use thunder::{ | ||
bip300301::bitcoin, | ||
types::{GetValue, Transaction}, | ||
}; | ||
|
||
use super::{ | ||
tx_creator::TxCreator, | ||
utxo_creator::UtxoCreator, | ||
utxo_selector::{show_utxo, UtxoSelector}, | ||
}; | ||
use crate::app::App; | ||
|
||
#[derive(Debug, Default)] | ||
pub struct TxBuilder { | ||
// regular tx without extra data or special inputs/outputs | ||
base_tx: Transaction, | ||
tx_creator: TxCreator, | ||
utxo_creator: UtxoCreator, | ||
utxo_selector: UtxoSelector, | ||
} | ||
|
||
impl TxBuilder { | ||
pub fn show_value_in(&mut self, app: &mut App, ui: &mut egui::Ui) { | ||
ui.heading("Value In"); | ||
let selected: HashSet<_> = self | ||
.base_tx | ||
.inputs | ||
.iter() | ||
.map(|(outpoint, _)| *outpoint) | ||
.collect(); | ||
let utxos_read = app.utxos.read(); | ||
let mut spent_utxos: Vec<_> = utxos_read | ||
.iter() | ||
.filter(|(outpoint, _)| selected.contains(outpoint)) | ||
.collect(); | ||
let value_in: u64 = spent_utxos | ||
.iter() | ||
.map(|(_, output)| output.get_value()) | ||
.sum(); | ||
self.tx_creator.value_in = value_in; | ||
spent_utxos.sort_by_key(|(outpoint, _)| format!("{outpoint}")); | ||
ui.separator(); | ||
ui.monospace(format!("Total: {}", bitcoin::Amount::from_sat(value_in))); | ||
ui.separator(); | ||
egui::Grid::new("utxos").striped(true).show(ui, |ui| { | ||
ui.monospace("kind"); | ||
ui.monospace("outpoint"); | ||
ui.monospace("value"); | ||
ui.end_row(); | ||
let mut remove = None; | ||
for (vout, (outpoint, _)) in self.base_tx.inputs.iter().enumerate() | ||
{ | ||
let output = &utxos_read[outpoint]; | ||
show_utxo(ui, outpoint, output); | ||
if ui.button("remove").clicked() { | ||
remove = Some(vout); | ||
} | ||
ui.end_row(); | ||
} | ||
if let Some(vout) = remove { | ||
self.base_tx.inputs.remove(vout); | ||
} | ||
}); | ||
} | ||
|
||
pub fn show_value_out(&mut self, ui: &mut egui::Ui) { | ||
ui.heading("Value Out"); | ||
ui.separator(); | ||
let value_out: u64 = | ||
self.base_tx.outputs.iter().map(GetValue::get_value).sum(); | ||
self.tx_creator.value_out = value_out; | ||
ui.monospace(format!( | ||
"Total: {}", | ||
bitcoin::Amount::from_sat(value_out) | ||
)); | ||
ui.separator(); | ||
egui::Grid::new("outputs").striped(true).show(ui, |ui| { | ||
let mut remove = None; | ||
ui.monospace("vout"); | ||
ui.monospace("address"); | ||
ui.monospace("value"); | ||
ui.end_row(); | ||
for (vout, output) in self.base_tx.outputs.iter().enumerate() { | ||
let address = &format!("{}", output.address)[0..8]; | ||
let value = bitcoin::Amount::from_sat(output.get_value()); | ||
ui.monospace(format!("{vout}")); | ||
ui.monospace(address.to_string()); | ||
ui.with_layout( | ||
egui::Layout::right_to_left(egui::Align::Max), | ||
|ui| { | ||
ui.monospace(format!("{value}")); | ||
}, | ||
); | ||
if ui.button("remove").clicked() { | ||
remove = Some(vout); | ||
} | ||
ui.end_row(); | ||
} | ||
if let Some(vout) = remove { | ||
self.base_tx.outputs.remove(vout); | ||
} | ||
}); | ||
} | ||
|
||
pub fn show( | ||
&mut self, | ||
app: &mut App, | ||
ui: &mut egui::Ui, | ||
) -> anyhow::Result<()> { | ||
egui::SidePanel::left("spend_utxo") | ||
.exact_width(250.) | ||
.resizable(false) | ||
.show_inside(ui, |ui| { | ||
self.utxo_selector.show(app, ui, &mut self.base_tx); | ||
}); | ||
egui::SidePanel::left("value_in") | ||
.exact_width(250.) | ||
.resizable(false) | ||
.show_inside(ui, |ui| { | ||
let () = self.show_value_in(app, ui); | ||
}); | ||
egui::SidePanel::left("value_out") | ||
.exact_width(250.) | ||
.resizable(false) | ||
.show_inside(ui, |ui| { | ||
let () = self.show_value_out(ui); | ||
}); | ||
egui::SidePanel::left("create_utxo") | ||
.exact_width(450.) | ||
.resizable(false) | ||
.show_separator_line(false) | ||
.show_inside(ui, |ui| { | ||
self.utxo_creator.show(app, ui, &mut self.base_tx); | ||
ui.separator(); | ||
self.tx_creator.show(app, ui, &mut self.base_tx).unwrap(); | ||
}); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use eframe::egui; | ||
|
||
use thunder::{ | ||
bip300301::bitcoin, | ||
types::{Transaction, Txid}, | ||
}; | ||
|
||
use crate::app::App; | ||
|
||
#[derive(Debug, Default)] | ||
pub struct TxCreator { | ||
pub value_in: u64, | ||
pub value_out: u64, | ||
// if the base tx has changed, need to recompute final tx | ||
base_txid: Txid, | ||
final_tx: Option<Transaction>, | ||
} | ||
|
||
fn send_tx(app: &App, tx: &mut Transaction) -> anyhow::Result<()> { | ||
app.node.regenerate_proof(tx)?; | ||
let () = app.sign_and_send(tx.clone())?; | ||
Ok(()) | ||
} | ||
|
||
impl TxCreator { | ||
pub fn show( | ||
&mut self, | ||
app: &mut App, | ||
ui: &mut egui::Ui, | ||
base_tx: &mut Transaction, | ||
) -> anyhow::Result<()> { | ||
// if base txid has changed, store the new txid | ||
let base_txid = base_tx.txid(); | ||
let base_txid_changed = base_txid != self.base_txid; | ||
if base_txid_changed { | ||
self.base_txid = base_txid; | ||
} | ||
// (re)compute final tx if: | ||
// * the tx type, tx data, or base txid has changed | ||
// * final tx not yet set | ||
let refresh_final_tx = base_txid_changed || self.final_tx.is_none(); | ||
if refresh_final_tx { | ||
self.final_tx = Some(base_tx.clone()); | ||
} | ||
let final_tx = match &mut self.final_tx { | ||
None => panic!("impossible! final tx should have been set"), | ||
Some(final_tx) => final_tx, | ||
}; | ||
let txid = &format!("{}", final_tx.txid())[0..8]; | ||
ui.monospace(format!("txid: {txid}")); | ||
if self.value_in >= self.value_out { | ||
let fee = self.value_in - self.value_out; | ||
let fee = bitcoin::Amount::from_sat(fee); | ||
ui.monospace(format!("fee: {fee}")); | ||
if ui.button("sign and send").clicked() { | ||
if let Err(err) = send_tx(app, final_tx) { | ||
tracing::error!("{err:#}"); | ||
} else { | ||
*base_tx = Transaction::default(); | ||
self.final_tx = None; | ||
} | ||
} | ||
} else { | ||
ui.label("Not Enough Value In"); | ||
} | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.