diff --git a/.env b/.env index 6c37ce9..2621070 100644 --- a/.env +++ b/.env @@ -1,9 +1,11 @@ -# Test setup, -# in productive use pubkeys and contract information -# should be handled outside of the client -PROVIDER_NOSTR_NSEC= -BUYER_NOSTR_NSEC= -SELLER_NOSTR_NSEC= -PROVIDER_ECASH_SECRET_HEX= -BUYER_ECASH_SECRET_HEX= -SELLER_ECASH_SECRET_HEX= +# Buyer +BUYER_NSEC=nsec182ul8zg2jlje6gtejs4pp4y4un674esq9qmrdxn2mewynkegahgqudmhvh +BUYER_NPUB=npub1rsdt7jfwkk9mnzn6vne6geuaaz6rvtr6twxsedzrkan3jzk2uyjsczwcxu + +# Seller +SELLER_NSEC=nsec1vackt9cn8ujwz9t2yj6x29d4tgjx3uhp5h0fyev8tp5lnw40lcls3rp7hp +SELLER_NPUB=npub1pjnvp4kwud0r80kk23h726tn8ewfqmd4y5m9g2rggs0xrdzrgexq0hv5gr + +# Escrow Provider +ESCROW_NSEC=nsec1z62pah093gfj7wzjssc24x3nczmjgy778pxwale7hwesemmzln0qc4dhhu +ESCROW_NPUB=npub1hcsc4r3xc9ygnefp4eqyan9r46tjvd3w0dxk2hgydc9k6m5xd3jq2hkjqp \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8f0b31b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'birdseed-escrow'", + "cargo": { + "args": [ + "build", + "--bin=birdseed-escrow", + "--package=birdseed-escrow" + ], + "filter": { + "name": "birdseed-escrow", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'birdseed-escrow'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=birdseed-escrow", + "--package=birdseed-escrow" + ], + "filter": { + "name": "birdseed-escrow", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/src/escrow_client/mod.rs b/src/escrow_client/mod.rs index c983cd1..8e79bd4 100644 --- a/src/escrow_client/mod.rs +++ b/src/escrow_client/mod.rs @@ -44,6 +44,9 @@ impl Trader { async fn seller_pipeline(&self, config: &EscrowUser) -> anyhow::Result<()> { let escrow_token = config.await_and_validate_trade_token().await?; + // send product and proof of delivery (oracle) to seller + + // await signature or begin dispute Ok(()) } @@ -104,12 +107,13 @@ impl EscrowUser { trade_beginning_ts: Timestamp, provider_npub: &String, ) -> anyhow::Result { - let filter_note = Filter::new().kind(Kind::EncryptedDirectMessage); - let filter_timestamp = Filter::new().since(trade_beginning_ts); - let filter_author = Filter::new().author(nostr_sdk::PublicKey::from_bech32(provider_npub)?); + let filter_note = Filter::new() + .kind(Kind::EncryptedDirectMessage) + .since(trade_beginning_ts) + .author(nostr_sdk::PublicKey::from_bech32(provider_npub)?); nostr_client .client - .subscribe(vec![filter_note, filter_timestamp, filter_author], None) + .subscribe(vec![filter_note], None) .await; let mut notifications = nostr_client.client.notifications(); @@ -132,15 +136,13 @@ impl EscrowUser { } async fn await_and_validate_trade_token(&self) -> anyhow::Result { - let filter_note = Filter::new().kind(Kind::EncryptedDirectMessage); - let filter_timestamp = - Filter::new().since(Timestamp::from(self.contract.trade_beginning_ts)); - let filter_author = Filter::new().author(nostr_sdk::PublicKey::from_bech32( - &self.contract.npub_buyer, - )?); + let filter_note = Filter::new() + .kind(Kind::EncryptedDirectMessage) + .since(Timestamp::from(self.contract.trade_beginning_ts)) + .author(nostr_sdk::PublicKey::from_bech32(&self.contract.npub_buyer,)?); self.nostr_client .client - .subscribe(vec![filter_note, filter_timestamp, filter_author], None) + .subscribe(vec![filter_note], None) .await; let mut notifications = self.nostr_client.client.notifications(); @@ -152,7 +154,7 @@ impl EscrowUser { .decrypt_msg(&event.content, &event.author()) .await { - println!("Received event: {:?}", &decrypted); + dbg!("Received token event: {:?}", &decrypted); if let Ok(escrow_token) = self.wallet.validate_escrow_token(&decrypted, &self).await { diff --git a/src/escrow_provider/mod.rs b/src/escrow_provider/mod.rs index a555f40..2ea3a24 100644 --- a/src/escrow_provider/mod.rs +++ b/src/escrow_provider/mod.rs @@ -1,5 +1,6 @@ use super::*; use cdk::nuts::SecretKey; +use hashes::hex::DisplayHex; use nostr_sdk::{Filter, Kind, RelayPoolNotification}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -41,16 +42,17 @@ impl EscrowProvider { } pub async fn run(&mut self) -> anyhow::Result<()> { - let filter_note = Filter::new().kind(Kind::EncryptedDirectMessage); - - let filter_self = Filter::new().custom_tag( - SingleLetterTag::lowercase(Alphabet::P), - [PublicKey::from_bech32(&self.nostr_client.get_npub().await?)?.to_hex()], - ); + let filter_note = Filter::new() + .kind(Kind::EncryptedDirectMessage) + .custom_tag( + SingleLetterTag::lowercase(Alphabet::P), + [PublicKey::from_bech32(&self.nostr_client.get_npub()?)?.to_hex()], + ) + .since(Timestamp::now()); self.nostr_client .client - .subscribe(vec![filter_note, filter_self], None) + .subscribe(vec![filter_note], None) .await; let mut notifications = self.nostr_client.client.notifications(); @@ -61,8 +63,8 @@ impl EscrowProvider { .decrypt_msg(&event.content, &event.author()) .await { - println!("Received event: {:?}", &decrypted); - if let Ok((contract_hash, contract)) = self.parse(&event.content).await { + dbg!("Received event: {:?}", &decrypted); + if let Ok((contract_hash, contract)) = self.parse(decrypted.as_str()).await { if self.pending_contracts.contains_key(&contract_hash) { self.pending_contracts.remove(&contract_hash); self.begin_trade(&contract_hash, &contract).await?; @@ -93,6 +95,7 @@ impl EscrowProvider { contract_hash: &[u8; 32], trade: &TradeContract, ) -> anyhow::Result<()> { + dbg!("Beginning trade: {}", contract_hash.to_hex_string(hashes::hex::Case::Lower)); let contract_secret = SecretKey::generate(); self.active_contracts.insert( contract_hash.clone(), diff --git a/src/main.rs b/src/main.rs index bffb7e5..d4d5e79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ mod escrow_client; mod escrow_provider; mod nostr; +use std::env; + use anyhow::{anyhow, Result}; use cli::get_user_input; use dotenv::dotenv; @@ -18,43 +20,45 @@ async fn main() -> anyhow::Result<()> { dotenv().ok(); // parsing was hacked together last minute :) // information would be communicated oob - let mut buyer_npub: String = String::new(); - let mut seller_npub: String = String::new(); - let mut coordinator_npub: String = String::new(); + let mut buyer_npub: String = env::var("BUYER_NPUB")?; + let mut seller_npub: String = env::var("SELLER_NPUB")?; + let coordinator_npub: String = env::var("ESCROW_NPUB")?; let ecash_wallet = EcashWallet::new().await?; let mut seller_ecash_pubkey: String = String::new(); let mut buyer_ecash_pubkey: String = String::new(); - let nostr_client = NostrClient::new(&get_user_input("Enter nostr nsec: ").await?).await?; + let nostr_client: NostrClient; - let mode = match get_user_input("Select mode: buyer, seller, provider: ") + let mode = match get_user_input("Select mode: (1) buyer, (2) seller, (3) provider: ") .await? .as_str() { - "buyer" => { - buyer_npub = nostr_client.get_npub().await?; - println!("Buyer npub: {}", &buyer_npub); - seller_npub = get_user_input("Enter seller's npub: ").await?; + "1" => { + nostr_client = NostrClient::new(&env::var("BUYER_NSEC")?).await?; + buyer_npub = nostr_client.get_npub()?; + //println!("Buyer npub: {}", &buyer_npub); seller_ecash_pubkey = get_user_input("Enter seller's ecash pubkey: ").await?; - coordinator_npub = get_user_input("Enter coordinator's npub: ").await?; buyer_ecash_pubkey = ecash_wallet.trade_pubkey.clone(); String::from("buyer") } - "seller" => { - seller_npub = nostr_client.get_npub().await?; - println!("Seller npub: {}", &seller_npub); + "2" => { + nostr_client = NostrClient::new(&env::var("SELLER_NSEC")?).await?; + seller_npub = nostr_client.get_npub()?; + //println!("Seller npub: {}", &seller_npub); seller_ecash_pubkey = ecash_wallet.trade_pubkey.clone(); - buyer_npub = get_user_input("Enter buyer's npub: ").await?; buyer_ecash_pubkey = get_user_input("Enter buyer's ecash pubkey: ").await?; - coordinator_npub = get_user_input("Enter coordinator's npub: ").await?; String::from("seller") } - "provider" => { - println!("Coordinator npub: {}", nostr_client.get_npub().await?); + "3" => { + nostr_client = NostrClient::new(&env::var("ESCROW_NSEC")?).await?; + //println!("Coordinator npub: {}", nostr_client.get_npub().await?); let mut escrow_provider = EscrowProvider::setup(nostr_client, ecash_wallet).await?; escrow_provider.run().await?; return Ok(()); } - _ => String::from("none"), + _ => { + nostr_client = NostrClient::new(&env::var("ESCROW_NSEC")?).await?;//irrelevant + String::from("none") + } }; let contract = TradeContract { @@ -74,10 +78,8 @@ async fn main() -> anyhow::Result<()> { EscrowUser::new(contract, ecash_wallet, nostr_client, coordinator_npub).await?; match mode.as_str() { - "buyer" => Trader::Buyer(escrow_user).init_trade().await?, - "seller" => Trader::Seller(escrow_user).init_trade().await?, + "buyer" => Trader::Buyer(escrow_user).init_trade().await, + "seller" => Trader::Seller(escrow_user).init_trade().await, _ => return Err(anyhow!("Invalid mode")), } - - Ok(()) } diff --git a/src/nostr/mod.rs b/src/nostr/mod.rs index f5c6b60..ff8a823 100644 --- a/src/nostr/mod.rs +++ b/src/nostr/mod.rs @@ -32,7 +32,7 @@ impl NostrClient { Ok(Self { keypair, client }) } - pub async fn get_npub(&self) -> anyhow::Result { + pub fn get_npub(&self) -> anyhow::Result { Ok(self.keypair.public_key().to_bech32()?) }