new libbolt interface with fixes and improvements to internal state management

This commit is contained in:
J. Ayo Akinyele 2019-07-29 01:18:40 -04:00
parent 2cd297ce00
commit 1c91cf03fe
8 changed files with 1435 additions and 2277 deletions

140
README.md
View File

@ -80,121 +80,111 @@ A bidirectional payment channel enables two parties to exchange arbitrary positi
### 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.
The first part of setting up bi-directional payment channels involve generating initial setup parameters using curve BLS12-381 with channel state.
use bolt::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 <-> B"), 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);
let mut channel_state = bidirectional::ChannelState::<Bls12>::new(String::from("Channel A -> B"), false);
let mut rng = &mut rand::thread_rng();
// generate fresh public parameters
channel_state.setup(&mut rng);
### Initialization
To initialize the channel for both parties, do the following:
To initialize wallet/keys for both parties, call the ``bidirectional::init_merchant()`` and ``bidirectional::init_customer()``:
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
// initialize the merchant wallet and initialize with balance
let (mut channel_token, mut merch_wallet) = bidirectional::init_merchant(rng, &mut channel_state, "Bob");
// initialize the balance for merch_wallet
merch_wallet.init_balance(b0_merch);
// generate the customer wallet using the channel token from the merchant
let mut cust_wallet = bidirectional::init_customer(rng, // rng
&mut channel_state, // channel state
&mut channel_token, // channel token
b0_cust, // init customer balance
b0_merch, // init merchant balance
&cm_csp, // commitment pub params
&c_keypair)); // customer keypair
"Alice")); // channel name/purpose
### 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));
// establish the channel by generating initial wallet commitment proof
let (com, com_proof) = bidirectional::establish_customer_generate_proof(rng, &mut channel_token, &mut cust_wallet);
// obtain close token for closing out channel
let close_token = bidirectional::establish_merchant_issue_close_token(rng, &channel_state, &com, &com_proof, &merch_wallet);
// customer verifies that close-token
assert!(cust_wallet.verify_close_token(&channel_state, &close_token));
// form funding tx and wait for network confirmation
// obtain payment token after confirming funding tx
let pay_token = bidirectional::establish_merchant_issue_pay_token(rng, &channel_state, &com, &merch_wallet);
// customer
assert!(bidirectional::establish_final(&mut channel_state, &mut cust_wallet, &pay_token));
// confirm that the channel state is now established
assert!(channel.channel_established);
assert!(channel_state.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
// phase 1 - payment proof and new cust wallet
let (payment, new_cust_wallet) = bidirectional::generate_payment(rng, &channel_state, &cust_wallet, 10);
// phase 1 - merchant verifies the payment proof and returns a close-token
let new_close_token = bidirectional::verify_payment(rng, &channel_state, &payment, &mut merch_wallet);
// phase 2 - verify the close-token, update cust wallet and generate a revoke token for previous cust wallet state
let revoke_token = bidirectional::generate_revoke_token(&channel_state, &mut cust_wallet, new_cust_wallet, &new_close_token);
// phase 2 - merchant verifies the revoke token and sends back the pay-token in response
let new_pay_token = bidirectional::verify_revoke_token(&revoke_token, &mut merch_wallet);
// final - customer verifies the pay token and updates internal state
assert!(cust_wallet.verify_pay_token(&channel_state, &new_pay_token));
### Channel Closure Algorithms (TODO)
To close a channel, the customer must execute 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);
let cust_close = bidirectional::customer_refund(&channel_state, &cust_wallet);
The merchant can dispute a customer's claim by executing the `bidirectional::merchant_retute()` routine as 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);
let merch_close = bidirectional::merchant_refute(&mut channel_state, &channel_token, &cust_close, &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,
let (new_b0_cust, new_b0_merch) = bidirectional::resolve(&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 Payments
## Third-party Payments (TODO)
The bidirectional payment channels can be used to construct third-party payments in which a party **A** pays a second party **B** through an untrusted intermediary (**I**) to which both **A** and **B** have already established a channel. With BOLT, the intermediary learns nothing about the payment from **A** to **B** and cannot link transactions to individual users.
To enable third-party payment support, initialize each payment channel as follows:
let pp = bidirectional::setup(true);
// create the channel state for each channel and indicate third-party support
let mut channel_a = bidirectional::ChannelState::new(String::from("Channel A <-> I"), true);
let mut channel_b = bidirectional::ChannelState::new(String::from("Channel B <-> I"), true);
let mut channel_a = bidirectional::ChannelState::<Bls12>::new(String::from("Channel A <-> I"), true);
let mut channel_b = bidirectional::ChannelState::<Bls12>::new(String::from("Channel B <-> I"), true);
Moreover, the intermediary can set a channel fee as follows:
@ -207,13 +197,13 @@ The channel establishment still works as described before and the pay protocol i
let payment_amount = 20;
// get payment proof on first channel with party A (and I)
let (t_c1, new_w1, pay_proof1) = bidirectional::pay_by_customer_phase1(&pp, &channel_a,
let (t_c1, new_w1, pay_proof1) = bidirectional::pay_by_customer_phase1(&channel_a,
&c1_data.channel_token, // channel token
&merch_keys.pk, // merchant pub key
&c1_data.csk, // wallet
payment_amount); // bal inc
// get payment proof on second channel with party B (and I)
let (t_c2, new_w2, pay_proof2) = bidirectional::pay_by_customer_phase1(&pp, &channel2,
let (t_c2, new_w2, pay_proof2) = bidirectional::pay_by_customer_phase1(&channel2,
&c2_data.channel_token, // channel token
&m_keys.pk, // merchant pub key
&c2_data.csk, // wallet
@ -227,7 +217,7 @@ The channel establishment still works as described before and the pay protocol i
See the `third_party_payment_basics_work()` unit test in `src/lib.rs` for more details.
# Documentation
# Documentation (TODO)
Build the api documentation by simply running `make doc`. Documentation will be generated in your local `target/doc` directory.

View File

@ -36,144 +36,148 @@ macro_rules! measure_ret_mut {
}
fn main() {
println!("******************************************");
// libbolt tests below
println!("Testing the channel setup...");
//println!("[1a] libbolt - setup bidirectional scheme params");
let (pp, setup_time1) = measure!(bidirectional::setup(false));
//println!("[1b] libbolt - generate the initial channel state");
let mut channel = bidirectional::ChannelState::new(String::from("My New Channel A"), false);
println!("Setup time: {}", setup_time1);
//let msg = "Open Channel ID: ";
//libbolt::debug_elem_in_hex(msg, &channel.cid);
let b0_cust = 50;
let b0_merch = 50;
// generate long-lived keypair for merchant -- used to identify
// it to all customers
//println!("[2] libbolt - generate long-lived key pair for merchant");
let (merch_keypair, _) = measure!(bidirectional::keygen(&pp));
// customer generates an ephemeral keypair for use on a single channel
println!("[3] libbolt - generate ephemeral key pair for customer (use with one channel)");
let (cust_keypair, _) = measure!(bidirectional::keygen(&pp));
// 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 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 cust_data, initc_time) = measure_ret_mut!(bidirectional::init_customer(&pp, &mut 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, &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, &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 cust_data.csk, wallet_sig));
assert!(channel.channel_established);
println!("Channel has been established!");
println!("******************************************");
println!("Testing the pay protocol...");
// let's test the pay protocol
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, &cust_data.channel_token, // channel token
&merch_keypair.pk, // merchant pub key
&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, &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, &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 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 cust_data, t_c, new_wallet, rt_w, new_wallet_sig));
{
// scope localizes the immutable borrow here (for debug purposes only)
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, &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
&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, &merch_data);
// get the revocation token (rv_w) on the old public key (wpk)
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 merch_data, &rv_w1);
assert!(bidirectional::pay_by_customer_final(&pp, &merch_keypair.pk, &mut cust_data, t_c1, new_wallet1, rt_w1, new_wallet_sig1));
{
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);
let updated_cust_bal = b0_cust + 5;
let updated_merch_bal = b0_merch - 5;
assert_eq!(updated_cust_bal, cust_wallet.balance);
assert_eq!(updated_merch_bal, merch_wallet.balance);
}
println!("Pay protocol complete!");
println!("******************************************");
println!("Testing the dispute algorithms...");
{
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 = &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, &cust_data, &merch_data,
Some(rc_c), Some(rc_m));
println!("Resolved! Customer = {}, Merchant = {}", new_b0_cust, new_b0_merch);
}
// TODO: add tests for customer/merchant cheating scenarios
println!("******************************************");
println!("Hello world!");
}
//fn main() {
// println!("******************************************");
// // libbolt tests below
// println!("Testing the channel setup...");
//
// //println!("[1a] libbolt - setup bidirectional scheme params");
// let (pp, setup_time1) = measure!(bidirectional::setup(false));
//
// //println!("[1b] libbolt - generate the initial channel state");
// let mut channel = bidirectional::ChannelState::new(String::from("My New Channel A"), false);
//
// println!("Setup time: {}", setup_time1);
//
// //let msg = "Open Channel ID: ";
// //libbolt::debug_elem_in_hex(msg, &channel.cid);
//
// let b0_cust = 50;
// let b0_merch = 50;
//
// // generate long-lived keypair for merchant -- used to identify
// // it to all customers
// //println!("[2] libbolt - generate long-lived key pair for merchant");
// let (merch_keypair, _) = measure!(bidirectional::keygen(&pp));
//
// // customer generates an ephemeral keypair for use on a single channel
// println!("[3] libbolt - generate ephemeral key pair for customer (use with one channel)");
// let (cust_keypair, _) = measure!(bidirectional::keygen(&pp));
//
// // 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 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 cust_data, initc_time) = measure_ret_mut!(bidirectional::init_customer(&pp, &mut 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, &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, &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 cust_data.csk, wallet_sig));
//
// assert!(channel.channel_established);
//
// println!("Channel has been established!");
// println!("******************************************");
//
// println!("Testing the pay protocol...");
// // let's test the pay protocol
// 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, &cust_data.channel_token, // channel token
// &merch_keypair.pk, // merchant pub key
// &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, &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, &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 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 cust_data, t_c, new_wallet, rt_w, new_wallet_sig));
//
// {
// // scope localizes the immutable borrow here (for debug purposes only)
// 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, &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
// &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, &merch_data);
//
// // get the revocation token (rv_w) on the old public key (wpk)
// 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 merch_data, &rv_w1);
//
// assert!(bidirectional::pay_by_customer_final(&pp, &merch_keypair.pk, &mut cust_data, t_c1, new_wallet1, rt_w1, new_wallet_sig1));
//
// {
// 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);
// let updated_cust_bal = b0_cust + 5;
// let updated_merch_bal = b0_merch - 5;
// assert_eq!(updated_cust_bal, cust_wallet.balance);
// assert_eq!(updated_merch_bal, merch_wallet.balance);
// }
// println!("Pay protocol complete!");
//
// println!("******************************************");
// println!("Testing the dispute algorithms...");
//
// {
// 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 = &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, &cust_data, &merch_data,
// Some(rc_c), Some(rc_m));
// println!("Resolved! Customer = {}, Merchant = {}", new_b0_cust, new_b0_merch);
// }
//
// // TODO: add tests for customer/merchant cheating scenarios
// println!("******************************************");
//}

View File

@ -15,7 +15,7 @@ use pairing::bls12_381::{Bls12};
use ff::PrimeField;
use cl::{BlindKeyPair, KeyPair, Signature, PublicParams, setup};
use ped92::{CSParams, Commitment, CSMultiParams};
use util::{hash_pubkey_to_fr, convert_int_to_fr, hash_to_fr, CommitmentProof};
use util::{hash_pubkey_to_fr, convert_int_to_fr, hash_to_fr, CommitmentProof, RevokedMessage};
use rand::Rng;
use std::collections::HashMap;
use std::fmt::Display;
@ -26,9 +26,9 @@ use nizk::{NIZKPublicParams, Proof};
use wallet::Wallet;
#[derive(Clone, Serialize, Deserialize)]
struct PubKeyMap {
wpk: secp256k1::PublicKey,
revoke_token: Option<secp256k1::Signature>
pub struct PubKeyMap {
pub wpk: secp256k1::PublicKey,
pub revoke_token: Option<secp256k1::Signature>
}
//#[derive(Clone, Serialize, Deserialize)]
@ -43,7 +43,6 @@ pub struct ChannelParams<E: Engine> {
//#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone)]
pub struct ChannelState<E: Engine> {
keys: HashMap<String, PubKeyMap>,
R: i32,
tx_fee: i32,
pub cp: Option<ChannelParams<E>>,
@ -82,7 +81,6 @@ impl<E: Engine> ChannelState<E> {
pub fn new(name: String, third_party_support: bool) -> ChannelState<E> {
ChannelState {
keys: HashMap::new(), // store wpks/revoke_tokens
R: 0,
tx_fee: 0,
cp: None,
@ -143,6 +141,7 @@ struct WalletKeyPair {
/// Customer wallet consists of a keypair (NEW)
///
pub struct CustomerWallet<E: Engine> {
pub name: String,
pub pk_c: secp256k1::PublicKey,
sk_c: secp256k1::SecretKey,
cust_balance: i32, //
@ -159,7 +158,7 @@ pub struct CustomerWallet<E: Engine> {
}
impl<E: Engine> CustomerWallet<E> {
pub fn new<R: Rng>(csprng: &mut R, channel: &mut ChannelState<E>, channel_token: &mut ChannelToken<E>, cust_bal: i32, merch_bal: i32) -> Self {
pub fn new<R: Rng>(csprng: &mut R, channel: &mut ChannelState<E>, channel_token: &mut ChannelToken<E>, cust_bal: i32, merch_bal: i32, name: String) -> Self {
assert!(!channel_token.is_init());
let mut kp = secp256k1::Secp256k1::new();
kp.randomize(csprng);
@ -192,6 +191,7 @@ impl<E: Engine> CustomerWallet<E> {
println!("Customer wallet formed -> now returning the structure to the caller.");
return CustomerWallet {
name: name,
pk_c: pk_c,
sk_c: sk_c,
cust_balance: cust_bal,
@ -209,7 +209,7 @@ impl<E: Engine> CustomerWallet<E> {
}
// generate nizk proof of knowledge of commitment opening
pub fn generate_proof<R: Rng>(&mut self, csprng: &mut R, channel_token: &ChannelToken<E>) -> CommitmentProof<E> {
pub fn generate_proof<R: Rng>(&self, csprng: &mut R, channel_token: &ChannelToken<E>) -> CommitmentProof<E> {
return CommitmentProof::<E>::new(csprng, &channel_token.comParams, &self.w_com.c, &self.wallet.as_fr_vec(), &self.r);
}
@ -218,6 +218,7 @@ impl<E: Engine> CustomerWallet<E> {
let close_wallet = self.wallet.with_close(String::from("close"));
let cp = channel.cp.as_ref().unwrap();
let mpk = cp.pub_params.mpk.clone();
//println!("verify_close_token - Wallet: {}", &self.wallet);
let is_close_valid = cp.pub_params.keypair.verify(&mpk, &close_wallet, &self.r, &close_token);
if is_close_valid {
@ -232,14 +233,16 @@ impl<E: Engine> CustomerWallet<E> {
return is_valid;
}
panic!("Channel establish - Verification failed for close token!");
panic!("Customer - Verification failed for close token!");
}
pub fn verify_pay_token(&mut self, channel: &ChannelState<E>, pay_token: &Signature<E>) -> bool {
// unblind and verify signature
let cp = channel.cp.as_ref().unwrap();
let mpk = cp.pub_params.mpk.clone();
let wallet = self.wallet.as_fr_vec();
// we don't want to include "close" prefix here (even if it is set)
let wallet = self.wallet.without_close();
//println!("verify_pay_token - Wallet: {}", &self.wallet);
let is_pay_valid = cp.pub_params.keypair.verify(&mpk, &wallet, &self.r, &pay_token);
if is_pay_valid {
@ -253,11 +256,18 @@ impl<E: Engine> CustomerWallet<E> {
return is_valid;
}
panic!("verify_pay_token - Channel establish - Verification failed for pay token!") ;
panic!("Customer - Verification failed for pay token!");
}
pub fn has_tokens(&self) -> bool {
let index = self.index - 1;
let is_ct = self.close_tokens.get(&index).is_some();
let is_pt = self.pay_tokens.get(&index).is_some();
return is_ct && is_pt;
}
// for channel pay
pub fn generate_payment<R: Rng>(&mut self, csprng: &mut R, channel: &ChannelState<E>, amount: i32) -> (Proof<E>, Commitment<E>, secp256k1::PublicKey, CustomerWallet<E>) {
pub fn generate_payment<R: Rng>(&self, csprng: &mut R, channel: &ChannelState<E>, amount: i32) -> (Proof<E>, Commitment<E>, secp256k1::PublicKey, CustomerWallet<E>) {
// 1 - chooose new wpk/wsk pair
let mut kp = secp256k1::Secp256k1::new();
kp.randomize(csprng);
@ -269,26 +279,47 @@ impl<E: Engine> CustomerWallet<E> {
let new_merch_bal = self.merch_balance + amount;
let new_r = E::Fr::rand(csprng);
println!("old wallet close => {}", self.wallet.close.unwrap());
//println!("old wallet close => {}", self.wallet.close.unwrap());
let cp = channel.cp.as_ref().unwrap();
let old_wallet = Wallet { pkc: self.wallet.pkc.clone(), wpk: self.wallet.wpk.clone(), bc: self.cust_balance, bm: self.merch_balance, close: None };
let new_wallet = Wallet { pkc: self.wallet.pkc.clone(), wpk: wpk_h, bc: new_cust_bal, bm: new_merch_bal, close: Some(self.wallet.close.unwrap()) };
let new_wcom = cp.pub_params.comParams.commit(&new_wallet.as_fr_vec(), &new_r);
// turn this into a isolated test to make sure we are handling transition between close/pay tokens
// println!("<==============================>");
// println!("new wcom: {}", new_wcom);
//
// let new_wcom_pay= cp.pub_params.comParams.commit(&new_wallet.without_close(), &new_r);
// println!("new pay com: {}", new_wcom_pay);
// println!("<==============================>");
//
// let x = hash_to_fr::<E>(String::from("close").into_bytes() );
// let ext_new_wcom = cp.pub_params.comParams.extend_commit(&new_wcom_pay, &x);
// assert!( ext_new_wcom.c == new_wcom.c );
//
// // remove
// let rm_close_new_wcom = cp.pub_params.comParams.remove_commit(&ext_new_wcom, &x);
// println!("removed close from ext new wcom: {}", rm_close_new_wcom);
// assert!( rm_close_new_wcom.c == new_wcom_pay.c );
//
// panic!("they are all equal!");
// 3 - generate new blinded and randomized pay token
let i = self.index - 1;
let mut prev_pay_token = self.pay_tokens.get(&i).unwrap();
println!("OLD {}", &self.wallet);
println!("NEW {}", &new_wallet);
println!("{}", &prev_pay_token);
// println!("OLD {}", &self.wallet);
// println!("NEW {}", &new_wallet);
// println!("{}", &prev_pay_token);
let pay_proof = cp.pub_params.prove(csprng, self.r.clone(), old_wallet, new_wallet.clone(),
new_wcom.clone(), new_r, &prev_pay_token);
// update internal state after proof has been verified by remote
let new_cw = CustomerWallet {
name: self.name.clone(),
pk_c: self.pk_c.clone(),
sk_c: self.sk_c.clone(),
cust_balance: new_cust_bal,
@ -310,6 +341,7 @@ impl<E: Engine> CustomerWallet<E> {
// update the internal state of the customer wallet
pub fn update(&mut self, new_wallet: CustomerWallet<E>) -> bool {
// update everything except for the wpk/wsk pair
assert!(self.name == new_wallet.name);
self.cust_balance = new_wallet.cust_balance;
self.merch_balance = new_wallet.merch_balance;
self.r = new_wallet.r;
@ -330,7 +362,7 @@ impl<E: Engine> CustomerWallet<E> {
let old_wallet = self.old_kp.unwrap();
// proceed with generating the close token
let secp = secp256k1::Secp256k1::new();
let rm = RevokedMessage::new(String::from("revoked"), old_wallet.wpk, None);
let mut rm = RevokedMessage::new(String::from("revoked"), old_wallet.wpk, None);
let revoke_msg = secp256k1::Message::from_slice(&rm.hash_to_slice()).unwrap();
// msg = "revoked"|| old wsk (for old wallet)
let revoke_token = secp.sign(&revoke_msg, &old_wallet.wsk);
@ -363,7 +395,9 @@ pub struct MerchantWallet<E: Engine> {
balance: i32,
pk: secp256k1::PublicKey, // pk_m
sk: secp256k1::SecretKey, // sk_m
comParams: CSMultiParams<E>
comParams: CSMultiParams<E>,
pub keys: HashMap<String, PubKeyMap>,
pub pay_tokens: HashMap<String, cl::Signature<E>>
}
impl<E: Engine> MerchantWallet<E> {
@ -379,7 +413,9 @@ impl<E: Engine> MerchantWallet<E> {
balance: 0,
pk: wpk,
sk: wsk,
comParams: cp.pub_params.comParams.clone()
comParams: cp.pub_params.comParams.clone(),
keys: HashMap::new(), // store wpks/revoke_tokens
pay_tokens: HashMap::new()
}
}
@ -393,19 +429,31 @@ impl<E: Engine> MerchantWallet<E> {
}
}
pub fn issue_close_token<R: Rng>(&self, csprng: &mut R, cp: &ChannelParams<E>, com: &Commitment<E>, extend: bool) -> Signature<E> {
pub fn init_balance(&mut self, balance: i32) {
// set by the escrow/funding transactionf for the channel
self.balance = balance;
}
pub fn issue_close_token<R: Rng>(&self, csprng: &mut R, cp: &ChannelParams<E>, com: &Commitment<E>, extend_close: bool) -> Signature<E> {
println!("issue_close_token => generating token");
let x = hash_to_fr::<E>(String::from("close").into_bytes() );
let close_com = match extend {
let close_com = match extend_close {
true => self.comParams.extend_commit(com, &x),
false => com.clone()
};
//println!("com for close-token: {}", &close_com);
return self.keypair.sign_blind(csprng, &cp.pub_params.mpk, close_com);
}
pub fn issue_pay_token<R: Rng>(&self, csprng: &mut R, cp: &ChannelParams<E>, com: &Commitment<E>) -> Signature<E> {
pub fn issue_pay_token<R: Rng>(&self, csprng: &mut R, cp: &ChannelParams<E>, com: &Commitment<E>, remove_close: bool) -> Signature<E> {
println!("issue_pay_token => generating token");
return self.keypair.sign_blind(csprng, &cp.pub_params.mpk, com.clone());
let x = hash_to_fr::<E>(String::from("close").into_bytes() );
let pay_com = match remove_close {
true => self.comParams.remove_commit(com, &x),
false => com.clone()
};
//println!("com for pay-token: {}", &pay_com);
return self.keypair.sign_blind(csprng, &cp.pub_params.mpk, pay_com);
}
pub fn verify_proof<R: Rng>(&self, csprng: &mut R, channel: &ChannelState<E>, com: &Commitment<E>, com_proof: &CommitmentProof<E>) -> (Signature<E>, Signature<E>) {
@ -414,35 +462,50 @@ impl<E: Engine> MerchantWallet<E> {
if is_valid {
println!("Commitment PoK is valid!");
let close_token = self.issue_close_token(csprng, cp, com, true);
let pay_token = self.issue_pay_token(csprng, cp, com);
let pay_token = self.issue_pay_token(csprng, cp, com, false);
return (close_token, pay_token);
}
panic!("verify_proof - Failed to verify PoK of commitment opening");
}
pub fn verify_payment<R: Rng>(&self, csprng: &mut R, channel: &ChannelState<E>, proof: &Proof<E>, com: &Commitment<E>, wpk: &secp256k1::PublicKey, amount: i32) -> (Signature<E>, Signature<E>) {
fn store_wpk_with_token(&mut self, wpk: &secp256k1::PublicKey, pay_token: Signature<E>) {
// compute fingerprint on wpk
let wpk_str = util::compute_pub_key_fingerprint(&wpk);
self.pay_tokens.insert(wpk_str, pay_token);
}
fn get_pay_token(&self, wpk: &secp256k1::PublicKey) -> Signature<E> {
let wpk_str = util::compute_pub_key_fingerprint(&wpk);
return self.pay_tokens.get(&wpk_str).unwrap().clone();
}
pub fn verify_payment<R: Rng>(&mut self, csprng: &mut R, channel: &ChannelState<E>, proof: &Proof<E>, com: &Commitment<E>, wpk: &secp256k1::PublicKey, amount: i32) -> Signature<E> {
let cp = channel.cp.as_ref().unwrap();
let pay_proof = proof.clone();
let prev_wpk = hash_pubkey_to_fr::<E>(&wpk);
let epsilon = E::Fr::from_str(&amount.to_string()).unwrap();
if cp.pub_params.verify(pay_proof, epsilon, com, prev_wpk) {
// 1 - proceed with generating close token
// 1 - proceed with generating close and pay token
let close_token = self.issue_close_token(csprng, cp, com, false);
let pay_token = self.issue_pay_token(csprng, cp, com);
return (close_token, pay_token);
let pay_token = self.issue_pay_token(csprng, cp, com, true);
// let's store the pay token with the wpk for now
self.store_wpk_with_token(wpk, pay_token);
return close_token;
}
panic!("verify_payment - Failed to validate NIZK PoK for payment.");
}
pub fn verify_revoke_token(&self, revoke_token: &secp256k1::Signature, revoke_msg: &RevokedMessage, wpk: &secp256k1::PublicKey) -> bool {
pub fn verify_revoke_token(&self, revoke_token: &secp256k1::Signature, revoke_msg: &RevokedMessage, wpk: &secp256k1::PublicKey) -> Signature<E> {
let secp = secp256k1::Secp256k1::new();
let msg = secp256k1::Message::from_slice(&revoke_msg.hash_to_slice()).unwrap();
// verify that the revocation token is valid
return secp.verify(&msg, revoke_token, wpk).is_ok();
if secp.verify(&msg, revoke_token, wpk).is_ok() {
return self.get_pay_token(wpk);
}
panic!("verify_revoke_token - Failed to verify the revoke token for wpk!");
}
}
#[cfg(test)]
@ -456,7 +519,6 @@ mod tests {
#[test]
fn channel_util_works() {
println!("Initializing channels...");
let mut channel = ChannelState::<Bls12>::new(String::from("Channel A <-> B"), false);
let mut rng = &mut rand::thread_rng();
@ -475,7 +537,7 @@ mod tests {
// retrieve commitment setup params (using merchant long lived pk params)
// initialize on the customer side with balance: b0_cust
let mut cust_wallet = CustomerWallet::<Bls12>::new(rng, &mut channel, &mut channel_token, b0_cust, b0_merch);
let mut cust_wallet = CustomerWallet::<Bls12>::new(rng, &mut channel, &mut channel_token, b0_cust, b0_merch, String::from("Alice"));
// lets establish the channel
let cust_com_proof = cust_wallet.generate_proof(rng, &mut channel_token);
@ -483,40 +545,43 @@ mod tests {
// first return the close token, then wait for escrow-tx confirmation
// then send the pay-token after confirmation
let (close_token, pay_token) = merch_wallet.verify_proof(rng, &channel, &cust_wallet.w_com, &cust_com_proof);
// unblind tokens and verify signatures
assert!(cust_wallet.verify_pay_token(&channel, &pay_token));
assert!(cust_wallet.verify_close_token(&channel, &close_token));
assert!(cust_wallet.verify_pay_token(&channel, &pay_token));
println!("Done!");
// pay protocol tests
let amount = 10;
let (pay_proof, new_com, old_wpk, new_cw) = cust_wallet.generate_payment(rng, &channel, amount);
println!("{}", new_com);
println!("wpk => {}", old_wpk);
println!("{}", new_cw);
// println!("{}", new_com);
// println!("wpk => {}", old_wpk);
// println!("{}", new_cw);
// new pay_token is not sent until revoke_token is obtained from the customer
let (new_close_token, new_pay_token) = merch_wallet.verify_payment(rng, &channel, &pay_proof, &new_com, &old_wpk, amount);
let new_close_token = merch_wallet.verify_payment(rng, &channel, &pay_proof, &new_com, &old_wpk, amount);
println!("Close Token : {}", new_close_token);
//println!("1 - Updated close Token : {}", new_close_token);
// unblind tokens and verify signatures
// assuming the pay_proof checks out, can go ahead and update internal state of cust_wallet
assert!(cust_wallet.update(new_cw));
//println!("2 - updated customer wallet!");
assert!(cust_wallet.verify_close_token(&channel, &new_close_token));
//println!("3 - verified the close token!");
// invalidate the previous state only if close token checks out
let (revoke_msg, revoke_token) = cust_wallet.generate_revoke_token(&channel, &new_close_token);
let (revoke_msg, revoke_sig) = cust_wallet.generate_revoke_token(&channel, &new_close_token);
//println!("4 - Generated revoke token successfully.");
//println!("5 - Revoke token => {}", revoke_token);
let new_pay_token = merch_wallet.verify_revoke_token(&revoke_sig, &revoke_msg, &old_wpk);
assert!(cust_wallet.verify_pay_token(&channel, &new_pay_token));
println!("Revoke token => {}", revoke_token);
assert!(merch_wallet.verify_revoke_token(&revoke_token, &revoke_msg, &old_wpk));
println!("Validated revoke token!");
//println!("Validated revoke token!");
}
}

View File

@ -90,6 +90,12 @@ pub struct Signature<E: Engine> {
pub H: E::G1,
}
impl<E: Engine> PartialEq for Signature<E> {
fn eq(&self, other: &Signature<E>) -> bool {
self.h == other.h && self.H == other.H
}
}
#[derive(Clone)]
pub struct KeyPair<E: Engine> {
pub secret: SecretKey<E>,

3043
src/lib.rs

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
// ped92.rs
use rand::{thread_rng, Rng};
use pairing::{Engine, CurveProjective};
use ff::Rand;
use ff::{Rand, Field};
use std::fmt;
#[derive(Clone)]
@ -128,6 +128,19 @@ impl<E: Engine> CSMultiParams<E> {
return Commitment { c };
}
pub fn remove_commit(&self, com: &Commitment<E>, x: &E::Fr) -> Commitment<E> {
// c = com * gn+1 ^ x
let len = self.pub_bases.len();
let mut c = self.pub_bases[len-1].clone();
let xx = x.clone();
c.mul_assign(xx);
c.negate();
c.add_assign(&com.c);
return Commitment { c };
}
pub fn decommit(&self, cm: &Commitment<E>, x: &Vec<E::Fr>, r: &E::Fr) -> bool {
let l = x.len();
// pub_base[0] => h, x[0] => r
@ -208,4 +221,6 @@ mod tests {
let c2 = csp.commit(&m2, &r);
assert_eq!(csp.decommit(&c2, &m2, &r), true);
}
// add tests for extend/remove commits dynamically
}

View File

@ -5,6 +5,7 @@ use ff::PrimeField;
use rand::Rng;
use ped92::CSMultiParams;
use secp256k1::{Signature, PublicKey};
use cl::Signature as clSignature;
pub fn hash_g1_to_fr<E: Engine>(x: &Vec<E::G1>) -> E::Fr {
let mut x_vec: Vec<u8> = Vec::new();
@ -63,6 +64,14 @@ pub fn convert_int_to_fr<E: Engine>(value: i32) -> E::Fr {
}
}
pub fn compute_pub_key_fingerprint(wpk: &secp256k1::PublicKey) -> String {
let x_slice = wpk.serialize();
let sha2_digest = sha512::hash(&x_slice);
let h = format!("{:x}", HexSlice::new(&sha2_digest[0..16]));
return h;
}
pub struct CommitmentProof<E: Engine> {
pub T: E::G1,
pub z: Vec<E::Fr>
@ -187,54 +196,6 @@ impl RevokedMessage {
}
}
// refund message
#[derive(Clone, Serialize, Deserialize)]
pub struct CloseMessage<E: Engine> {
pub msgtype: String, // purpose type of message
pub wpk: secp256k1::PublicKey,
pub balance: usize, // the balance
pub r: Option<E::Fr>, // randomness from customer wallet
pub rt: Option<cl::Signature<E>> // refund token
}
impl<E: Engine> CloseMessage<E> {
pub fn new(_msgtype: String, _wpk: secp256k1::PublicKey,
_balance: usize, _r: Option<E::Fr>, _rt: Option<cl::Signature<E>>) -> CloseMessage<E> {
RefundMessage {
msgtype: _msgtype, wpk: _wpk, balance: _balance, r: _r, rt: _rt
}
}
// pub fn hash(&self) -> Vec<E::Fr> {
// let mut v: Vec<Fr> = Vec::new();
// let mut input_buf = Vec::new();
// input_buf.extend_from_slice(self.msgtype.as_bytes());
// v.push(convert_to_fr::<E>(&input_buf));
//
// v.push(hash_pubkey_to_fr::<E>(&self.wpk));
//
// // encoee the balance as a hex string
// let b = format!("{:x}", self.balance);
// let mut b_buf = Vec::new();
// b_buf.extend_from_slice(b.as_bytes());
// v.push(convert_to_fr(&b_buf));
//
// //let r_vec: Vec<u8> = encode(&self.r, Infinite).unwrap();
// if !self.r.is_none() {
// v.push(self.r.unwrap().clone());
// }
//
// if !self.rt.is_none() {
// let rt = {
// &self.rt.clone()
// };
// let rt_ref = rt.as_ref();
// v.push(rt_ref.unwrap().hash(&self.msgtype));
// }
//
// return v;
// }
}
#[cfg(test)]
mod tests {

View File

@ -24,6 +24,10 @@ impl<E: Engine> Wallet<E> {
}
}
pub fn without_close(&self) -> Vec<E::Fr> {
vec!(self.pkc, self.wpk, E::Fr::from_str(&self.bc.to_string()).unwrap(), E::Fr::from_str(&self.bm.to_string()).unwrap())
}
pub fn with_close(&mut self, msg: String) -> Vec<E::Fr> {
let m = hash_to_fr::<E>(msg.into_bytes() );
self.close = Some(m.clone());