diff --git a/py/libbolt.py b/py/libbolt.py index edae1c0..d4ec29f 100644 --- a/py/libbolt.py +++ b/py/libbolt.py @@ -61,9 +61,9 @@ class Libbolt(object): self.lib.ffishim_bidirectional_customer_close.argtypes = (c_void_p, c_void_p) self.lib.ffishim_bidirectional_customer_close.restype = c_void_p - # self.lib.ffishim_bidirectional_merchant_close.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p) - # self.lib.ffishim_bidirectional_merchant_close.restype = c_void_p - # + self.lib.ffishim_bidirectional_merchant_close.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p) + self.lib.ffishim_bidirectional_merchant_close.restype = c_void_p + # self.lib.ffishim_bidirectional_resolve.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p, c_void_p) # self.lib.ffishim_bidirectional_resolve.restype = c_void_p @@ -79,78 +79,80 @@ class Libbolt(object): def bidirectional_init_merchant(self, channel_state, b0_merch, name): output_string = self.lib.ffishim_bidirectional_init_merchant(channel_state.encode(), b0_merch, name.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return output_dictionary['channel_token'], output_dictionary['merch_wallet'] + return output_dictionary['channel_token'], output_dictionary['merch_state'] def bidirectional_init_customer(self, channel_state, channel_token, b0_cust, b0_merch, name): output_string = self.lib.ffishim_bidirectional_init_customer(channel_state.encode(), channel_token.encode(), b0_cust, b0_merch, name.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return (output_dictionary['channel_token'], output_dictionary['cust_wallet']) + return (output_dictionary['channel_token'], output_dictionary['cust_state']) # ESTABLISH PROTOCOL - def bidirectional_establish_customer_generate_proof(self, channel_token, cust_wallet): - output_string = self.lib.ffishim_bidirectional_establish_customer_generate_proof(channel_token.encode(), cust_wallet.encode()) + def bidirectional_establish_customer_generate_proof(self, channel_token, cust_state): + output_string = self.lib.ffishim_bidirectional_establish_customer_generate_proof(channel_token.encode(), cust_state.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return output_dictionary['channel_token'], output_dictionary['cust_wallet'], output_dictionary['com'], output_dictionary['com_proof'] + return output_dictionary['channel_token'], output_dictionary['cust_state'], output_dictionary['com'], output_dictionary['com_proof'] - def bidirectional_establish_merchant_issue_close_token(self, channel_state, com, com_proof, merch_wallet): - output_string = self.lib.ffishim_bidirectional_establish_merchant_issue_close_token(channel_state.encode(), com.encode(), com_proof.encode(), merch_wallet.encode()) + def bidirectional_establish_merchant_issue_close_token(self, channel_state, com, com_proof, merch_state): + output_string = self.lib.ffishim_bidirectional_establish_merchant_issue_close_token(channel_state.encode(), com.encode(), com_proof.encode(), merch_state.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) return output_dictionary['close_token'] - def bidirectional_establish_merchant_issue_pay_token(self, channel_state, com, merch_wallet): - output_string = self.lib.ffishim_bidirectional_establish_merchant_issue_pay_token(channel_state.encode(), com.encode(), merch_wallet.encode()) + def bidirectional_establish_merchant_issue_pay_token(self, channel_state, com, merch_state): + output_string = self.lib.ffishim_bidirectional_establish_merchant_issue_pay_token(channel_state.encode(), com.encode(), merch_state.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) return output_dictionary['pay_token'] - def bidirectional_establish_customer_verify_close_token(self, channel_state, cust_wallet, close_token): - output_string = self.lib.ffishim_bidirectional_verify_close_token(channel_state.encode(), cust_wallet.encode(), close_token.encode()) + def bidirectional_establish_customer_verify_close_token(self, channel_state, cust_state, close_token): + output_string = self.lib.ffishim_bidirectional_verify_close_token(channel_state.encode(), cust_state.encode(), close_token.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return output_dictionary['is_token_valid'], output_dictionary['channel_state'], output_dictionary['cust_wallet'] + return output_dictionary['is_token_valid'], output_dictionary['channel_state'], output_dictionary['cust_state'] - def bidirectional_establish_customer_final(self, channel_state, cust_wallet, pay_token): - output_string = self.lib.ffishim_bidirectional_establish_customer_final(channel_state.encode(), cust_wallet.encode(), pay_token.encode()) + def bidirectional_establish_customer_final(self, channel_state, cust_state, pay_token): + output_string = self.lib.ffishim_bidirectional_establish_customer_final(channel_state.encode(), cust_state.encode(), pay_token.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return output_dictionary['is_established'], output_dictionary['channel_state'], output_dictionary['cust_wallet'] + return output_dictionary['is_established'], output_dictionary['channel_state'], output_dictionary['cust_state'] # PAY PROTOCOL # generate payment proof - def bidirectional_pay_generate_payment_proof(self, channel_state, cust_wallet, amount): - output_string = self.lib.ffishim_bidirectional_pay_generate_payment_proof(channel_state.encode(), cust_wallet.encode(), amount) + def bidirectional_pay_generate_payment_proof(self, channel_state, cust_state, amount): + output_string = self.lib.ffishim_bidirectional_pay_generate_payment_proof(channel_state.encode(), cust_state.encode(), amount) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return output_dictionary['payment'], output_dictionary['cust_wallet'] + return output_dictionary['payment'], output_dictionary['cust_state'] # verify payment proof - def bidirectional_pay_verify_payment_proof(self, channel_state, pay_proof, merch_wallet): - output_string = self.lib.ffishim_bidirectional_pay_verify_payment_proof(channel_state.encode(), pay_proof.encode(), merch_wallet.encode()) + def bidirectional_pay_verify_payment_proof(self, channel_state, pay_proof, merch_state): + output_string = self.lib.ffishim_bidirectional_pay_verify_payment_proof(channel_state.encode(), pay_proof.encode(), merch_state.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return (output_dictionary['close_token'], output_dictionary['merch_wallet']) + return (output_dictionary['close_token'], output_dictionary['merch_state']) # generate revoke token - def bidirectional_pay_generate_revoke_token(self, channel_state, cust_wallet, new_cust_wallet, close_token): - output_string = self.lib.ffishim_bidirectional_pay_generate_revoke_token(channel_state.encode(), cust_wallet.encode(), - new_cust_wallet.encode(), close_token.encode()) + def bidirectional_pay_generate_revoke_token(self, channel_state, cust_state, new_cust_state, close_token): + output_string = self.lib.ffishim_bidirectional_pay_generate_revoke_token(channel_state.encode(), cust_state.encode(), + new_cust_state.encode(), close_token.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return output_dictionary['revoke_token'], output_dictionary['cust_wallet'] + return output_dictionary['revoke_token'], output_dictionary['cust_state'] # verify revoke token - def bidirectional_pay_verify_revoke_token(self, revoke_token, merch_wallet): - output_string = self.lib.ffishim_bidirectional_pay_verify_revoke_token(revoke_token.encode(), merch_wallet.encode()) + def bidirectional_pay_verify_revoke_token(self, revoke_token, merch_state): + output_string = self.lib.ffishim_bidirectional_pay_verify_revoke_token(revoke_token.encode(), merch_state.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return (output_dictionary['pay_token'], output_dictionary['merch_wallet']) + return (output_dictionary['pay_token'], output_dictionary['merch_state']) # CLOSE - def bidirectional_customer_close(self, channel_state, cust_wallet): - output_string = self.lib.ffishim_bidirectional_customer_close(channel_state.encode(), cust_wallet.encode()) + def bidirectional_customer_close(self, channel_state, cust_state): + output_string = self.lib.ffishim_bidirectional_customer_close(channel_state.encode(), cust_state.encode()) output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) - return output_dictionary['cust_close'] + return output_dictionary.get('cust_close') + + def bidirectional_merchant_close(self, channel_state, channel_token, cust_close, merch_state): + output_string = self.lib.ffishim_bidirectional_merchant_close(channel_state.encode(), channel_token.encode(), + cust_close.encode(), merch_state.encode()) + output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) + return (output_dictionary.get('wpk'), output_dictionary.get('revoke_token'), output_dictionary.get('error')) -# def bidirectional_merchant_refund(self, pp, channel, channel_token, merch_data, channel_closure, revoke_token): -# output_string = self.lib.ffishim_bidirectional_merchant_refund(pp.encode(), channel.encode(), channel_token.encode(), merch_data.encode(), channel_closure.encode(), revoke_token.encode()) -# output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8')) -# return (output_dictionary['rc_m'], output_dictionary['state']) # # def bidirectional_resolve(self, pp, cust_data, merch_data, cust_closure, merch_closure): # output_string = self.lib.ffishim_bidirectional_resolve( pp.encode(), cust_data.encode(), merch_data.encode(), cust_closure.encode(), merch_closure.encode()) @@ -203,28 +205,28 @@ channel_state = libbolt.channel_setup("My New Channel A") print("channel state new: ", channel_state) -(channel_token, merch_wallet) = libbolt.bidirectional_init_merchant(channel_state, b0_merch, "Bob") +(channel_token, merch_state) = libbolt.bidirectional_init_merchant(channel_state, b0_merch, "Bob") -print("merch_wallet: ", len(merch_wallet)) +print("merch_state: ", len(merch_state)) #print("channel_token: ", type(_channel_token)) -(channel_token, cust_wallet) = libbolt.bidirectional_init_customer(channel_state, channel_token, b0_cust, b0_merch, "Alice") -print("cust_wallet: ", len(cust_wallet)) +(channel_token, cust_state) = libbolt.bidirectional_init_customer(channel_state, channel_token, b0_cust, b0_merch, "Alice") +print("cust_state: ", len(cust_state)) -(channel_token, cust_wallet, com, com_proof) = libbolt.bidirectional_establish_customer_generate_proof(channel_token, cust_wallet) +(channel_token, cust_state, com, com_proof) = libbolt.bidirectional_establish_customer_generate_proof(channel_token, cust_state) print("com: ", com) -close_token = libbolt.bidirectional_establish_merchant_issue_close_token(channel_state, com, com_proof, merch_wallet) +close_token = libbolt.bidirectional_establish_merchant_issue_close_token(channel_state, com, com_proof, merch_state) print("close token: ", close_token) -(is_token_valid, channel_state, cust_wallet) = libbolt.bidirectional_establish_customer_verify_close_token(channel_state, cust_wallet, close_token) +(is_token_valid, channel_state, cust_state) = libbolt.bidirectional_establish_customer_verify_close_token(channel_state, cust_state, close_token) -pay_token = libbolt.bidirectional_establish_merchant_issue_pay_token(channel_state, com, merch_wallet) +pay_token = libbolt.bidirectional_establish_merchant_issue_pay_token(channel_state, com, merch_state) print("pay token: ", pay_token) -(is_channel_established, channel_state, cust_wallet) = libbolt.bidirectional_establish_customer_final(channel_state, cust_wallet, pay_token) +(is_channel_established, channel_state, cust_state) = libbolt.bidirectional_establish_customer_final(channel_state, cust_state, pay_token) if is_channel_established: - print("updated cust_wallet: ", cust_wallet) + print("updated cust_state: ", cust_state) else: print("channel still not established. did you verify close token?") @@ -232,21 +234,24 @@ else: print("Pay protocol...") amount = 5 -(payment_proof, new_cust_wallet) = libbolt.bidirectional_pay_generate_payment_proof(channel_state, cust_wallet, amount) +(payment_proof, new_cust_state) = libbolt.bidirectional_pay_generate_payment_proof(channel_state, cust_state, amount) print("Pay proof: ", len(payment_proof)) -print("new cust wallet: ", new_cust_wallet) +print("new cust wallet: ", new_cust_state) print("<========================================>") -(new_close_token, merch_wallet) = libbolt.bidirectional_pay_verify_payment_proof(channel_state, payment_proof, merch_wallet) +(new_close_token, merch_state) = libbolt.bidirectional_pay_verify_payment_proof(channel_state, payment_proof, merch_state) print("Close token: ", new_close_token) print("<========================================>") -(revoke_token, cust_wallet) = libbolt.bidirectional_pay_generate_revoke_token(channel_state, cust_wallet, new_cust_wallet, new_close_token) +(revoke_token, cust_state) = libbolt.bidirectional_pay_generate_revoke_token(channel_state, cust_state, new_cust_state, new_close_token) print("Revoke token: ", revoke_token) -(pay_token, merch_wallet) = libbolt.bidirectional_pay_verify_revoke_token(revoke_token, merch_wallet) +(pay_token, merch_state) = libbolt.bidirectional_pay_verify_revoke_token(revoke_token, merch_state) print("Pay token: ", pay_token) -cust_close_msg = libbolt.bidirectional_customer_close(channel_state, cust_wallet) -print("Cust close msg: ", cust_close_msg) +cust_close = libbolt.bidirectional_customer_close(channel_state, cust_state) +print("Cust close msg: ", cust_close) print("<========================================>") + +merch_close_tokens = libbolt.bidirectional_merchant_close(channel_state, channel_token, cust_close, merch_state) +print("Merch close tokens: ", merch_close_tokens) diff --git a/src/ffishim.rs b/src/ffishim.rs index 2a2dc82..a21e7a6 100644 --- a/src/ffishim.rs +++ b/src/ffishim.rs @@ -12,6 +12,7 @@ pub mod ffishim { use std::ffi::{CStr, CString}; use std::str; use std::mem; + use bidirectional::ChannelcloseC; fn error_message(s: String) -> *mut c_char { let ser = ["{\'error\':\'", serde_json::to_string(&s).unwrap().as_str(), "\'}"].concat(); @@ -324,34 +325,32 @@ pub mod ffishim { cser.into_raw() } + #[no_mangle] + pub extern fn ffishim_bidirectional_merchant_close(ser_channel_state: *mut c_char, ser_channel_token: *mut c_char, + ser_cust_close: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char { + // Deserialize the channel state + let channel_state: bidirectional::ChannelState = deserialize_object(ser_channel_state); + // Deserialize the channel token + let mut channel_token: bidirectional::ChannelToken = deserialize_object(ser_channel_token); + // Deserialize the customer close structure + let cust_close: bidirectional::ChannelcloseC = deserialize_object(ser_cust_close); + // Deserialize the merch wallet + let mut merch_state: bidirectional::MerchantState = deserialize_object(ser_merch_state); + + let option = bidirectional::merchant_close(&channel_state, &channel_token, &cust_close, &merch_state); + let keys = match option { + Ok(n) => n.unwrap(), + Err(err) => return error_message(err), + }; + + let ser = ["{\'wpk\':\'", serde_json::to_string(&keys.wpk).unwrap().as_str(), + "\', \'revoke_token\':\'", serde_json::to_string(&keys.revoke_token).unwrap().as_str(), "\'}"].concat(); + let cser = CString::new(ser).unwrap(); + cser.into_raw() + } + } -// #[no_mangle] -// pub extern fn ffishim_bidirectional_merchant_refund(serialized_pp: *mut c_char, serialized_channel: *mut c_char, serialized_channel_token: *mut c_char, serialized_merchant_data: *mut c_char, serialized_channel_closure: *mut c_char, serialized_revoke_token: *mut c_char) -> *mut c_char { -// // Deserialize the pp -// let deserialized_pp: bidirectional::PublicParams = deserialize_object(serialized_pp); -// -// // Deserialize the channel state -// let mut deserialized_channel_state: bidirectional::ChannelState = deserialize_object(serialized_channel); -// -// // Deserialize the channel token -// let deserialized_channel_token: bidirectional::ChannelToken = deserialize_object(serialized_channel_token); -// -// // Deserialize the merchant data -// let deserialized_merchant_data: bidirectional::InitMerchantData = deserialize_object(serialized_merchant_data); -// -// // Deserialize the closure -// let deserialized_channel_closure: bidirectional::ChannelclosureC = deserialize_object(serialized_channel_closure); -// -// // Deserialize the revoke_token -// let deserialized_revoke_token: secp256k1::Signature = deserialize_object(serialized_revoke_token); -// -// let rc_m = bidirectional::merchant_refute(&deserialized_pp, &mut deserialized_channel_state, &deserialized_channel_token, &deserialized_merchant_data, &deserialized_channel_closure, &deserialized_revoke_token); -// let ser = ["{\'rc_m\':\'", serde_json::to_string(&rc_m).unwrap().as_str(), "\', \'state\':\'", serde_json::to_string(&deserialized_channel_state).unwrap().as_str(), "\'}"].concat(); -// let cser = CString::new(ser).unwrap(); -// cser.into_raw() -// } -// // #[no_mangle] // pub extern fn ffishim_bidirectional_resolve(serialized_pp: *mut c_char, serialized_customer_data: *mut c_char, serialized_merchant_data: *mut c_char, serialized_closure_customer: *mut c_char, serialized_closure_merchant: *mut c_char) -> *mut c_char { // // Deserialize the pp diff --git a/src/lib.rs b/src/lib.rs index 64400d6..ff3d462 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,16 +140,11 @@ pub mod bidirectional { #[serde(bound(deserialize = "::Fr: serde::Deserialize<'de>, \ ::G1: serde::Deserialize<'de>"))] pub struct ChannelcloseC { + pub wpk: secp256k1::PublicKey, pub message: wallet::Wallet, pub signature: cl::Signature } - #[derive(Clone)] - pub struct ChannelcloseM { - pub message: util::RevokedMessage, - pub signature: cl::Signature - } - #[derive(Clone, Serialize, Deserialize)] #[serde(bound(serialize = "::Fr: serde::Serialize, \ ::G1: serde::Serialize, \ @@ -288,6 +283,8 @@ pub mod bidirectional { // if valid revoke_token is provided later for wpk, then release pay-token let new_close_token = merch_state.verify_payment(csprng, &channel_state, &payment.proof, &payment.com, &payment.wpk, payment.amount).unwrap(); + // store the wpk since it has been revealed + update_merchant_state(&mut merch_state.keys, &payment.wpk, None); return new_close_token; } @@ -384,29 +381,32 @@ pub mod bidirectional { let close_wallet = wallet.with_close(String::from("close")); assert!(pk.verify(&cp.pub_params.mpk, &close_wallet, &close_token)); - ChannelcloseC { message: wallet, signature: close_token } + ChannelcloseC { wpk: cust_state.wpk, message: wallet, signature: close_token } } - fn exist_in_merchant_state(db: &mut HashMap, wpk: &secp256k1::PublicKey, rev: Option) -> bool { - if db.is_empty() { - return false; - } - - let fingerprint = util::compute_pub_key_fingerprint(wpk); - if db.contains_key(&fingerprint) { - let pub_key = db.get(&fingerprint).unwrap(); - if pub_key.revoke_token.is_none() { - // let's just check the public key - return pub_key.wpk == *wpk; - } - if !rev.is_none() { - return pub_key.wpk == *wpk && pub_key.revoke_token.unwrap() == rev.unwrap(); - } - return pub_key.wpk == *wpk; - } - - return false; - } +// fn exist_in_merchant_state(db: &HashMap, wpk: &secp256k1::PublicKey, rev: Option) -> (bool, Option) { +// if db.is_empty() { +// return (false, None); +// } +// +// let fingerprint = util::compute_pub_key_fingerprint(wpk); +// if db.contains_key(&fingerprint) { +// let revoked_state = db.get(&fingerprint).unwrap(); +// +// +// +// if revoked_state.revoke_token.is_none() { +// // let's just check the public key +// return (revoked_state.wpk == *wpk, None); +// } +// if !rev.is_none() { +// return (revoked_state.wpk == *wpk && pub_key.revoke_token.unwrap() == rev.unwrap(), None); +// } +// return (pub_key.wpk == *wpk, Some(pub_key)); +// } +// +// return false; +// } fn update_merchant_state(db: &mut HashMap, wpk: &secp256k1::PublicKey, rev: Option) { let fingerprint = util::compute_pub_key_fingerprint(wpk); @@ -421,35 +421,49 @@ pub mod bidirectional { } /// - /// merchant_refute - takes as input the public params, channel token, merchant's wallet, - /// channels tate, channel closure from customer, and revocation token. - /// Generates a channel closure message for merchant and updated merchant internal state. + /// merchant_close - takes as input the channel state, channel token, customer close msg/sig, + /// Returns tokens for merchant close transaction (only if customer close message is found to be a + /// double spend). If not, then None is returned. /// - pub fn merchant_refute(channel_state: &mut ChannelState, - channel_token: &ChannelToken, - rc_c: &ChannelcloseC, rv_token: &secp256k1::Signature) -> bool { - return true; + pub fn merchant_close(channel_state: &ChannelState, + channel_token: &ChannelToken, + cust_close: &ChannelcloseC, + merch_state: &MerchantState) -> BoltResult { + if (!channel_state.channel_established) { + return Err(String::from("merchant_close - Channel not established! Cannot generate channel closure message.")); + } + + let cp = channel_state.cp.as_ref().unwrap(); + let pk = cp.pub_params.keypair.get_public_key(&cp.pub_params.mpk); + let mut wallet = cust_close.message.clone(); + let close_wallet = wallet.with_close(String::from("close")).clone(); + let close_token = cust_close.signature.clone(); + + let is_valid = pk.verify(&cp.pub_params.mpk, &close_wallet, &close_token); + + if is_valid { + let wpk = cust_close.wpk; + // found the wpk, which means old close token + let fingerprint = util::compute_pub_key_fingerprint(&wpk); + if merch_state.keys.contains_key(&fingerprint) { + let revoked_state = merch_state.keys.get(&fingerprint).unwrap(); + if !revoked_state.revoke_token.is_none() { + let revoke_token = revoked_state.revoke_token.unwrap().clone(); + // verify the revoked state first before returning + let secp = secp256k1::Secp256k1::new(); + let revoke_msg = RevokedMessage::new(String::from("revoked"), wpk.clone()); + let msg = secp256k1::Message::from_slice(&revoke_msg.hash_to_slice()).unwrap(); + // verify that the revocation token is valid + if secp.verify(&msg, &revoke_token, &wpk).is_ok() { + return Ok(Some(revoked_state.clone())); + } + } + return Err(String::from("merchant_close - Found wpk but could not find the revoke token. Merchant abort detected.")); + } + return Err(String::from("merchant_close - Could not find entry for wpk & revoke token pair. Valid close!")); + } + Err(String::from("merchant_close - Customer close message not valid!")) } -// rc_c: &ChannelclosureC, rv_token: &secp256k1::Signature) // -> ChannelclosureM { -// // for merchant => on input the merchant's current state S_old and a customer channel closure message, -// // outputs a merchant channel closure message rc_m and updated merchant state S_new -// let is_valid = cl::verify_d(&pp.cl_mpk, &t_c.pk, &rc_c.message.hash(), &rc_c.signature); -// if is_valid { -// let wpk = rc_c.message.wpk; -// let balance = rc_c.message.balance; -// if !exist_in_merchant_state(&state, &wpk, Some(*rv_token)) { -// // update state to include the user's wallet key -// assert!(update_merchant_state(state, &wpk, Some(*rv_token))); -// } -// let ser_rv_token = rv_token.serialize_compact(); -// let rm = RevokedMessage::new(String::from("revoked"), wpk, Some(ser_rv_token)); -// // sign the revoked message -// let signature = cl::sign_d(&pp.cl_mpk, &m_data.csk.sk, &rm.hash()); -// return ChannelclosureM { message: rm, signature: signature }; -// } else { -// panic!("Signature on customer closure message is invalid!"); -// } -// } } #[cfg(all(test, feature = "unstable"))] @@ -468,6 +482,7 @@ mod tests { use super::*; use ff::Rand; use pairing::bls12_381::{Bls12}; + use rand::Rng; fn setup_new_channel_helper(channel_state: &mut bidirectional::ChannelState, init_cust_bal: i32, init_merch_bal: i32) @@ -636,10 +651,10 @@ mod tests { assert!(cust_state.cust_balance == (b0_customer - total_owed) && cust_state.merch_balance == total_owed + b0_merchant); } - let cust_close = bidirectional::customer_close(&channel_state, &cust_state); + let cust_close_msg = bidirectional::customer_close(&channel_state, &cust_state); println!("Obtained the channel close message"); - println!("{}", cust_close.message); - println!("{}", cust_close.signature); + println!("{}", cust_close_msg.message); + println!("{}", cust_close_msg.signature); } } @@ -664,7 +679,6 @@ mod tests { assert!(channel_state.channel_established); { - // make multiple payments in a loop execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, payment_increment); { @@ -676,6 +690,94 @@ mod tests { } } + #[test] + fn bidirectional_merchant_close_detects_double_spends() { + let mut rng = &mut rand::thread_rng(); + + let b0_customer = rng.gen_range(100, 1000); + let b0_merchant = 10; + let pay_increment = 20; + + let mut channel_state = bidirectional::ChannelState::::new(String::from("Channel A -> B"), false); + + channel_state.setup(&mut rng); // or load_setup params + + let (mut channel_token, mut merch_state, mut cust_state) = setup_new_channel_helper( &mut channel_state, b0_customer, b0_merchant); + + // run establish protocol for customer and merchant channel + execute_establish_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state); + + assert!(channel_state.channel_established); + + // let's make a few payments then exit channel (will post an old channel state + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + + // let's close then move state forward + let old_cust_close_msg = bidirectional::customer_close(&channel_state, &cust_state); + + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + let cur_cust_close_msg = bidirectional::customer_close(&channel_state, &cust_state); + + let merch_close_result = bidirectional::merchant_close(&channel_state, + &channel_token, + &old_cust_close_msg, + &merch_state); + let merch_close_msg = match merch_close_result { + Ok(n) => n.unwrap(), + Err(err) => panic!("Merchant close msg: {}", err) + }; + + println!("Double spend attempt by customer! Evidence below..."); + println!("Merchant close: wpk = {}", merch_close_msg.wpk); + println!("Merchant close: revoke_token = {}", merch_close_msg.revoke_token.unwrap()); + } + + #[test] + #[should_panic] + fn bidirectional_merchant_close_works() { + let mut rng = &mut rand::thread_rng(); + + let b0_customer = rng.gen_range(100, 1000); + let b0_merchant = 10; + let pay_increment = 20; + + let mut channel_state = bidirectional::ChannelState::::new(String::from("Channel A -> B"), false); + + channel_state.setup(&mut rng); // or load_setup params + + let (mut channel_token, mut merch_state, mut cust_state) = setup_new_channel_helper( &mut channel_state, b0_customer, b0_merchant); + + // run establish protocol for customer and merchant channel + execute_establish_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state); + + assert!(channel_state.channel_established); + + // let's make a few payments then exit channel (will post an old channel state + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + + execute_payment_protocol_helper(&mut channel_state, &mut channel_token, &mut merch_state, &mut cust_state, pay_increment); + + let cust_close_msg = bidirectional::customer_close(&channel_state, &cust_state); + + let merch_close_result = bidirectional::merchant_close(&channel_state, + &channel_token, + &cust_close_msg, + &merch_state); + let merch_close_msg = match merch_close_result { + Ok(n) => n.unwrap(), + Err(err) => panic!("Merchant close msg: {}", err) + }; + } + + // fn execute_third_party_pay_protocol_helper(pp: &bidirectional::PublicParams, // channel1: &mut bidirectional::ChannelState, channel2: &mut bidirectional::ChannelState, // merch_keys: &cl::KeyPairD, merch1_data: &mut bidirectional::InitMerchantData, diff --git a/src/util.rs b/src/util.rs index c58a3d5..c4dad62 100644 --- a/src/util.rs +++ b/src/util.rs @@ -119,7 +119,6 @@ pub fn hash_buffer_to_fr<'a, E: Engine>(prefix: &'a str, buf: &[u8; 64]) -> E::F pub struct RevokedMessage { pub msgtype: String, pub wpk: secp256k1::PublicKey - //pub sig: Option<[u8; 64]> // represents revocation token serialized compact bytes } impl RevokedMessage {