add api description to README and clean up vars

This commit is contained in:
J. Ayo Akinyele 2018-08-16 18:05:01 -04:00
parent 5ac39d5860
commit af5ff70399
3 changed files with 174 additions and 56 deletions

120
README.md
View File

@ -69,10 +69,119 @@ The libbolt library provides APIs for three types of privacy-preserving payment
* bidirectional payment channels (done)
* third-party payments (done)
## Bidirectional Payment Channels
## Unidirectional Payment Channels
**TODO**
## Bidirectional Payment Channels
A bidirectional payment channel enables two parties to exchange arbitrary positive and negative amounts.
### Channel Setup and Key Generation
The first part of setting up bi-directional payment channels involve generating initial setup parameters, channel state and key generation for both parties.
use libbolt::bidirectional;
// setup bidirectional scheme params
let pp = bidirectional::setup(true);
// generate the initial channel state
// second argument represents third-party mode
let mut channel = bidirectional::ChannelState::new(String::from("My New Channel A"), false);
To generate keys for both parties, call the `bidirectional::keygen()` routine with the public parameters as input.
// merchant generates a long-lived key pair
let m_keypair = bidirectional::keygen(&pp);
// customer generates an ephemeral keypair for use on a single channel
let c_keypair = bidirectional::keygen(&pp);
### Initialization
To initialize the channel for both parties, do the following:
let b0_merch = 10;
let b0_cust = 100;
// initialize on the merchant side with balance, b0_merch
let mut m_data = bidirectional::init_merchant(&pp, b0_merch, &m_keypair));
// generate the public params for the commitment scheme
let cm_csp = bidirectional::generate_commit_setup(&pp, &m_keypair.pk);
// initialize on the customer side with balance, b0_cust
let mut c_data = bidirectional::init_customer(&pp, // public params
&channel, // channel state
b0_cust, // init customer balance
b0_merch, // init merchant balance
&cm_csp, // commitment pub params
&c_keypair)); // customer keypair
### Establish Protocol
When opening a payment channel, execute the establishment protocol API to escrow funds privately as follows:
// entering the establish protocol for the channel
let proof1 = bidirectional::establish_customer_phase1(&pp, &c_data, &m_data.bases);
// obtain the wallet signature from the merchant
let w_sig = bidirectional::establish_merchant_phase2(&pp, &mut channel, &m_data, &proof1));
// complete channel establishment");
assert!(bidirectional::establish_customer_final(&pp, &m_keypair.pk, &mut c_data.csk, w_sig));
// confirm that the channel state is now established
assert!(channel.channel_established);
### Pay protocol
To spend on the channel, execute the pay protocol API (can be executed as many times as necessary):
// precomputation phase that customer does offline prior to a spend
bidirectional::pay_by_customer_phase1_precompute(&pp, &c_data.channel_token, &m_keypair.pk, &mut c_data.csk);
// generate new channel token, new wallet and payment proof
// send the payment proof to the merchant
let (t_c, new_w, pay_proof) = bidirectional::pay_by_customer_phase1(&pp, &channel,
&c_data.channel_token, // channel token
&m_keypair.pk, // merchant verification key
&c_data.csk, // current wallet
5); // balance increment
// get the refund token (rt_w) from the merchant
let rt_w = bidirectional::pay_by_merchant_phase1(&pp, &mut channel, &pay_proof, &m_data));
// generate the revocation token (rv_w) on the old public key (wpk)
let rv_w = bidirectional::pay_by_customer_phase2(&pp, &c_data.csk, &new_w, &m_keypair.pk, &rt_w));
// get the signature on the new wallet from merchant
let new_w_sig = bidirectional::pay_by_merchant_phase2(&pp, &mut channel, &pay_proof, &mut m_data, &rv_w));
// complete the final step of pay protocol - verify merchant signature on wallet
assert!(bidirectional::pay_by_customer_final(&pp, &m_keypair.pk, &mut c_data, t_c, new_w, new_w_sig));
### Channel Closure Algorithms
To close a channel, the customer must executes the `bidirectional::customer_refund()` routine as follows:
let cust_wallet = &c_data.csk;
let rc_c = bidirectional::customer_refund(&pp, &channel, &m_keypair.pk, &cust_wallet);
The merchant can dispute a customer's claim by executing the `bidirectional::merchant_retute()` routine follows:
let channel_token = &c_data.channel_token;
let rc_m = bidirectional::merchant_refute(&pp, &mut channel, &channel_token, &m_data, &rc_c, &rv_w.signature);
To resolve a dispute between a customer and a merchant, the following routine is executed by the network:
let (new_b0_cust, new_b0_merch) = bidirectional::resolve(&pp, &c_data, &m_data,
Some(rc_c), Some(rc_m), Some(rt_w));
`new_b0_cust` and `new_b0_merch` represent the new balances for the customer and merchant (respectively).
## Third-party Payment Support
**TODO**
@ -89,6 +198,15 @@ To contribute code improvements, please checkout the repository, make your chang
git clone https://github.com/yeletech/libbolt.git
# TODOs
Here are some TODOs (not in any particular order):
* Serialization support for libbolt structures such as `CustomerWallet`, `PaymentProof`, and so on.
* Support for other curves (e.g., pairing library from Zcash)
* Finish unidirectional channel construction
* Fix warnings
* Add more unit tests for other dispute resolution scenarios and pay protocol (to ensure appopriate aborts), third-party test cases, etc.
# License

View File

@ -66,27 +66,27 @@ fn main() {
// each party executes the init algorithm on the agreed initial challenge balance
// in order to derive the channel tokens
println!("[5a] libbolt - initialize on the merchant side with balance {}", b0_merch);
let (mut init_merch_data, initm_time) = measure_ret_mut!(bidirectional::init_merchant(&pp, b0_merch, &merch_keypair));
let (mut merch_data, initm_time) = measure_ret_mut!(bidirectional::init_merchant(&pp, b0_merch, &merch_keypair));
println!(">> TIME for init_merchant: {}", initm_time);
println!("[5b] libbolt - initialize on the customer side with balance {}", b0_cust);
let cm_csp = bidirectional::generate_commit_setup(&pp, &merch_keypair.pk);
let (mut init_cust_data, initc_time) = measure_ret_mut!(bidirectional::init_customer(&pp, &channel, b0_cust, b0_merch, &cm_csp, &cust_keypair));
let (mut cust_data, initc_time) = measure_ret_mut!(bidirectional::init_customer(&pp, &channel, b0_cust, b0_merch, &cm_csp, &cust_keypair));
println!(">> TIME for init_customer: {}", initc_time);
println!("******************************************");
// libbolt tests below
println!("Testing the establish protocol...");
println!("[6a] libbolt - entering the establish protocol for the channel");
let (proof1, est_cust_time1) = measure!(bidirectional::establish_customer_phase1(&pp, &init_cust_data, &init_merch_data.bases));
let (proof1, est_cust_time1) = measure!(bidirectional::establish_customer_phase1(&pp, &cust_data, &merch_data.bases));
println!(">> TIME for establish_customer_phase1: {}", est_cust_time1);
println!("[6b] libbolt - obtain the wallet signature from the merchant");
let (wallet_sig, est_merch_time2) = measure!(bidirectional::establish_merchant_phase2(&pp, &mut channel, &init_merch_data, &proof1));
let (wallet_sig, est_merch_time2) = measure!(bidirectional::establish_merchant_phase2(&pp, &mut channel, &merch_data, &proof1));
println!(">> TIME for establish_merchant_phase2: {}", est_merch_time2);
println!("[6c] libbolt - complete channel establishment");
assert!(bidirectional::establish_customer_final(&pp, &merch_keypair.pk, &mut init_cust_data.csk, wallet_sig));
assert!(bidirectional::establish_customer_final(&pp, &merch_keypair.pk, &mut cust_data.csk, wallet_sig));
assert!(channel.channel_established);
@ -95,57 +95,57 @@ fn main() {
println!("Testing the pay protocol...");
// let's test the pay protocol
bidirectional::pay_by_customer_phase1_precompute(&pp, &init_cust_data.T, &merch_keypair.pk, &mut init_cust_data.csk);
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust_data.channel_token, &merch_keypair.pk, &mut cust_data.csk);
let s = PreciseTime::now();
let (t_c, new_wallet, pay_proof) = bidirectional::pay_by_customer_phase1(&pp, &channel, &init_cust_data.T, // channel token
let (t_c, new_wallet, pay_proof) = bidirectional::pay_by_customer_phase1(&pp, &channel, &cust_data.channel_token, // channel token
&merch_keypair.pk, // merchant pub key
&init_cust_data.csk, // wallet
&cust_data.csk, // wallet
5); // balance increment
let e = PreciseTime::now();
println!(">> TIME for pay_by_customer_phase1: {}", s.to(e));
// get the refund token (rt_w)
let (rt_w, pay_merch_time1) = measure!(bidirectional::pay_by_merchant_phase1(&pp, &mut channel, &pay_proof, &init_merch_data));
let (rt_w, pay_merch_time1) = measure!(bidirectional::pay_by_merchant_phase1(&pp, &mut channel, &pay_proof, &merch_data));
println!(">> TIME for pay_by_merchant_phase1: {}", pay_merch_time1);
// get the revocation token (rv_w) on the old public key (wpk)
let (rv_w, pay_cust_time2) = measure!(bidirectional::pay_by_customer_phase2(&pp, &init_cust_data.csk, &new_wallet, &merch_keypair.pk, &rt_w));
let (rv_w, pay_cust_time2) = measure!(bidirectional::pay_by_customer_phase2(&pp, &cust_data.csk, &new_wallet, &merch_keypair.pk, &rt_w));
println!(">> TIME for pay_by_customer_phase2: {}", pay_cust_time2);
// get the new wallet sig (new_wallet_sig) on the new wallet
let (new_wallet_sig, pay_merch_time2) = measure!(bidirectional::pay_by_merchant_phase2(&pp, &mut channel, &pay_proof, &mut init_merch_data, &rv_w));
let (new_wallet_sig, pay_merch_time2) = measure!(bidirectional::pay_by_merchant_phase2(&pp, &mut channel, &pay_proof, &mut merch_data, &rv_w));
println!(">> TIME for pay_by_merchant_phase2: {}", pay_merch_time2);
assert!(bidirectional::pay_by_customer_final(&pp, &merch_keypair.pk, &mut init_cust_data, t_c, new_wallet, new_wallet_sig));
assert!(bidirectional::pay_by_customer_final(&pp, &merch_keypair.pk, &mut cust_data, t_c, new_wallet, new_wallet_sig));
{
// scope localizes the immutable borrow here (for debug purposes only)
let cust_wallet = &init_cust_data.csk;
let merch_wallet = &init_merch_data.csk;
let cust_wallet = &cust_data.csk;
let merch_wallet = &merch_data.csk;
println!("Customer balance: {}", cust_wallet.balance);
println!("Merchant balance: {}", merch_wallet.balance);
}
bidirectional::pay_by_customer_phase1_precompute(&pp, &init_cust_data.T, &merch_keypair.pk, &mut init_cust_data.csk);
let (t_c1, new_wallet1, pay_proof1) = bidirectional::pay_by_customer_phase1(&pp, &channel, &init_cust_data.T, // channel token
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust_data.channel_token, &merch_keypair.pk, &mut cust_data.csk);
let (t_c1, new_wallet1, pay_proof1) = bidirectional::pay_by_customer_phase1(&pp, &channel, &cust_data.channel_token, // channel token
&merch_keypair.pk, // merchant pub key
&init_cust_data.csk, // wallet
&cust_data.csk, // wallet
-10); // balance increment
// get the refund token (rt_w)
let rt_w1 = bidirectional::pay_by_merchant_phase1(&pp, &mut channel, &pay_proof1, &init_merch_data);
let rt_w1 = bidirectional::pay_by_merchant_phase1(&pp, &mut channel, &pay_proof1, &merch_data);
// get the revocation token (rv_w) on the old public key (wpk)
let rv_w1 = bidirectional::pay_by_customer_phase2(&pp, &init_cust_data.csk, &new_wallet1, &merch_keypair.pk, &rt_w1);
let rv_w1 = bidirectional::pay_by_customer_phase2(&pp, &cust_data.csk, &new_wallet1, &merch_keypair.pk, &rt_w1);
// get the new wallet sig (new_wallet_sig) on the new wallet
let new_wallet_sig1 = bidirectional::pay_by_merchant_phase2(&pp, &mut channel, &pay_proof1, &mut init_merch_data, &rv_w1);
let new_wallet_sig1 = bidirectional::pay_by_merchant_phase2(&pp, &mut channel, &pay_proof1, &mut merch_data, &rv_w1);
assert!(bidirectional::pay_by_customer_final(&pp, &merch_keypair.pk, &mut init_cust_data, t_c1, new_wallet1, new_wallet_sig1));
assert!(bidirectional::pay_by_customer_final(&pp, &merch_keypair.pk, &mut cust_data, t_c1, new_wallet1, new_wallet_sig1));
{
let cust_wallet = &init_cust_data.csk;
let merch_wallet = &init_merch_data.csk;
let cust_wallet = &cust_data.csk;
let merch_wallet = &merch_data.csk;
println!("Updated balances...");
println!("Customer balance: {}", cust_wallet.balance);
println!("Merchant balance: {}", merch_wallet.balance);
@ -160,16 +160,16 @@ fn main() {
println!("Testing the dispute algorithms...");
{
let cust_wallet = &init_cust_data.csk;
let cust_wallet = &cust_data.csk;
// get channel closure message
let rc_c = bidirectional::customer_refund(&pp, &channel, &merch_keypair.pk, &cust_wallet);
println!("Obtained the channel closure message: {}", rc_c.message.msgtype);
let channel_token = &init_cust_data.T;
let rc_m = bidirectional::merchant_refute(&pp, &channel_token, &init_merch_data, &mut channel, &rc_c, &rv_w1.signature);
let channel_token = &cust_data.channel_token;
let rc_m = bidirectional::merchant_refute(&pp, &mut channel, &channel_token, &merch_data, &rc_c, &rv_w1.signature);
println!("Merchant has refuted the refund request!");
let (new_b0_cust, new_b0_merch) = bidirectional::resolve(&pp, &init_cust_data, &init_merch_data,
let (new_b0_cust, new_b0_merch) = bidirectional::resolve(&pp, &cust_data, &merch_data,
Some(rc_c), Some(rc_m), Some(rt_w1));
println!("Resolved! Customer = {}, Merchant = {}", new_b0_cust, new_b0_merch);
}

View File

@ -427,12 +427,12 @@ pub mod unidirectional {
}
pub struct InitCustomerData {
T: ChannelToken,
channel_token: ChannelToken,
csk: CustSecretKey
}
pub struct InitMerchantData {
T: clsigs::PublicKey,
channel_token: clsigs::PublicKey,
csk: MerchSecretKey
}
@ -471,12 +471,12 @@ pub mod unidirectional {
let w_com = commit_scheme::commit(&cm_pk, &msg.hash(), r);
let t_c = ChannelToken { w_com: w_com, pk: keypair.pk };
let csk_c = CustSecretKey { sk: keypair.sk, k1: k1, k2: k2, r: r, balance: b0_customer, ck_vec: ck_vec };
return InitCustomerData { T: t_c, csk: csk_c };
return InitCustomerData { channel_token: t_c, csk: csk_c };
}
pub fn init_merchant(pp: &PublicParams, b0_merchant: i32, keypair: &clsigs::KeyPair) -> InitMerchantData {
let csk_m = MerchSecretKey { sk: keypair.sk, balance: b0_merchant };
return InitMerchantData { T: keypair.pk, csk: csk_m };
return InitMerchantData { channel_token: keypair.pk, csk: csk_m };
}
// pub fn establish_customer(pp: &PublicParams, t_m: &clsigs::PublicKey, csk_c: &CustSecretKey) {
@ -595,14 +595,14 @@ pub mod bidirectional {
#[derive(Clone)]
pub struct InitCustomerData {
pub T: ChannelToken,
pub channel_token: ChannelToken,
pub csk: CustomerWallet,
pub bases: Vec<G2>,
}
#[derive(Clone)]
pub struct InitMerchantData {
pub T: clsigs::PublicKeyD,
pub channel_token: clsigs::PublicKeyD,
pub csk: MerchSecretKey,
pub bases: Vec<G2>
}
@ -759,7 +759,7 @@ pub mod bidirectional {
r: r, balance: b0_customer, merchant_balance: b0_merchant,
proof: None, signature: None, refund_token: None };
return InitCustomerData { T: t_c, csk: csk_c, bases: cm_csp.pub_bases.clone() };
return InitCustomerData { channel_token: t_c, csk: csk_c, bases: cm_csp.pub_bases.clone() };
}
///
@ -770,7 +770,7 @@ pub mod bidirectional {
assert!(b0_merchant >= 0);
let cm_csp = generate_commit_setup(&pp, &keypair.pk);
let csk_m = MerchSecretKey { sk: keypair.sk.clone(), balance: b0_merchant };
return InitMerchantData { T: keypair.pk.clone(), csk: csk_m, bases: cm_csp.pub_bases };
return InitMerchantData { channel_token: keypair.pk.clone(), csk: csk_m, bases: cm_csp.pub_bases };
}
///
@ -781,7 +781,7 @@ pub mod bidirectional {
pub fn establish_customer_phase1(pp: &PublicParams, c_data: &InitCustomerData,
pub_bases: &Vec<G2>) -> clproto::ProofCV {
// obtain customer init data
let t_c = &c_data.T;
let t_c = &c_data.channel_token;
let csk_c = &c_data.csk;
//let pub_bases = &m_data.bases;
@ -834,7 +834,7 @@ pub mod bidirectional {
/// pay_by_customer_phase1_precompute - takes as input the public params, channel token,
/// merchant verification key, old customer wallet. Generates PoK of signature on previous wallet.
///
pub fn pay_by_customer_phase1_precompute(pp: &PublicParams, T: &ChannelToken, pk_m: &clsigs::PublicKeyD, old_w: &mut CustomerWallet) {
pub fn pay_by_customer_phase1_precompute(pp: &PublicParams, t: &ChannelToken, pk_m: &clsigs::PublicKeyD, old_w: &mut CustomerWallet) {
// generate proof of knowledge of valid signature on previous wallet
let old_wallet_sig = &old_w.signature;
@ -857,7 +857,7 @@ pub mod bidirectional {
// proof of committed values not including the old wpk since we are revealing it
// to the merchant
let index = 3;
let old_w_com_pr = T.w_com.c - old_w_bal_com - (cm_csp.pub_bases[index] * old_h_wpk);
let old_w_com_pr = t.w_com.c - old_w_bal_com - (cm_csp.pub_bases[index] * old_h_wpk);
// NOTE: the third argument represents the challenge that is included in the final proof structure
let proof_old_cv = clproto::bs_gen_nizk_proof(&old_x, &cm_csp.pub_bases, old_w_com_pr);
@ -883,7 +883,7 @@ pub mod bidirectional {
/// PoK of the committed values in new wallet and PoK of old wallet. Return new channel token,
/// new wallet (minus blind signature and refund token) and payment proof.
///
pub fn pay_by_customer_phase1(pp: &PublicParams, channel: &ChannelState, T: &ChannelToken, pk_m: &clsigs::PublicKeyD,
pub fn pay_by_customer_phase1(pp: &PublicParams, channel: &ChannelState, t: &ChannelToken, pk_m: &clsigs::PublicKeyD,
old_w: &CustomerWallet, balance_increment: i32) -> (ChannelToken, CustomerWallet, PaymentProof) {
// get balance, keypair, commitment randomness and wallet sig
let mut rng = &mut rand::thread_rng();
@ -942,7 +942,7 @@ pub mod bidirectional {
let proof_rp = ProofVB { range_proof: range_proof, value_commitment: value_cm };
let mut bal_proof;
if T.third_party_pay {
if t.third_party_pay {
let r_inc = Fr::random(rng);
let bal_inc_fr = -convert_int_to_fr(balance_increment + channel.tx_fee);
let inc_vec: Vec<Fr> = vec![r_inc, bal_inc_fr];
@ -999,7 +999,7 @@ pub mod bidirectional {
wallet_sig: wallet_proof.blind_sig // blinded signature for old wallet
};
// create new wallet structure (w/o signature or refund token)
let t_c = ChannelToken { w_com: w_com, pk: T.pk.clone(), third_party_pay: T.third_party_pay };
let t_c = ChannelToken { w_com: w_com, pk: t.pk.clone(), third_party_pay: t.third_party_pay };
let csk_c = CustomerWallet { sk: old_w.sk.clone(), cid: cid, wpk: wpk, wsk: wsk, h_wpk: h_wpk,
r: r_pr, balance: updated_balance, merchant_balance: merchant_balance,
proof: None, signature: None, refund_token: None };
@ -1020,7 +1020,7 @@ pub mod bidirectional {
let bal_proof = &proof.bal_proof;
let blinded_sig = &proof.wallet_sig;
// get merchant keypair
let pk_m = &m_data.T;
let pk_m = &m_data.channel_token;
let sk_m = &m_data.csk.sk;
// let's first confirm that proof of knowledge of signature on old wallet is valid
@ -1219,7 +1219,7 @@ pub mod bidirectional {
// update csk in new wallet
c_data.csk = new_w;
// update the channel token
c_data.T = new_t;
c_data.channel_token = new_t;
return true;
}
// must be an old wallet
@ -1290,11 +1290,11 @@ pub mod bidirectional {
/// channels tate, channel closure from customer, and revocation token.
/// Generates a channel closure message for merchant and updated merchant internal state.
///
pub fn merchant_refute(pp: &PublicParams, T_c: &ChannelToken, m_data: &InitMerchantData,
state: &mut ChannelState, rc_c: &ChannelclosureC, rv_token: &secp256k1::Signature) -> ChannelclosureM {
pub fn merchant_refute(pp: &PublicParams, state: &mut ChannelState, t_c: &ChannelToken, m_data: &InitMerchantData,
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 = clsigs::verify_d(&pp.cl_mpk, &T_c.pk, &rc_c.message.hash(), &rc_c.signature);
let is_valid = clsigs::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;
@ -1333,8 +1333,8 @@ pub mod bidirectional {
return (0, total_balance);
}
let pk_c = &c.T.pk; // get public key for customer
let pk_m = &m.T; // get public key for merchant
let pk_c = &c.channel_token.pk; // get public key for customer
let pk_m = &m.channel_token; // get public key for merchant
let rc_cust = rc_c.unwrap();
let rcc_valid = clsigs::verify_d(&pp.cl_mpk, &pk_c, &rc_cust.message.hash(), &rc_cust.signature);
@ -1342,7 +1342,7 @@ pub mod bidirectional {
panic!("resolve2 - rc_c signature is invalid!");
}
let msg = &rc_cust.message;
let w_com = &c.T.w_com;
let w_com = &c.channel_token.w_com;
if msg.msgtype == "refundUnsigned" {
// assert the validity of the w_com
@ -1485,9 +1485,9 @@ mod tests {
cust_keys: &clsigs::KeyPairD, cust_data: &mut bidirectional::InitCustomerData,
payment_increment: i32) {
// let's test the pay protocol
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust_data.T, &merch_keys.pk, &mut cust_data.csk);
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust_data.channel_token, &merch_keys.pk, &mut cust_data.csk);
let (t_c, new_wallet, pay_proof) = bidirectional::pay_by_customer_phase1(&pp, &channel, &cust_data.T, // channel token
let (t_c, new_wallet, pay_proof) = bidirectional::pay_by_customer_phase1(&pp, &channel, &cust_data.channel_token, // channel token
&merch_keys.pk, // merchant pub key
&cust_data.csk, // wallet
payment_increment); // balance increment (FUNC INPUT)
@ -1595,18 +1595,18 @@ mod tests {
cust2_keys: &clsigs::KeyPairD, cust2_data: &mut bidirectional::InitCustomerData,
payment_increment: i32) {
// let's test the pay protocol
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust1_data.T, &merch_keys.pk, &mut cust1_data.csk);
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust2_data.T, &merch_keys.pk, &mut cust2_data.csk);
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust1_data.channel_token, &merch_keys.pk, &mut cust1_data.csk);
bidirectional::pay_by_customer_phase1_precompute(&pp, &cust2_data.channel_token, &merch_keys.pk, &mut cust2_data.csk);
println!("Channel 1 fee: {}", channel1.get_channel_fee());
let (t_c1, new_wallet1, pay_proof1) = bidirectional::pay_by_customer_phase1(&pp, &channel1,
&cust1_data.T, // channel token
&cust1_data.channel_token, // channel token
&merch_keys.pk, // merchant pub key
&cust1_data.csk, // wallet
payment_increment); // balance increment
println!("Channel 2 fee: {}", channel2.get_channel_fee());
let (t_c2, new_wallet2, pay_proof2) = bidirectional::pay_by_customer_phase1(&pp, &channel2,
&cust2_data.T, // channel token
&cust2_data.channel_token, // channel token
&merch_keys.pk, // merchant pub key
&cust2_data.csk, // wallet
-payment_increment); // balance decrement